Commit 53dc786d authored by Florent Chehab's avatar Florent Chehab

Fixed frontend genereation.

Removed nasty automatic file generation. #39 #25
parent fa8052f4
......@@ -4,28 +4,19 @@
install_backend:
pip install -r requirements.txt --quiet
generate_backend:
export PYTHONPATH=$$PWD ; python ./backend/generate/generate_backend_files.py
generate_frontend_files:
export PYTHONPATH=$$PWD ; python ./frontend/generate/generate_frontend_files.py
generate_frontend: generate_frontend_files
npm run dev
build_frontend: generate_frontend_files
build_frontend:
npm run build
test_backend: generate_backend
test_backend:
pytest general/ frontend/ backend/
test_backend_server:
pytest -n 4 general/ frontend/ backend/ --cov-report html
check_backend: generate_backend
check_backend:
./manage.py check
run_backend: generate_backend
run_backend:
./manage.py runserver
......
#####
# This python file is used to generate js files for redux
from os import makedirs
from os.path import join, dirname, realpath, exists
from django import template
import re
from general.api import get_api_config
############
# Need to do this first so that Django template engine is working
import django
from django.conf import settings
settings.configure(TEMPLATES=[
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['.'], # if you want the templates from a file
'APP_DIRS': False, # we have no apps
},
])
django.setup()
##########
current_dir = dirname(realpath(__file__))
templates_dir = current_dir + '/templates/'
saving_dir = realpath(current_dir + "/../src/generated/")
if not exists(saving_dir):
makedirs(saving_dir)
templates = [
'action-types',
'actions',
'reducers',
'combinedReducers'
]
api_config = get_api_config()
API_BASE = "/api/"
contexts = []
for api in api_config:
if "requires_testing" in api and api["requires_testing"]:
continue
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"] + '/',
})
# API outside rest framework here
contexts.append({
"name": 'serverModerationStatus',
"api_end_point": API_BASE + 'serverModerationStatus' + '/',
})
def convert(name):
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).upper()
for c in contexts:
c['NAME'] = convert(c['name'])
for filename in templates:
template_path = join(templates_dir, filename + '.tpl')
with open(template_path, "r") as f:
t = f.read()
t = template.Template(t)
c = template.Context({'data': contexts})
output = t.render(c)
output_path = join(saving_dir, filename + '.js')
with open(output_path, 'w') as f:
f.write(output)
// WARNING
// THIS FILE HAS BEEN AUTOMATICALLY GENERATED
// WITH /frontend/generate/generate_frontend_files.js
// MODIFY THE FILE ABOVE IF YOUR NOT SATISFIED
// THIS WARNING DOESN'T APPLY TO .tpl FILES...
{% for obj in data %}
////////////////
// {{obj.NAME}}
////////////////
export const {{obj.NAME}}_FETCH_HAS_ERROR = '{{obj.NAME}}_FETCH_HAS_ERROR';
export const {{obj.NAME}}_IS_LOADING = '{{obj.NAME}}_IS_LOADING';
export const {{obj.NAME}}_FETCH_DATA_SUCCESS = '{{obj.NAME}}_FETCH_DATA_SUCCESS';
export const {{obj.NAME}}_INVALIDATED = '{{obj.NAME}}_INVALIDATED';
export const {{obj.NAME}}_EL_FETCH_HAS_ERROR = '{{obj.NAME}}_EL_FETCH_HAS_ERROR';
export const {{obj.NAME}}_EL_INVALIDATED = '{{obj.NAME}}_EL_INVALIDATED';
export const {{obj.NAME}}_EL_IS_LOADING = '{{obj.NAME}}_EL_IS_LOADING';
export const {{obj.NAME}}_EL_FETCH_DATA_SUCCESS = '{{obj.NAME}}_EL_FETCH_DATA_SUCCESS';
{% if not obj.read_only %}
export const {{obj.NAME}}_EL_IS_SAVING = '{{obj.NAME}}_EL_IS_SAVING';
export const {{obj.NAME}}_EL_SHARE_SAVED_TIME = '{{obj.NAME}}_EL_SHARE_SAVED_TIME';
export const {{obj.NAME}}_EL_SAVING_HAS_ERROR = '{{obj.NAME}}_EL_SAVING_HAS_ERROR';
{% endif %}
{% endfor %}
// WARNING
// THIS FILE HAS BEEN AUTOMATICALLY GENERATED
// WITH /frontend/generate/generate_frontend_files.js
// MODIFY THE FILE ABOVE IF YOUR NOT SATISFIED
// THIS WARNING DOESN'T APPLY TO .tpl FILES...
import SmartActions from '../actions/SmartActions';
import {
{% for obj in data %}
{{obj.NAME}}_FETCH_HAS_ERROR,
{{obj.NAME}}_IS_LOADING,
{{obj.NAME}}_FETCH_DATA_SUCCESS,
{{obj.NAME}}_INVALIDATED,
{{obj.NAME}}_EL_INVALIDATED,
{{obj.NAME}}_EL_FETCH_HAS_ERROR,
{{obj.NAME}}_EL_IS_LOADING,
{{obj.NAME}}_EL_FETCH_DATA_SUCCESS,
{% if not obj.read_only %}
{{obj.NAME}}_EL_IS_SAVING,
{{obj.NAME}}_EL_SAVING_HAS_ERROR,
{{obj.NAME}}_EL_SHARE_SAVED_TIME,
{% endif %}
{% endfor %}
} from "./action-types";
//////////////////////////////////
// generic function definitions
const smartActions = new SmartActions();
/////////////////////////////////
{% for obj in data %}
export function {{obj.name}}FetchHasError(bool, error=null) {
return {
type: {{obj.NAME}}_FETCH_HAS_ERROR,
hasError: bool,
error
};
}
export function {{obj.name}}IsLoading(bool) {
return {
type: {{obj.NAME}}_IS_LOADING,
isLoading: bool
};
}
export function {{obj.name}}Invalidated(bool) {
return {
type: {{obj.NAME}}_INVALIDATED,
invalidated: bool
};
}
export function {{obj.name}}FetchDataSuccess({{obj.name}}, setInvalidateFalse=true) {
let time = null;
if (setInvalidateFalse){
{{obj.name}}Invalidated(false)
time = Date.now();
}
return {
type: {{obj.NAME}}_FETCH_DATA_SUCCESS,
{{obj.name}},
{{obj.name}}FetchedAt: time
};
}
export function {{obj.name}}FetchData(pk="") {
return smartActions._FetchData(
pk,
"{{obj.api_end_point}}",
{{obj.name}}IsLoading,
{{obj.name}}FetchDataSuccess,
{{obj.name}}Invalidated,
{{obj.name}}FetchHasError
)
}
export function {{obj.name}}ElFetchHasError(bool, error) {
return {
type: {{obj.NAME}}_EL_FETCH_HAS_ERROR,
hasError: bool,
error
};
}
export function {{obj.name}}ElInvalidated(bool) {
return {
type: {{obj.NAME}}_EL_INVALIDATED,
invalidated: bool
};
}
export function {{obj.name}}ElIsLoading(bool) {
return {
type: {{obj.NAME}}_EL_IS_LOADING,
isLoading: bool
};
}
export function {{obj.name}}ElFetchDataSuccess({{obj.name}}El, setInvalidateFalse=true) {
let time = null;
if (setInvalidateFalse){
{{obj.name}}ElInvalidated(false);
time = Date.now();
}
return {
type: {{obj.NAME}}_EL_FETCH_DATA_SUCCESS,
{{obj.name}}El,
{{obj.name}}ElFetchedAt: time
};
}
export function {{obj.name}}ElFetchData(pk) {
return smartActions._FetchData(
pk,
"{{obj.api_end_point}}",
{{obj.name}}ElIsLoading,
{{obj.name}}ElFetchDataSuccess,
{{obj.name}}ElInvalidated,
{{obj.name}}ElFetchHasError,
true
)
}
{% if not obj.read_only %}
export function {{obj.name}}ElIsSaving(bool) {
return {
type: {{obj.NAME}}_EL_IS_SAVING,
isSaving: bool
};
}
export function {{obj.name}}ElSavingHasError(b, error=null) {
return {
type: {{obj.NAME}}_EL_SAVING_HAS_ERROR,
hasError: b,
error
};
}
export function {{obj.name}}ElShareSavedTime() {
return {
type: {{obj.NAME}}_EL_SHARE_SAVED_TIME,
time: Date.now()
};
}
export function {{obj.name}}ElSaveData(data) {
return smartActions._ElSaveData(data, "{{obj.api_end_point}}", {{obj.name}}ElIsSaving, {{obj.name}}ElFetchDataSuccess, {{obj.name}}ElShareSavedTime, {{obj.name}}ElInvalidated, {{obj.name}}ElSavingHasError)
}
{% endif %}
{% endfor %}
\ No newline at end of file
// WARNING
// THIS FILE HAS BEEN AUTOMATICALLY GENERATED
// WITH /frontend/generate/generate_frontend_files.js
// MODIFY THE FILE ABOVE IF YOUR NOT SATISFIED
// THIS WARNING DOESN'T APPLY TO .tpl FILES...
import { combineReducers } from 'redux';
import {
{% for obj in data %}
{{obj.name}}Invalidated,
{{obj.name}}FetchHasError,
{{obj.name}}IsLoading,
{{obj.name}}Fetched,
{{obj.name}}ElInvalidated,
{{obj.name}}ElFetchHasError,
{{obj.name}}ElIsLoading,
{{obj.name}}ElFetched,
{% if not obj.read_only %}
{{obj.name}}ElIsSaving,
{{obj.name}}ElShareSavedTime,
{{obj.name}}ElSavingHasError,
{% endif %}
{% endfor %}
} from './reducers';
{% for obj in data %}
export const {{obj.name}}Reducers = combineReducers({
invalidated: {{obj.name}}Invalidated,
fetchHasError: {{obj.name}}FetchHasError,
isLoading: {{obj.name}}IsLoading,
fetched: {{obj.name}}Fetched,
})
export const {{obj.name}}ElReducers = combineReducers({
invalidated: {{obj.name}}ElInvalidated,
fetchHasError: {{obj.name}}ElFetchHasError,
isLoading: {{obj.name}}ElIsLoading,
fetched: {{obj.name}}ElFetched,
{% if not obj.read_only %}
isSaving: {{obj.name}}ElIsSaving,
savedAt: {{obj.name}}ElShareSavedTime,
savingHasError: {{obj.name}}ElSavingHasError,
{% endif %}
})
{% endfor %}
// WARNING
// THIS FILE HAS BEEN AUTOMATICALLY GENERATED
// WITH /frontend/generate/generate_frontend_files.js
// MODIFY THE FILE ABOVE IF YOUR NOT SATISFIED
// THIS WARNING DOESN'T APPLY TO .tpl FILES...
import {
{% for obj in data %}
{{obj.NAME}}_INVALIDATED,
{{obj.NAME}}_FETCH_HAS_ERROR,
{{obj.NAME}}_IS_LOADING,
{{obj.NAME}}_FETCH_DATA_SUCCESS,
{{obj.NAME}}_EL_INVALIDATED,
{{obj.NAME}}_EL_FETCH_HAS_ERROR,
{{obj.NAME}}_EL_IS_LOADING,
{{obj.NAME}}_EL_FETCH_DATA_SUCCESS,
{% if not obj.read_only %}
{{obj.NAME}}_EL_IS_SAVING,
{{obj.NAME}}_EL_SAVING_HAS_ERROR,
{{obj.NAME}}_EL_SHARE_SAVED_TIME,
{{obj.NAME}}_EL_SAVING_DATA_SUCCESS,
{% endif %}
{% endfor %}
} from "./action-types";
{% for obj in data %}
export function {{obj.name}}FetchHasError(state = {status: false, error: null}, action) {
switch (action.type) {
case {{obj.NAME}}_FETCH_HAS_ERROR:
return {
status: action.hasError,
error: action.error
}
default:
return state;
}
}
export function {{obj.name}}IsLoading(state = false, action) {
switch (action.type) {
case {{obj.NAME}}_IS_LOADING:
return action.isLoading;
default:
return state;
}
}
export function {{obj.name}}Invalidated(state = false, action) {
switch (action.type) {
case {{obj.NAME}}_INVALIDATED:
return action.invalidated;
default:
return state;
}
}
export function {{obj.name}}ElIsLoading(state = false, action) {
switch (action.type) {
case {{obj.NAME}}_EL_IS_LOADING:
return action.isLoading;
default:
return state;
}
}
export function {{obj.name}}Fetched(state = { data: Object(), fetchedAt: null }, action) {
switch (action.type) {
case {{obj.NAME}}_FETCH_DATA_SUCCESS:
return {
data: action.{{obj.name}},
fetchedAt: action.{{obj.name}}FetchedAt
}
default:
return state;
}
}
export function {{obj.name}}ElFetchHasError(state = {status: false, error: null}, action) {
switch (action.type) {
case {{obj.NAME}}_EL_FETCH_HAS_ERROR:
return {
status: action.hasError,
error: action.error
}
default:
return state;
}
}
export function {{obj.name}}ElInvalidated(state = false, action) {
switch (action.type) {
case {{obj.NAME}}_EL_INVALIDATED:
return action.invalidated;
default:
return state;
}
}
export function {{obj.name}}ElFetched(state = { data: Object(), fetchedAt: null }, action) {
switch (action.type) {
case {{obj.NAME}}_EL_FETCH_DATA_SUCCESS:
return {
data: action.{{obj.name}}El,
fetchedAt: action.{{obj.name}}ElFetchedAt
}
default:
return state;
}
}
{% if not obj.read_only %}
// Handling POST and PUT
export function {{obj.name}}ElIsSaving(state = false, action) {
switch (action.type) {
case {{obj.NAME}}_EL_IS_SAVING:
return action.isSaving;
default:
return state;
}
}
export function {{obj.name}}ElShareSavedTime(state = null, action) {
switch (action.type) {
case {{obj.NAME}}_EL_SHARE_SAVED_TIME:
return action.time;
default:
return state;
}
}
export function {{obj.name}}ElSavingHasError(state = {status: false, error: null}, action) {
switch (action.type) {
case {{obj.NAME}}_EL_SAVING_HAS_ERROR:
return {
status: action.hasError,
error: action.error
}
default:
return state;
}
}
{% endif %}
{% endfor %}
\ No newline at end of file
generated
\ No newline at end of file
import SmartActions from "./SmartActions";
import getCrudActionTypes from "./getCrudActionTypes";
// TODO : Lacks delete actions
// Read generic functions
function _isReading(status, type) {
return {
type,
status
}
}
function _readFailed(failed, error, type) {
return {
type,
failed,
error
}
}
function _readSucceeded(data, type) {
return {
type,
data,
};
}
/**
* Class that contains all actions related to API handling
*
* @export
* @class CrudActions
*/
export default class CrudActions {
/**
* Creates an instance of CrudActions.
* @param {object} apiInfo
* @memberof CrudActions
*/
constructor(apiInfo) {
this.name = apiInfo.name;
this.apiEndPoint = apiInfo.apiEndPoint;
this.readOnly = apiInfo.readOnly === true;
this.smartActions = new SmartActions();
this.types = getCrudActionTypes(this.name);
}
// read all
/**
* Tells redux to read the root of the api endpoint
*
* @returns {function}
* @memberof CrudActions
*/
readAll() {
const self = this;
function isReadingAll(isReadingAll) {
return _isReading(isReadingAll, self.types.isReadingAll);
}
function readAllSucceeded(data) {
return _readSucceeded(data, self.types.readAllSucceeded);
}
function readAllFailed(bool, error = null) {
return _readFailed(bool, error, self.types.readAllFailed);
}
return this.smartActions._FetchData(
"",
this.apiEndPoint,
isReadingAll,
readAllSucceeded,
this.setInvalidatedAll.bind(this),
readAllFailed
)
}
// Read one
/**
* Tells redux to read a specific element at the api endpoint
*
* @returns {function}
* @memberof CrudActions
*/
readSpecific(id = "") {
const self = this;
function isReadingSpecific(isReadingSpecific) {
return _isReading(isReadingSpecific, self.types.isReadingSpecific);
}
function readSpecificSucceeded(data) {
return _readSucceeded(data, self.types.readSpecificSucceeded);
}
function readSpecificFailed(bool, error = null) {
return _readFailed(bool, error, self.types.readSpecificFailed);
}
return this.smartActions._FetchData(
id,
this.apiEndPoint,
isReadingSpecific,
readSpecificSucceeded,
this.setInvalidatedSpecific.bind(this),
readSpecificFailed
)
}
/////
/////
// Not readonly functions
/////
/**
* Simple function to check if we are not trying to do non-readonly operations
* on a readonly element
*
* @memberof CrudActions
*/
_checkNotReadonly() {
if (this.readOnly === true) throw Error("Element is readonly, cannot perform action !");
}
/**
* Function to create a new entry
* If `data` contains an `id` then the behavior should be similar to an update.
*
* @param {object} data
* @returns {function}
* @memberof CrudActions
*/
create(data) {
const self = this;
function isCreating(status) {
return {
type: self.types.isCreating,
status
};
}
function createSucceeded(data) {
return {
type: self.types.createSucceeded,
data,
};
}
function createFailed(failed, error = null) {
return {
type: self.types.createFailed,
failed,
error
};
}
self._checkNotReadonly();
return this.smartActions._ElSaveData(
data,
this.apiEndPoint,