Commit 818af843 authored by Florent Chehab's avatar Florent Chehab

Merge branch 'clean' into 'master'

Removed nasty file generation and cleaned the frontend a bit.

See merge request !41
parents d85bfe58 9715fac0
Pipeline #34751 passed with stages
in 2 minutes and 41 seconds
......@@ -10,8 +10,5 @@ node_modules
htmlcov
.vscode
.pytest_cache
/backend/urls.py
/backend/admin.py
database.db
database.db-journal
backend/permissions/__list_user_post_permission.py
{
"python.pythonPath": "~/Env/outgoing/bin/python",
"python.pythonPath": "env/bin/python",
"files.exclude": {
"**/.git": true,
"**/.svn": true,
......
......@@ -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
......
{% autoescape off %}
# WARNING
# THIS FILE HAS BEEN AUTOMATICALLY GENERATED
# WITH /backend/generate/generate_backend_files.py
# MODIFY THE FILE ABOVE IF YOUR NOT SATISFIED
# THIS WARNING DOESN'T APPLY TO .tpl FILES...
from django.contrib import admin
from dotmap import DotMap
from reversion_compare.admin import CompareVersionAdmin
{% spaceless %}
{% for model in data %}
from backend.models.{{model.import_location}} import {{model.model}}
{% endfor %}
{% endspaceless %}
from shared import get_api_config
import importlib
api_config = get_api_config()
CLASSIC_MODELS = []
VERSIONNED_MODELS = []
CLASSIC_MODELS = [
{% for model in data %}{% if not model.versionned %}
{{model.model}},
{% endif %}{% endfor %}
]
for model in api_config:
if "model" in model and model['model']:
model = DotMap(model)
if (not model.requires_testing) and (not model.ignore_in_admin):
module = importlib.import_module("backend.models.{}".format (model.import_location))
if model.versionned:
VERSIONNED_MODELS.append(getattr(module, model.model))
else:
CLASSIC_MODELS.append(getattr(module, model.model))
VERSIONNED_MODELS = [
{% for model in data %}{% if model.versionned %}
{{model.model}},
{% endif %}{% endfor %}
]
for model in CLASSIC_MODELS:
admin.site.register(model)
try:
model.get_serializer()
raise Exception("CLASSIX MODEL SHOULDN'T have this method")
raise Exception("CLASSIC MODEL SHOULDN'T have this method")
except AttributeError:
pass
for model in VERSIONNED_MODELS:
admin.site.register(model, CompareVersionAdmin)
if (model.get_serializer().Meta.model != model):
raise Exception(
"Get_serializer configuration incorrect in", str(model))
{% endautoescape %}
#####
# This python file is used to generate some backend files
from django import template
from os.path import join, realpath, dirname
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()
##########
def render_and_save(template_path, context, output_path):
with open(template_path, 'r') as f:
t = f.read()
t = template.Template(t)
c = template.Context({'data': context})
output = t.render(c)
with open(output_path, 'w') as f:
f.write(output)
current_dir = dirname(realpath(__file__))
templates_dir = current_dir + '/templates/'
saving_dir = realpath(current_dir + "/../")
api_config = get_api_config()
# Render urls.py
template_path = join(templates_dir, 'urls.tpl')
output_path = join(saving_dir, 'urls.py')
render_and_save(template_path, api_config, output_path)
# render list_user_post_permission.py
template_path = join(templates_dir, 'list_user_post_permission.tpl')
output_path = join(saving_dir, './permissions/__list_user_post_permission.py')
render_and_save(template_path, api_config, output_path)
# Render admin.py
data = []
for obj in api_config:
if 'model' in obj and obj['model']:
if obj['requires_testing']:
continue # we don't want testing models to be register in admin
if obj['ignore_in_admin']:
continue
data.append(obj)
template_path = join(templates_dir, 'admin.tpl')
output_path = join(saving_dir, 'admin.py')
render_and_save(template_path, data, output_path)
......@@ -2,8 +2,8 @@ from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericRelation
from .pendingModeration import PendingModeration
from backend.permissions import OBJ_MODERATION_PERMISSIONS
from backend.permissions import DEFAULT_OBJ_MODERATION_LV
from shared import OBJ_MODERATION_PERMISSIONS
from shared import DEFAULT_OBJ_MODERATION_LV
from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError
......
from .obj_moderation_permission import OBJ_MODERATION_PERMISSIONS # noqa: F401
from .obj_moderation_permission import DEFAULT_OBJ_MODERATION_LV # noqa: F401
try:
# the two imports are used outside of django
# the try catch is here to prevent the raise of error
from .noDeleteIfNotStaff import NoDeleteIfNotStaff # noqa: F401
from .isOwner import IsOwner # noqa: F401
from .noDelete import NoDelete # noqa: F401
from .isStaffOrReadOnly import IsStaffOrReadOnly # noqa: F401
from .readOnly import ReadOnly # noqa: F401
from .isDriOrReadOnly import IsDriOrReadOnly # noqa: F401
from .isDriOrNoPost import IsDriOrNoPost # noqa: F401
from .noPostIfNotStaff import NoPostIfNotStaff # noqa: F401
from .default_viewset_permissions import DEFAULT_VIEWSET_PERMISSIONS # noqa: F401
from .__is_moderation_required import is_moderation_required # noqa: F401
except Exception:
pass
from .noDeleteIfNotStaff import NoDeleteIfNotStaff # noqa: F401
from .isOwner import IsOwner # noqa: F401
from .noDelete import NoDelete # noqa: F401
from .isStaffOrReadOnly import IsStaffOrReadOnly # noqa: F401
from .readOnly import ReadOnly # noqa: F401
from .isDriOrReadOnly import IsDriOrReadOnly # noqa: F401
from .isDriOrNoPost import IsDriOrNoPost # noqa: F401
from .noPostIfNotStaff import NoPostIfNotStaff # noqa: F401
from .default_viewset_permissions import DEFAULT_VIEWSET_PERMISSIONS # noqa: F401
from .__is_moderation_required import is_moderation_required # noqa: F401
from backend.utils.__get_user_level import get_user_level
from django.conf import settings
from .obj_moderation_permission import OBJ_MODERATION_PERMISSIONS
from shared import OBJ_MODERATION_PERMISSIONS
######################################
......
{% autoescape off %}
# WARNING
# THIS FILE HAS BEEN AUTOMATICALLY GENERATED
# WITH /backend/generate/generate_backend_files.py
# MODIFY THE FILE ABOVE IF YOUR NOT SATISFIED
# THIS WARNING DOESN'T APPLY TO .tpl FILES...
from django.conf import settings
from dotmap import DotMap
from shared import get_api_config
import importlib
api_config = get_api_config()
ALL_VIEWSETS = {}
{% for model in data %}{% if not model.requires_testing %}{% if model.viewset != 'UserDataViewSet' %}
from backend.models.{{model.import_location}} import {{model.viewset}}
ALL_VIEWSETS["{{model.viewset}}"] = {{model.viewset}}
{% endif %}{% endif %}{% endfor %}
for model in api_config:
model = DotMap(model)
if not model.requires_testing:
if model.viewset != 'UserDataViewSet':
module = importlib.import_module("backend.models.{}".format (model.import_location))
ALL_VIEWSETS[model.viewset] = getattr(module, model.viewset)
if settings.TESTING:
{% for model in data %}{% if model.requires_testing %}
from backend.models.{{model.import_location}} import {{model.viewset}}
ALL_VIEWSETS["{{model.viewset}}"] = {{model.viewset}}
{% endif %}{% endfor %}
for model in api_config:
model = DotMap(model)
if model.requires_testing:
if model.viewset != 'UserDataViewSet':
module = importlib.import_module("backend.models.{}".format (model.import_location))
ALL_VIEWSETS[model.viewset] = getattr(module, model.viewset)
class Request(object):
......@@ -25,6 +29,7 @@ class Request(object):
self.user = user
self.method = method
def list_user_post_permission(user):
viewsets_user_can_post = []
request = Request(user, 'POST')
......@@ -40,5 +45,3 @@ def list_user_post_permission(user):
name = name[0].lower() + name[1:]
viewsets_user_can_post.append(name)
return viewsets_user_can_post
{% endautoescape %}
......@@ -2,7 +2,7 @@ from django.test import TestCase
from backend.models.abstract.my_model.myModel import validate_obj_model_lv
import pytest
from django.core.exceptions import ValidationError
from backend.permissions import OBJ_MODERATION_PERMISSIONS
from shared import OBJ_MODERATION_PERMISSIONS
class MyModelTestCase(TestCase):
......
{% autoescape off %}
# WARNING
# THIS FILE HAS BEEN AUTOMATICALLY GENERATED
# WITH /backend/generate/generate_backend_files.py
# MODIFY THE FILE ABOVE IF YOUR NOT SATISFIED
# THIS WARNING DOESN'T APPLY TO .tpl FILES...
from django.conf.urls import url, include
from django.conf import settings
from rest_framework import routers
from django.conf.urls import include, url
from django.urls import path
from rest_framework import routers
from rest_framework.documentation import include_docs_urls
from backend.permissions import DEFAULT_VIEWSET_PERMISSIONS
from shared import get_api_config
from . import views
from dotmap import DotMap
import importlib
urlpatterns = [
url(r'^api-docs/', include_docs_urls(title='Outgoing API')),
]
router = routers.DefaultRouter()
ALL_MODELS = []
ALL_VIEWSETS = []
{% for model in data %}
{% if not model.requires_testing %}
from backend.models.{{model.import_location}} import {{model.viewset}}
ALL_VIEWSETS.append({{model.viewset}})
{% if model.model is not None and not model.ignore_in_admin%}
from backend.models.{{model.import_location}} import {{model.model}}
ALL_MODELS.append({{model.model}})
{% endif %}
{% endif %}
{% endfor %}
from rest_framework.documentation import include_docs_urls
# Automatically loading models based on API config file
api_config = get_api_config()
urlpatterns = [
url(r'^api-docs/', include_docs_urls(title='Outgoing API'))
]
for model in api_config:
model = DotMap(model)
if not model.requires_testing:
module = importlib.import_module("backend.models.{}".format(model.import_location))
Viewset = getattr(module, model.viewset)
ALL_VIEWSETS.append(Viewset)
if model.model is not None and not model.ignore_in_admin:
ALL_MODELS.append(getattr(module, model.model))
# print(viewset)
str_url = model.api_end_point
if 'api_attr' in model:
str_url += '/{}'.format(model.api_attr)
if 'api_name' in model:
router.register(str_url, Viewset, model.api_name)
else:
router.register(str_url, Viewset)
router = routers.DefaultRouter()
if settings.TESTING:
{% for model in data %}{% if model.requires_testing %}
from backend.models.{{model.import_location}} import {{model.viewset}}
ALL_VIEWSETS.append({{model.viewset}})
{% if model.model is not None %}
from backend.models.{{model.import_location}} import {{model.model}}
ALL_MODELS.append({{model.model}})
{% endif %}
router.register(
r'{{model.api_end_point}}{% if 'api_attr' in model %}/{{model.api_attr}}{% endif %}',
{{model.viewset}}{%if 'api_name' in model%},"{{model.api_name}}"{% endif %}
){% endif %}{% endfor %}
{% for model in data %}{% if not model.requires_testing %}
router.register(
r'{{model.api_end_point}}{% if 'api_attr' in model %}/{{model.api_attr}}{% endif %}',
{{model.viewset}}{%if 'api_name' in model%},"{{model.api_name}}"{% endif %}
){% endif %}{% endfor %}
for model in api_config:
model = DotMap(model)
if model.requires_testing:
module = importlib.import_module("backend.models.{}".format(model.import_location))
Viewset = getattr(module, model.viewset)
ALL_VIEWSETS.append(Viewset)
if model.model is not None:
ALL_MODELS.append(getattr(module, model.model))
str_url = model.api_end_point
if 'api_attr' in model:
str_url += '/{}'.format(model.api_attr)
if 'api_name' in model:
router.register(str_url, Viewset, model.api_name)
else:
router.register(str_url, Viewset, model.viewset)
urlpatterns += [url(r'^api/', include(router.urls))]
urlpatterns.append(path('api/serverModerationStatus/', views.app_moderation_status))
for model in ALL_MODELS:
for key in model.model_config:
val = model.model_config[key]
for Model in ALL_MODELS:
for key in Model.model_config:
val = Model.model_config[key]
if val is None:
raise Exception("You forgot to set the {} config variable in the model {}".format(key, str(model)))
from backend.permissions import DEFAULT_VIEWSET_PERMISSIONS
for viewset in ALL_VIEWSETS:
for Viewset in ALL_VIEWSETS:
for p in DEFAULT_VIEWSET_PERMISSIONS:
v_p = viewset.permission_classes
v_p = Viewset.permission_classes
if p not in v_p:
raise Exception("Permission_classes are not defined correctly in the viewset {}".format(str(viewset)))
{% endautoescape %}
from general.api import get_api_config
from shared import get_api_config
def find_api_end_point_for_viewset(viewset_name):
......
from general.api import get_api_config
from shared import get_api_config
def get_model_config(model):
......
from .__is_member import is_member
from backend.permissions import OBJ_MODERATION_PERMISSIONS
from shared import OBJ_MODERATION_PERMISSIONS
def get_user_level(user):
......
from backend.permissions import IsOwner, IsStaffOrReadOnly, IsDriOrReadOnly, ReadOnly, IsDriOrNoPost, NoPostIfNotStaff
from rest_framework.permissions import IsAdminUser
from backend.permissions import DEFAULT_VIEWSET_PERMISSIONS
from general.api import get_api_config
from shared import get_api_config
def get_viewset_permissions(viewset):
......
......@@ -2,7 +2,7 @@ from django.conf import settings
from django.http import HttpResponse
import json
from backend.permissions.obj_moderation_permission import OBJ_MODERATION_PERMISSIONS
from shared import OBJ_MODERATION_PERMISSIONS
def app_moderation_status(request):
......
#####
# 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){