Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Rex Dri
Rex Dri
Commits
d569450d
Commit
d569450d
authored
Sep 01, 2018
by
Florent Chehab
Browse files
API generation for frontend and more standardized js processes
parent
34db017b
Changes
9
Hide whitespace changes
Inline
Side-by-side
Makefile
View file @
d569450d
...
...
@@ -7,6 +7,9 @@ install_backend:
generate_backend
:
python ./backend/generate/generate_all.py
generate_frontend
:
python ./frontend/generate/generate_frontend_files.py
test_backend
:
generate_backend
pytest
...
...
frontend/generate/generate_frontend_files.py
View file @
d569450d
#####
# This python file is used to generate js files for redux
import
os
from
os.path
import
join
from
django
import
template
import
re
import
yaml
############
# Need to do this first so that Django template engine is working
...
...
@@ -35,21 +37,19 @@ templates = [
'combinedReducers'
]
API_BASE
=
"http://127.0.0.1:8000/api/"
contexts
=
[
{
'name'
:
'universities'
,
'api_url'
:
API_BASE
+
"university/"
,
},
{
'name'
:
'countries'
,
'api_url'
:
API_BASE
+
"country/"
},
{
'name'
:
'mainCampus'
,
'api_url'
:
API_BASE
+
"main_campus/"
,
'read_only'
:
True
}
]
API_BASE
=
"/api/"
with
open
(
join
(
current_dir
,
'../../backend/generate/api_config.yml'
),
'r'
)
as
f
:
data
=
f
.
read
()
api_config
=
yaml
.
load
(
data
)
contexts
=
[]
for
api
in
api_config
:
name
=
api
[
'viewset'
].
split
(
'ViewSet'
)[
0
]
name
=
name
[
0
].
lower
()
+
name
[
1
:]
contexts
.
append
({
"name"
:
name
,
"api_end_point"
:
API_BASE
+
api
[
"api_end_point"
]
+
'/'
})
def
read_file
(
file
):
...
...
frontend/generate/templates/actions.tpl
View file @
d569450d
...
...
@@ -29,25 +29,25 @@ import {
//////////////////////////////////
// generic function definitions
function _FetchData(pk, api_
url
, _IsLoading, _FetchDataSuccess, _Invalidated, _HasError, pk_required=false) {
function _FetchData(pk, api_
end_point
, _IsLoading, _FetchDataSuccess, _Invalidated, _HasError, pk_required=false) {
if (pk_required
&&
pk == ""){
throw "pk shouldn't be empty when requesting a specific element";
}
return (dispatch) => {
dispatch(_IsLoading(true));
fetch(api_
url
)
let token = Cookies.get('csrftoken');
fetch(api_
end_point,
{
credentials
:
'same-origin'
,
headers
:
{
'X-CSRFToken'
:
token
}}
)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(_IsLoading(false));
return response;
})
.then((response) => response.json())
.then((obj) => {
dispatch(_Invalidated(false));
dispatch(_FetchDataSuccess(obj));
dispatch(_IsLoading(false));
})
.catch(() => dispatch(_HasError(true)));
};
...
...
@@ -55,7 +55,7 @@ function _FetchData(pk, api_url, _IsLoading, _FetchDataSuccess, _Invalidated, _H
function _ElSaveData(data, api_
url
, _IsLoading, _FetchDataSuccess, _Invalidated, _HasError) {
function _ElSaveData(data, api_
end_point
, _IsLoading, _FetchDataSuccess, _Invalidated, _HasError) {
return (dispatch) => {
let method = "POST";
let pk = "";
...
...
@@ -66,7 +66,7 @@ function _ElSaveData(data, api_url, _IsLoading, _FetchDataSuccess, _Invalidated,
dispatch(_ElIsSaving(true));
let token = Cookies.get('csrftoken');
fetch(api_
url
+pk, {
fetch(api_
end_point
+pk, {
method: method,
credentials: 'same-origin',
headers: {
...
...
@@ -81,13 +81,13 @@ function _ElSaveData(data, api_url, _IsLoading, _FetchDataSuccess, _Invalidated,
throw Error(response.statusText);
}
dispatch(_ElIsSaving(false));
return response;
})
.then((response) => response.json())
.then((_El) => {
dispatch(_ElInvalidated(false));
dispatch(_ElSaveDataSuccess(_El));
dispatch(_ElIsSaving(false));
})
.catch(() => dispatch(_ElHasError(true)));
};
...
...
@@ -131,7 +131,7 @@ export function {{obj.name}}FetchDataSuccess({{obj.name}}) {
export function {
{
obj
.
name
}
}FetchData() {
return _FetchData(
"",
"{
{
obj
.
api_
url
}
}",
"{
{
obj
.
api_
end_point
}
}",
{
{
obj
.
name
}
}IsLoading,
{
{
obj
.
name
}
}FetchDataSuccess,
{
{
obj
.
name
}
}Invalidated,
...
...
@@ -176,7 +176,7 @@ export function {{obj.name}}ElFetchDataSuccess({{obj.name}}El) {
export function {
{
obj
.
name
}
}ElFetchData(pk) {
return _FetchData(
pk,
"{
{
obj
.
api_
url
}
}",
"{
{
obj
.
api_
end_point
}
}",
{
{
obj
.
name
}
}ElIsLoading,
{
{
obj
.
name
}
}ElFetchDataSuccess,
{
{
obj
.
name
}
}ElInvalidated,
...
...
@@ -207,7 +207,7 @@ export function {{obj.name}}ElSaveDataSuccess({{obj.name}}El) {
}
export function {
{
obj
.
name
}
}ElSaveData(data) {
return _ElSaveData(data, "{
{
obj
.
api_
url
}
}", {
{
obj
.
name
}
}ElIsLoading, {
{
obj
.
name
}
}ElFetchDataSuccess, {
{
obj
.
name
}
}ElInvalidated, {
{
obj
.
name
}
}ElHasError)
return _ElSaveData(data, "{
{
obj
.
api_
end_point
}
}", {
{
obj
.
name
}
}ElIsLoading, {
{
obj
.
name
}
}ElFetchDataSuccess, {
{
obj
.
name
}
}ElInvalidated, {
{
obj
.
name
}
}ElHasError)
}
{% endif %}
...
...
frontend/generate/templates/reducers.tpl
View file @
d569450d
...
...
@@ -65,11 +65,11 @@ export function {{obj.name}}ElIsLoading(state = false, action) {
}
}
export function {
{
obj
.
name
}
}Fetched(state = {
{
{
obj
.
name
}
}: [], {
{
obj
.
name
}
}F
etchedAt: null }, action) {
export function {
{
obj
.
name
}
}Fetched(state = {
data: Object(), f
etchedAt: null }, action) {
switch (action.type) {
case {
{
obj
.
NAME
}
}_FETCH_DATA_SUCCESS:
return {
{
{
obj
.
name
}
}
: action.{
{
obj
.
name
}
},
data
: action.{
{
obj
.
name
}
},
fetchedAt: action.{
{
obj
.
name
}
}FetchedAt
}
...
...
@@ -100,11 +100,11 @@ export function {{obj.name}}ElInvalidated(state = false, action) {
}
}
export function {
{
obj
.
name
}
}ElFetched(state = {
{
{
obj
.
name
}
}El: [], {
{
obj
.
name
}
}ElF
etchedAt: null }, action) {
export function {
{
obj
.
name
}
}ElFetched(state = {
data: Object(), f
etchedAt: null }, action) {
switch (action.type) {
case {
{
obj
.
NAME
}
}_EL_FETCH_DATA_SUCCESS:
return {
{
{
obj
.
name
}
}El
: action.{
{
obj
.
name
}
}El,
data
: action.{
{
obj
.
name
}
}El,
fetchedAt: action.{
{
obj
.
name
}
}ElFetchedAt
}
...
...
@@ -128,11 +128,11 @@ export function {{obj.name}}ElIsSaving(state = false, action) {
}
}
export function {
{
obj
.
name
}
}ElSaved(state = {
{
{
obj
.
name
}
}El: [], {
{
obj
.
name
}
}ElS
avedAt: null }, action) {
export function {
{
obj
.
name
}
}ElSaved(state = {
data: Object(), s
avedAt: null }, action) {
switch (action.type) {
case {
{
obj
.
NAME
}
}_EL_SAVING_DATA_SUCCESS:
return {
{
{
obj
.
name
}
}El
: action.{
{
obj
.
name
}
}El,
data
: action.{
{
obj
.
name
}
}El,
fetchedAt: action.{
{
obj
.
name
}
}ElSavedAt
}
...
...
frontend/src/components/App.js
View file @
d569450d
...
...
@@ -17,7 +17,7 @@ import SchoolIcon from '@material-ui/icons/School';
import
{
mainListItems
,
secondaryListItems
}
from
'
./template/listItems
'
;
import
{
connect
}
from
"
react-redux
"
;
import
Loading
from
'
./other/Loading
'
;
import
MyComponent
from
'
./MyComponent
'
// import route Components here
...
...
@@ -27,7 +27,7 @@ import {
}
from
'
react-router-dom
'
;
import
{
countr
ies
FetchData
,
countr
y
FetchData
,
}
from
'
../generated/actions
'
;
...
...
@@ -97,10 +97,10 @@ const styles = theme => ({
myPaper
:
{
padding
:
16
},
null
:{}
null
:
{}
});
class
App
extends
React
.
Component
{
class
App
extends
My
Component
{
state
=
{
open
:
true
,
};
...
...
@@ -113,32 +113,8 @@ class App extends React.Component {
this
.
setState
({
open
:
false
});
};
componentDidMount
()
{
if
(
this
.
props
.
countries
.
fetched
.
countries
.
length
==
0
||
this
.
props
.
invalidated
)
{
this
.
props
.
fetchData
();
}
}
componentDidUpdate
()
{
// TODO ajouter expire date
if
(
this
.
props
.
countries
.
invalidated
)
{
this
.
props
.
countries
.
fetchData
();
}
}
componentWillUnmount
()
{
}
render
()
{
if
(
this
.
props
.
countries
.
hasError
)
{
return
<
p
>
Sorry
!
There
was
an
error
loading
the
items
<
/p>
;
}
if
(
this
.
props
.
countries
.
isLoading
||
this
.
props
.
countries
.
invalidated
)
{
return
<
Loading
/>
;
}
myRender
()
{
const
{
classes
}
=
this
.
props
;
return
(
...
...
@@ -204,7 +180,9 @@ const mapStateToProps = (state) => {
const
mapDispatchToProps
=
(
dispatch
)
=>
{
return
{
fetchData
:
()
=>
dispatch
(
countriesFetchData
()),
fetchData
:
{
countries
:
()
=>
dispatch
(
countryFetchData
())
}
};
};
...
...
frontend/src/components/MyComponent.js
View file @
d569450d
import
React
,
{
Component
}
from
'
react
'
;
import
Loading
from
'
./other/Loading
'
;
class
MyComponent
extends
Component
{
checkProps
(
val
)
{
for
(
let
el
in
this
.
props
)
{
let
prop
=
this
.
props
[
el
];
if
(
val
in
prop
&&
prop
[
val
])
{
if
(
prop
&&
val
in
prop
&&
prop
[
val
])
{
return
true
;
}
}
return
false
;
}
checkPropsHasError
()
{
return
this
.
checkProps
(
'
hasError
'
);
}
...
...
@@ -18,6 +20,48 @@ class MyComponent extends Component {
checkPropsIsLoading
()
{
return
this
.
checkProps
(
'
isLoading
'
);
}
checkPropsInvalidated
()
{
return
this
.
checkProps
(
'
invalidated
'
);
}
loadPropsIfNeeded
(){
for
(
let
prop_key
in
this
.
props
)
{
let
prop
=
this
.
props
[
prop_key
];
if
(
prop
&&
'
fetched
'
in
prop
){
if
(
(
!
prop
.
fetched
.
fetchedAt
)
||
prop
.
invalidated
){
if
(
!
prop
.
isLoading
){
this
.
props
.
fetchData
[
prop_key
]();
}
}
}
}
}
componentDidMount
()
{
this
.
loadPropsIfNeeded
();
this
.
myComponentDidMount
();
}
myComponentDidMount
(){};
componentDidUpdate
()
{
// TODO ajouter expire date
this
.
loadPropsIfNeeded
();
this
.
myComponentDidMount
();
}
myComponentDidMount
(){};
render
()
{
if
(
this
.
checkPropsHasError
())
{
return
<
p
>
Sorry
!
There
was
an
error
loading
the
items
<
/p>
;
}
if
(
this
.
checkPropsIsLoading
()
||
this
.
checkPropsInvalidated
())
{
return
<
Loading
/>
;
}
return
this
.
myRender
();
}
}
...
...
frontend/src/components/map/UnivMakers.js
View file @
d569450d
...
...
@@ -17,8 +17,8 @@ class UnivMarkers extends Component {
render
()
{
let
universities
=
this
.
props
.
universities
;
let
mainCampus
=
this
.
props
.
mainCampus
;
let
universities
=
this
.
props
.
universities
.
fetched
.
data
;
let
mainCampus
=
this
.
props
.
mainCampus
.
fetched
.
data
;
let
selected_main_campus
=
[];
for
(
let
main_campus_pk
in
mainCampus
)
{
...
...
@@ -50,17 +50,8 @@ class UnivMarkers extends Component {
const
mapStateToProps
=
(
state
)
=>
{
return
{
universities
:
state
.
universities
.
fetched
.
universities
,
universitiesFetchedAt
:
state
.
universities
.
fetched
.
fetchedAt
,
universitiesHasError
:
state
.
universities
.
hasError
,
universitiesIsLoading
:
state
.
universities
.
isLoading
,
universitiesInvalidated
:
state
.
universities
.
invalidated
,
mainCampus
:
state
.
mainCampus
.
fetched
.
mainCampus
,
mainCampusFetchedAt
:
state
.
mainCampus
.
fetched
.
fetchedAt
,
mainCampusHasError
:
state
.
mainCampus
.
hasError
,
mainCampusIsLoading
:
state
.
mainCampus
.
isLoading
,
mainCampusInvalidated
:
state
.
mainCampus
.
invalidated
,
universities
:
state
.
universities
,
mainCampus
:
state
.
mainCampus
};
};
...
...
frontend/src/components/map/UnivMap.js
View file @
d569450d
...
...
@@ -5,7 +5,7 @@ import Loading from '../other/Loading';
import
{
Map
,
TileLayer
,
Marker
,
Popup
,
LayersControl
,
FeatureGroup
,
Circle
,
LayerGroup
}
from
'
react-leaflet
'
;
// import MarkerClusterGroup from 'react-leaflet-markercluster';
import
{
universit
ies
FetchData
,
universit
y
FetchData
,
mainCampusFetchData
,
}
from
'
../../generated/actions
'
;
import
UnivMarkers
from
'
./UnivMakers
'
;
...
...
@@ -20,30 +20,6 @@ class UnivMap extends MyComponent {
};
}
componentDidMount
()
{
let
univ_data
=
this
.
props
.
universities
.
fetched
.
universities
;
if
(
univ_data
.
length
==
0
||
this
.
props
.
universities
.
invalidated
)
{
this
.
props
.
fetchUniversities
();
}
let
main_campus_data
=
this
.
props
.
mainCampus
.
fetched
.
mainCampus
;
if
(
main_campus_data
.
length
==
0
||
this
.
props
.
mainCampus
.
invalidated
)
{
this
.
props
.
fetchMainCampus
();
}
}
componentDidUpdate
()
{
// TODO ajouter expire date
if
(
this
.
props
.
universities
.
invalidated
)
{
this
.
props
.
fetchUniversities
();
}
if
(
this
.
props
.
mainCampus
.
invalidated
)
{
this
.
props
.
fetchMainCampus
();
}
}
componentWillUnmount
()
{
let
l
=
this
.
state
.
leaflet_instance
;
...
...
@@ -76,18 +52,11 @@ class UnivMap extends MyComponent {
}))
}
render
()
{
if
(
this
.
checkPropsHasError
())
{
return
<
p
>
Sorry
!
There
was
an
error
loading
the
items
<
/p>
;
}
if
(
this
.
checkPropsIsLoading
())
{
return
<
Loading
/>
;
}
myRender
()
{
let
stamen_name
=
"
Stamen Watercolor
"
;
let
osm_fr_name
=
"
OpenStreetMap France
"
;
let
esri_name
=
"
Esri WorldImagery
"
;
return
(
<
Map
center
=
{
this
.
props
.
map
.
center
}
zoom
=
{
this
.
props
.
map
.
zoom
}
style
=
{{
height
:
"
800px
"
}}
whenReady
=
{(
e
)
=>
this
.
saveLeafletInstance
(
e
.
target
)}
onBaselayerchange
=
{(
e
)
=>
this
.
saveSelectedLayer
(
e
)}
>
<
LayersControl
position
=
"
topright
"
>
...
...
@@ -145,8 +114,10 @@ const mapStateToProps = (state) => {
const
mapDispatchToProps
=
(
dispatch
)
=>
{
return
{
fetchUniversities
:
()
=>
dispatch
(
universitiesFetchData
()),
fetchMainCampus
:
()
=>
dispatch
(
mainCampusFetchData
()),
fetchData
:
{
universities
:
()
=>
dispatch
(
universityFetchData
()),
mainCampus
:
()
=>
dispatch
(
mainCampusFetchData
())
},
saveMainMap
:
(
pos
)
=>
dispatch
(
saveMainMapPosition
(
pos
)),
};
};
...
...
frontend/src/reducers/index.js
View file @
d569450d
import
{
combineReducers
}
from
'
redux
'
;
import
{
universit
ies
Reducers
,
universit
y
Reducers
,
mainCampusReducers
,
countr
ies
Reducers
,
countr
ies
ElReducers
,
universit
ies
ElReducers
countr
y
Reducers
,
countr
y
ElReducers
,
universit
y
ElReducers
}
from
'
../generated/combinedReducers
'
;
import
{
...
...
@@ -17,10 +17,10 @@ const appReducers = combineReducers({
})
const
rootReducer
=
combineReducers
({
countries
:
countr
ies
Reducers
,
countr
ies
El
:
countr
ies
ElReducers
,
universities
:
universit
ies
Reducers
,
universit
ies
El
:
universit
ies
ElReducers
,
countries
:
countr
y
Reducers
,
countr
y
El
:
countr
y
ElReducers
,
universities
:
universit
y
Reducers
,
universit
y
El
:
universit
y
ElReducers
,
mainCampus
:
mainCampusReducers
,
app
:
appReducers
})
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment