Commit d569450d authored by Florent Chehab's avatar Florent Chehab
Browse files

API generation for frontend and more standardized js processes

parent 34db017b
......@@ -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
......
#####
# 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):
......
......@@ -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 %}
......
......@@ -65,11 +65,11 @@ export function {{obj.name}}ElIsLoading(state = false, action) {
}
}
export function {{obj.name}}Fetched(state = { {{obj.name}}: [], {{obj.name}}FetchedAt: null }, action) {
export function {{obj.name}}Fetched(state = { data: Object(), fetchedAt: 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}}ElFetchedAt: null }, action) {
export function {{obj.name}}ElFetched(state = { data: Object(), fetchedAt: 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}}ElSavedAt: null }, action) {
export function {{obj.name}}ElSaved(state = { data: Object(), savedAt: 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
}
......
......@@ -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 {
countriesFetchData,
countryFetchData,
} from '../generated/actions';
......@@ -97,10 +97,10 @@ const styles = theme => ({
myPaper: {
padding: 16
},
null:{}
null: {}
});
class App extends React.Component {
class App extends MyComponent {
state = {
open: true,
};
......@@ -113,31 +113,7 @@ 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;
......@@ -204,7 +180,9 @@ const mapStateToProps = (state) => {
const mapDispatchToProps = (dispatch) => {
return {
fetchData: () => dispatch(countriesFetchData()),
fetchData: {
countries: () => dispatch(countryFetchData())
}
};
};
......
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();
}
}
......
......@@ -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
};
};
......
......@@ -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 {
universitiesFetchData,
universityFetchData,
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,14 +52,7 @@ 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";
......@@ -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)),
};
};
......
import { combineReducers } from 'redux';
import {
universitiesReducers,
universityReducers,
mainCampusReducers,
countriesReducers,
countriesElReducers,
universitiesElReducers
countryReducers,
countryElReducers,
universityElReducers
} from '../generated/combinedReducers';
import {
......@@ -17,10 +17,10 @@ const appReducers = combineReducers({
})
const rootReducer = combineReducers({
countries: countriesReducers,
countriesEl: countriesElReducers,
universities: universitiesReducers,
universitiesEl: universitiesElReducers,
countries: countryReducers,
countryEl: countryElReducers,
universities: universityReducers,
universityEl: universityElReducers,
mainCampus: mainCampusReducers,
app: appReducers
})
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment