Skip to content
GitLab
Menu
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
e9e8ecbe
Commit
e9e8ecbe
authored
Sep 01, 2018
by
Florent Chehab
Browse files
Merge branch 'better_api_integration' into 'master'
Better api integration See merge request chehabfl/outgoing_rex!24
parents
6ebf4368
86794957
Pipeline
#26849
passed with stages
in 2 minutes and 32 seconds
Changes
10
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Makefile
View file @
e9e8ecbe
...
...
@@ -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
...
...
backend/generate/api_config.yml
View file @
e9e8ecbe
# THIS FILE IS USED TO GENERATE OTHER VERY IMPORTANT FILES
# BOTH IN THE FRONTEND AND IN THE BACKEND !
# TAKE CARE WHEN MODYFING IT ;)
# model : the model name (may be null)
# viewset : the viewset name for the api
# api_end_pont : the main part of the url for making request to the api
# This string will also be used for naming variables in JS !!
# So no weird characters there please...
# versionned: boolean to specify wether this model is versionned or not
# api_attr : to specify some attributes that may be captured
# and used in the viewset
# requires_testing: boolean to tell if this viewset is only availble in
# a testing environment.
-
model
:
Country
viewset
:
CountryViewSet
import_location
:
location
api_end_point
:
countr
y
api_end_point
:
countr
ies
-
model
:
City
viewset
:
CityViewSet
import_location
:
location
api_end_point
:
city
-
model
:
Tag
viewset
:
TagViewSet
import_location
:
tag
api_end_point
:
tag
api_end_point
:
cities
-
model
:
University
TaggedItem
viewset
:
University
TaggedItem
ViewSet
-
model
:
University
viewset
:
UniversityViewSet
import_location
:
university
api_end_point
:
university_more_tmp
versionned
:
true
api_end_point
:
universities
-
model
:
Campus
TaggedItem
viewset
:
Campus
TaggedItem
ViewSet
-
model
:
Campus
viewset
:
CampusViewSet
import_location
:
university
api_end_point
:
campus
_more_tmp
api_end_point
:
campus
es
versionned
:
true
-
model
:
CountryTaggedItem
viewset
:
CountryTaggedItemViewSet
import_location
:
location
api_end_point
:
country_more_tmp
versionned
:
true
-
model
:
CityTaggedItem
viewset
:
CityTaggedItemViewSet
import_location
:
location
api_end_point
:
city_more_tmp
versionned
:
true
-
model
:
UserData
viewset
:
UserDataViewSet
import_location
:
user
api_end_point
:
userData
api_name
:
user-data-detail
-
model
:
Tag
viewset
:
TagViewSet
import_location
:
tag
api_end_point
:
tags
-
model
:
Currency
viewset
:
CurrencyViewSet
import_location
:
location
api_end_point
:
currenc
y
api_end_point
:
currenc
ies
-
model
:
Department
viewset
:
DepartmentViewSet
import_location
:
other_core
api_end_point
:
other/
department
api_end_point
:
department
s
-
model
:
Specialty
viewset
:
SpecialtyViewSet
import_location
:
other_core
api_end_point
:
other/
specialt
y
api_end_point
:
specialt
ies
-
model
:
Semester
viewset
:
SemesterViewSet
import_location
:
other_core
api_end_point
:
other/
semester
api_end_point
:
semester
s
-
model
:
Offer
viewset
:
OfferViewSet
import_location
:
other_core
api_end_point
:
other/offer
api_end_point
:
offers
-
model
:
CountryTaggedItem
viewset
:
CountryTaggedItemViewSet
import_location
:
location
api_end_point
:
countriesTaggedItems
versionned
:
true
-
model
:
CountryScholarship
viewset
:
CountryScholarshipViewSet
import_location
:
location
api_end_point
:
countr
y_more/s
cholarship
api_end_point
:
countr
iesS
cholarship
s
versionned
:
true
-
model
:
CountryDri
viewset
:
CountryDriViewSet
import_location
:
location
api_end_point
:
countr
y_more/d
ri
api_end_point
:
countr
iesD
ri
versionned
:
true
-
model
:
University
viewset
:
UniversityViewSet
import_location
:
university
api_end_point
:
university
-
model
:
Campus
viewset
:
CampusViewSet
import_location
:
university
api_end_point
:
campus
-
model
:
CityTaggedItem
viewset
:
CityTaggedItemViewSet
import_location
:
location
api_end_point
:
citiesTaggedItems
versionned
:
true
-
model
:
null
viewset
:
MainCampusViewSet
-
model
:
UniversityTaggedItem
viewset
:
UniversityTaggedItemViewSet
import_location
:
university
api_end_point
:
main_campu
s
read_only
:
true
api_end_point
:
universitiesTaggedItem
s
versionned
:
true
-
model
:
UniversityScholarship
viewset
:
UniversityScholarshipViewSet
import_location
:
university
api_end_point
:
universit
y_more/s
cholarship
api_end_point
:
universit
iesS
cholarship
s
versionned
:
true
-
model
:
UniversityInfo
viewset
:
UniversityInfoViewSet
import_location
:
university
api_end_point
:
universit
y_more/i
nfo
api_end_point
:
universit
iesI
nfo
versionned
:
true
-
model
:
UniversitySemestersDates
viewset
:
UniversitySemestersDatesViewSet
import_location
:
university
api_end_point
:
universit
y_more/s
emesters
_d
ates
api_end_point
:
universit
iesS
emesters
D
ates
versionned
:
true
-
model
:
UniversityDri
viewset
:
UniversityDriViewSet
import_location
:
university
api_end_point
:
universit
y_more/d
ri
api_end_point
:
universit
iesD
ri
versionned
:
true
-
model
:
null
viewset
:
UniversityModulesViewSet
import_location
:
university
api_end_point
:
universit
y_more/a
ll
api_end_point
:
universit
iesA
ll
api_attr
:
(?P<univ_id>[0-9]+)
api_name
:
university_details
-
model
:
CampusTaggedItem
viewset
:
CampusTaggedItemViewSet
import_location
:
university
api_end_point
:
campusesTaggedItems
versionned
:
true
-
model
:
null
viewset
:
MainCampusViewSet
import_location
:
university
api_end_point
:
mainCampuses
read_only
:
true
-
model
:
Recommendation
viewset
:
RecommendationViewSet
import_location
:
user
api_end_point
:
user
/r
ecommendation
api_end_point
:
user
R
ecommendation
s
-
model
:
RecommendationList
viewset
:
RecommendationListViewSet
import_location
:
user
api_end_point
:
user
/r
ecommendation
s_l
ist
api_end_point
:
user
R
ecommendation
L
ist
s
-
model
:
PreviousDeparture
viewset
:
PreviousDepartureViewSet
import_location
:
user
api_end_point
:
universit
y_more/p
revious
_d
eparture
api_end_point
:
universit
iesP
revious
D
eparture
s
-
model
:
PreviousDepartureFeedback
viewset
:
PreviousDepartureFeedbackViewSet
import_location
:
user
api_end_point
:
university_more/previous_departure_feedback
api_end_point
:
universitiesPreviousDepartureFeedback
-
model
:
UserData
viewset
:
UserDataViewSet
import_location
:
user
api_end_point
:
user/data
api_name
:
user-data-detail
-
model
:
PendingModeration
viewset
:
PendingModerationViewSet
import_location
:
my_model
api_end_point
:
pending
_m
oderation
api_end_point
:
pending
M
oderation
-
model
:
Version
viewset
:
VersionViewSet
import_location
:
my_model
api_end_point
:
version
api_end_point
:
version
s
api_attr
:
(?P<content_type_id>[0-9]+)/(?P<object_pk>[0-9A-Za-z]+)
api_name
:
versions
-l
ist
api_name
:
versions
L
ist
ignore_in_admin
:
true
-
model
:
ForTestingModeration
...
...
frontend/generate/generate_frontend_files.py
View file @
e9e8ecbe
#####
# 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,21 @@ 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
+
"
co
u
nt
ry/"
},
{
'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
:
if
"requires_testing"
in
api
and
api
[
"requires_testing"
]:
cont
inue
name
=
api
[
'viewset'
].
split
(
'ViewSet'
)[
0
]
name
=
name
[
0
].
lower
()
+
name
[
1
:]
contexts
.
append
({
"name"
:
api
[
"api_end_point"
],
"api_end_point"
:
API_BASE
+
api
[
"api_end_point"
]
+
'/'
,
})
def
read_file
(
file
):
...
...
frontend/generate/templates/actions.tpl
View file @
e9e8ecbe
...
...
@@ -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 @
e9e8ecbe
...
...
@@ -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 @
e9e8ecbe
...
...
@@ -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
...
...
@@ -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
(
countriesFetchData
())
}
};
};
...
...
frontend/src/components/MyComponent.js
View file @
e9e8ecbe
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 @
e9e8ecbe
...
...
@@ -7,8 +7,8 @@ import { Map, TileLayer, Marker, Popup, LayersControl, FeatureGroup, Circle, Lay
import
{
universitiesFetchData
,
universitiesInvalidated
,
mainCampusFetchData
,
mainCampusInvalidated
mainCampus
es
FetchData
,
mainCampus
es
Invalidated
}
from
'
../../generated/actions
'
;
...
...
@@ -17,12 +17,12 @@ class UnivMarkers extends Component {
render
()
{
let
universities
=
this
.
props
.
universities
;
let
mainCampus
=
this
.
props
.
mainCampus
;
let
universities
=
this
.
props
.
universities
.
fetched
.
data
;
let
mainCampus
es
=
this
.
props
.
mainCampus
es
.
fetched
.
data
;
let
selected_main_campus
=
[];
for
(
let
main_campus_pk
in
mainCampus
)
{
let
campus
=
mainCampus
[
main_campus_pk
]
for
(
let
main_campus_pk
in
mainCampus
es
)
{
let
campus
=
mainCampus
es
[
main_campus_pk
]
let
univ
=
universities
[
campus
.
university
]
if
(
univ
&&
campus
)
{
selected_main_campus
.
push
({
...
...
@@ -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
<