Commit 4fe1bdf2 authored by Florent Chehab's avatar Florent Chehab

Cleaning

parent 0e42118e
...@@ -15,4 +15,7 @@ indent_size = 4 ...@@ -15,4 +15,7 @@ indent_size = 4
indent_size = 2 indent_size = 2
[*.json] [*.json]
indent_size = 2 indent_size = 2
\ No newline at end of file
[Makefile]
indent_style = tab
...@@ -9,6 +9,9 @@ docker-pull: ...@@ -9,6 +9,9 @@ docker-pull:
up--build: up--build:
docker-compose up --build docker-compose up --build
reformat_backend:
docker-compose exec backend sh -c "cd backend && black ."
test_backend: test_backend:
docker-compose exec backend sh -c "cd backend && pytest base_app/ frontend_app/ backend_app/" docker-compose exec backend sh -c "cd backend && pytest base_app/ frontend_app/ backend_app/"
......
...@@ -7,31 +7,44 @@ import importlib ...@@ -7,31 +7,44 @@ import importlib
api_config = get_api_config() api_config = get_api_config()
CLASSIC_MODELS = [] # models that are versionned in the app
VERSIONNED_MODELS = [] VERSIONNED_MODELS = []
# Other models, ie not versionned
CLASSIC_MODELS = []
for model in api_config: # Go through the API configuraion
if "model" in model and model["model"]: for entry in api_config:
model = DotMap(model) if "model" in entry and entry["model"]:
if (not model.requires_testing) and (not model.ignore_in_admin): model_obj = DotMap(entry)
if (not model_obj.requires_testing) and (not model_obj.ignore_in_admin):
# Import the model
module = importlib.import_module( module = importlib.import_module(
"backend_app.models.{}".format(model.import_location) "backend_app.models.{}".format(model_obj.import_location)
) )
if model.versionned: # Add it to the correct list of models
VERSIONNED_MODELS.append(getattr(module, model.model)) if model_obj.versionned:
VERSIONNED_MODELS.append(getattr(module, model_obj.model))
else: else:
CLASSIC_MODELS.append(getattr(module, model.model)) CLASSIC_MODELS.append(getattr(module, model_obj.model))
#######
# Some dynamic checks
#######
for model in CLASSIC_MODELS: for Model in CLASSIC_MODELS:
admin.site.register(model) # Register the model in the admin in a standard way
admin.site.register(Model)
try: try:
model.get_serializer() # Check that it doesn't have the get_serializer method
raise Exception("CLASSIC MODEL SHOULDN'T have this method") Model.get_serializer()
raise Exception("A 'CLASSIC MODEL' SHOULDN'T have the get_serializer method")
except AttributeError: except AttributeError:
pass pass
for model in VERSIONNED_MODELS: for Model in VERSIONNED_MODELS:
admin.site.register(model, CompareVersionAdmin) # Register the model in the admin with versioning
if model.get_serializer().Meta.model != model: admin.site.register(Model, CompareVersionAdmin)
raise Exception("Get_serializer configuration incorrect in", str(model)) # Check that it has a get_serializer method
if Model.get_serializer().Meta.model != Model:
raise Exception("Get_serializer configuration incorrect in", str(Model))
...@@ -2,12 +2,13 @@ from backend_app.fields import JSONField ...@@ -2,12 +2,13 @@ from backend_app.fields import JSONField
from rest_framework import serializers from rest_framework import serializers
field_mapping = serializers.ModelSerializer.serializer_field_mapping field_mapping = serializers.ModelSerializer.serializer_field_mapping
# Small hack to register our custom JSONField class as a regular JSONfield
field_mapping[JSONField] = serializers.JSONField field_mapping[JSONField] = serializers.JSONField
class MySerializerWithJSON(serializers.ModelSerializer): class MySerializerWithJSON(serializers.ModelSerializer):
""" """
Simple class to add support for custom JSONField support Simple class to add serializing support for custom JSONField
""" """
serializer_field_mapping = field_mapping serializer_field_mapping = field_mapping
#!/usr/bin/env python3
""" """
Script to insert the country data in the database Script to insert the country data in the database
IT HAS TO BE RUN INSIDE ./manage.py shell IT HAS TO BE RUN INSIDE ./manage.py shell
TODO YURK. Use pandas @florent !!
""" """
import csv import csv
import os import os
import time import time
from geopy.geocoders import Nominatim
import reverse_geocoder as rg import reverse_geocoder as rg
from geopy.geocoders import Nominatim
tmp = os.path.join(os.path.realpath(__file__), "../../assets/destinations.csv") tmp = os.path.join(os.path.realpath(__file__), "../../assets/destinations.csv")
destinations_path = os.path.abspath(tmp) destinations_path = os.path.abspath(tmp)
......
from .loading_scripts import LoadGroups
from .loading_scripts import LoadAdminUser
from .loading_scripts import LoadCurrencies
from .loading_scripts import LoadCountries
from .loading_scripts import LoadUniversities
from .loading_scripts import LoadTags
from .loading_scripts import LoadUniversityEx
import reversion import reversion
from .loading_scripts import (
LoadAdminUser,
LoadCountries,
LoadCurrencies,
LoadGroups,
LoadTags,
LoadUniversities,
LoadUniversityEx,
)
def load_all(): def load_all():
"""Function to load all the initial data in the app
"""
with reversion.create_revision(): with reversion.create_revision():
LoadGroups() LoadGroups()
admin = LoadAdminUser().get() admin = LoadAdminUser().get()
......
...@@ -15,3 +15,5 @@ __all__ = [ ...@@ -15,3 +15,5 @@ __all__ = [
"LoadCurrencies", "LoadCurrencies",
"LoadUniversityEx", "LoadUniversityEx",
] ]
# TODO in all loading files, use a path to the assets folder define somewhere else.
...@@ -22,5 +22,5 @@ class LoadAdminUser(object): ...@@ -22,5 +22,5 @@ class LoadAdminUser(object):
username=os.environ["DJANGO_ADMIN_USERNAME"] username=os.environ["DJANGO_ADMIN_USERNAME"]
)[0] )[0]
def get(self): def get(self) -> User:
return self.admin return self.admin
from backend_app.models.country import Country
import os import os
from django.contrib.auth.models import User
import pandas as pd import pandas as pd
from backend_app.models.country import Country
from .loadGeneric import LoadGeneric from .loadGeneric import LoadGeneric
class LoadCountries(LoadGeneric): class LoadCountries(LoadGeneric):
def __init__(self, admin): """
Class to handle the loading of countries in the app
"""
def __init__(self, admin: User):
self.admin = admin self.admin = admin
def load(self): def load(self):
......
from backend_app.models.currency import Currency
import os
import csv import csv
from .loadGeneric import LoadGeneric import os
from decimal import Decimal from decimal import Decimal
from django.contrib.auth.models import User
from backend_app.models.currency import Currency
from .loadGeneric import LoadGeneric
class LoadCurrencies(LoadGeneric): class LoadCurrencies(LoadGeneric):
def __init__(self, admin): """
Load currencies in the app
"""
def __init__(self, admin: User):
self.admin = admin self.admin = admin
def load(self): def load(self):
......
from django.contrib.auth.models import User
from django.utils import timezone from django.utils import timezone
import reversion import reversion
from backend_app.models.abstract.my_model import MyModel
class LoadGeneric(object): class LoadGeneric(object):
"""Class to handle the loading of initial data in a generic fashion
"""
@classmethod @classmethod
def add_info_and_save(cls, obj, admin): def add_info_and_save(cls, obj: MyModel, admin: User):
with reversion.create_revision(): with reversion.create_revision():
obj.moderated_by = admin obj.moderated_by = admin
obj.updated_by = admin obj.updated_by = admin
......
...@@ -2,6 +2,9 @@ from django.contrib.auth.models import Group ...@@ -2,6 +2,9 @@ from django.contrib.auth.models import Group
class LoadGroups(object): class LoadGroups(object):
"""Class to add the default user groups to the app
"""
def __init__(self): def __init__(self):
Group.objects.get_or_create(name="Moderators") Group.objects.get_or_create(name="Moderators")
Group.objects.get_or_create(name="DRI") Group.objects.get_or_create(name="DRI")
import json
import os
from django.contrib.auth.models import User
from backend_app.models.tag import Tag from backend_app.models.tag import Tag
import os
import json
from .loadGeneric import LoadGeneric from .loadGeneric import LoadGeneric
class LoadTags(LoadGeneric): class LoadTags(LoadGeneric):
def __init__(self, admin): """
Class to load the tags in the app.
"""
def __init__(self, admin: User):
self.admin = admin self.admin = admin
def load(self): def load(self):
......
from backend_app.models.country import Country import os
from django.contrib.auth.models import User
import pandas as pd
from backend_app.models.campus import Campus
from backend_app.models.city import City from backend_app.models.city import City
from backend_app.models.country import Country
from backend_app.models.university import University from backend_app.models.university import University
from backend_app.models.campus import Campus
import os
import pandas as pd
from .loadGeneric import LoadGeneric from .loadGeneric import LoadGeneric
class LoadUniversities(LoadGeneric): class LoadUniversities(LoadGeneric):
def __init__(self, admin): """
Load the universities in the app
"""
def __init__(self, admin: User):
self.admin = admin self.admin = admin
def load(self): def load(self):
......
from .loadGeneric import LoadGeneric from datetime import datetime
from backend_app.models.university import University
from backend_app.models.university import UniversityDri from django.contrib.auth.models import User
from backend_app.models.university import UniversityInfo
from backend_app.models.university import UniversitySemestersDates
from backend_app.models.country import CountryScholarship
from backend_app.models.country import Country
from backend_app.models.university import UniversityTaggedItem
from backend_app.models.country import Country, CountryScholarship
from backend_app.models.currency import Currency from backend_app.models.currency import Currency
from backend_app.models.tag import Tag from backend_app.models.tag import Tag
from backend_app.models.university import (
University,
UniversityDri,
UniversityInfo,
UniversitySemestersDates,
UniversityTaggedItem,
)
from datetime import datetime from .loadGeneric import LoadGeneric
class LoadUniversityEx(LoadGeneric): class LoadUniversityEx(LoadGeneric):
def __init__(self, admin): """
Load some exemple data for the EPFL
"""
def __init__(self, admin: User):
self.admin = admin self.admin = admin
def load(self): def load(self):
......
from django.db import models from django.db import models
from backend_app.fields import JSONField
from backend_app.models.abstract.my_model import ( from backend_app.models.abstract.my_model import (
MyModelVersionned, MyModelVersionned,
MyModelVersionnedSerializer, MyModelVersionnedSerializer,
MyModelVersionnedViewSet, MyModelVersionnedViewSet,
) )
from backend_app.fields import JSONField
from backend_app.validators.tag import validate_content_against_config from backend_app.validators.tag import validate_content_against_config
from backend_app.validators.tag.tags_config import USEFULL_LINKS_CONFIG from backend_app.validators.tag.tags_config import USEFULL_LINKS_CONFIG
...@@ -12,6 +13,15 @@ IMPORTANCE_LEVEL = (("-", "normal"), ("+", "important"), ("++", "IMPORTANT")) ...@@ -12,6 +13,15 @@ IMPORTANCE_LEVEL = (("-", "normal"), ("+", "important"), ("++", "IMPORTANT"))
class BasicModule(MyModelVersionned): class BasicModule(MyModelVersionned):
"""
Abstract module that provides defaults fields:
Title, comment, useful_links and importance_level
Those field will be inherited.
All Basic modules are also "versionned" modules
"""
title = models.CharField(default="", blank=True, max_length=150) title = models.CharField(default="", blank=True, max_length=150)
comment = models.CharField(default="", blank=True, max_length=5000) comment = models.CharField(default="", blank=True, max_length=5000)
useful_links = JSONField(default=list) useful_links = JSONField(default=list)
...@@ -24,7 +34,15 @@ class BasicModule(MyModelVersionned): ...@@ -24,7 +34,15 @@ class BasicModule(MyModelVersionned):
class BasicModuleSerializer(MyModelVersionnedSerializer): class BasicModuleSerializer(MyModelVersionnedSerializer):
"""
Custom serializer that performs checks on the Basic module filed
"""
def my_validate(self, attrs): def my_validate(self, attrs):
"""
Checks that the useful_links have been filled properly
"""
content = {"useful_links": attrs["useful_links"]} content = {"useful_links": attrs["useful_links"]}
config = {"useful_links": USEFULL_LINKS_CONFIG} config = {"useful_links": USEFULL_LINKS_CONFIG}
validate_content_against_config(config, content) validate_content_against_config(config, content)
...@@ -36,4 +54,8 @@ class BasicModuleSerializer(MyModelVersionnedSerializer): ...@@ -36,4 +54,8 @@ class BasicModuleSerializer(MyModelVersionnedSerializer):
class BasicModuleViewSet(MyModelVersionnedViewSet): class BasicModuleViewSet(MyModelVersionnedViewSet):
"""
Viewset for the Basic Module
"""
serializer_class = BasicModuleSerializer serializer_class = BasicModuleSerializer
...@@ -12,6 +12,7 @@ from .forTestingModeration import ( ...@@ -12,6 +12,7 @@ from .forTestingModeration import (
ForTestingModerationViewSet, ForTestingModerationViewSet,
) )
from .myModelVersionned import ( from .myModelVersionned import (
Version,
MyModelVersionned, MyModelVersionned,
MyModelVersionnedSerializer, MyModelVersionnedSerializer,
MyModelVersionnedViewSet, MyModelVersionnedViewSet,
...@@ -33,6 +34,7 @@ __all__ = [ ...@@ -33,6 +34,7 @@ __all__ = [
"ForTestingModeration", "ForTestingModeration",
"ForTestingModerationSerializer", "ForTestingModerationSerializer",
"ForTestingModerationViewSet", "ForTestingModerationViewSet",
"Version",
"MyModelVersionned", "MyModelVersionned",
"MyModelVersionnedSerializer", "MyModelVersionnedSerializer",
"MyModelVersionnedViewSet", "MyModelVersionnedViewSet",
......
from django.db import models
from backend_app.utils import get_model_config, get_viewset_permissions
from .myModel import MyModel from .myModel import MyModel
from .myModelSerializer import MyModelSerializer from .myModelSerializer import MyModelSerializer
from .myModelViewSet import MyModelViewSet from .myModelViewSet import MyModelViewSet
from django.db import models
from backend_app.utils import get_model_config, get_viewset_permissions
class ForTestingModeration(MyModel): class ForTestingModeration(MyModel):
...@@ -16,7 +18,7 @@ class ForTestingModeration(MyModel): ...@@ -16,7 +18,7 @@ class ForTestingModeration(MyModel):
class ForTestingModerationSerializer(MyModelSerializer): class ForTestingModerationSerializer(MyModelSerializer):
""" """
Same as above Simple serializer for testing purpose
""" """
class Meta: class Meta:
...@@ -26,7 +28,7 @@ class ForTestingModerationSerializer(MyModelSerializer): ...@@ -26,7 +28,7 @@ class ForTestingModerationSerializer(MyModelSerializer):
class ForTestingModerationViewSet(MyModelViewSet): class ForTestingModerationViewSet(MyModelViewSet):
""" """
Same as above Simple Viewset for testing purpose
""" """
permission_classes = get_viewset_permissions("ForTestingModerationViewSet") permission_classes = get_viewset_permissions("ForTestingModerationViewSet")
......
from django.db import models
import reversion
from backend_app.utils import get_model_config, get_viewset_permissions
from .myModelVersionned import ( from .myModelVersionned import (
MyModelVersionned, MyModelVersionned,
MyModelVersionnedSerializer, MyModelVersionnedSerializer,
MyModelVersionnedViewSet, MyModelVersionnedViewSet,
) )
from django.db import models
import reversion
from backend_app.utils import get_model_config, get_viewset_permissions
@reversion.register() @reversion.register()
class ForTestingVersioning(MyModelVersionned): class ForTestingVersioning(MyModelVersionned):
""" """
Simple model for testing purposes Simple model for testing purposes (versioning)
""" """
model_config = get_model_config("ForTestingVersioning") model_config = get_model_config("ForTestingVersioning")
...@@ -24,7 +26,7 @@ class ForTestingVersioning(MyModelVersionned): ...@@ -24,7 +26,7 @@ class ForTestingVersioning(MyModelVersionned):
class ForTestingVersioningSerializer(MyModelVersionnedSerializer): class ForTestingVersioningSerializer(MyModelVersionnedSerializer):
""" """
Same as above Simple Serializer for testing purposes (versioning)
""" """
class Meta: class Meta:
...@@ -34,7 +36,7 @@ class ForTestingVersioningSerializer(MyModelVersionnedSerializer): ...@@ -34,7 +36,7 @@ class ForTestingVersioningSerializer(MyModelVersionnedSerializer):
class ForTestingVersioningViewSet(MyModelVersionnedViewSet): class ForTestingVersioningViewSet(MyModelVersionnedViewSet):
""" """
Same as above Simple Viewset for testing purposes (versioning)
""" """
permission_classes = get_viewset_permissions("ForTestingVersioningViewSet") permission_classes = get_viewset_permissions("ForTestingVersioningViewSet")
......
from django.db import models
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from .pendingModeration import PendingModeration
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 django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
from shared import DEFAULT_OBJ_MODERATION_LV, OBJ_MODERATION_PERMISSIONS
from .pendingModeration import PendingModeration
oml = DEFAULT_OBJ_MODERATION_LV
POSSIBLE_OBJ_MODER_LV = [ POSSIBLE_OBJ_MODER_LV = [
OBJ_MODERATION_PERMISSIONS[key] for key in OBJ_MODERATION_PERMISSIONS OBJ_MODERATION_PERMISSIONS[key] for key in OBJ_MODERATION_PERMISSIONS
] ]
...@@ -21,23 +21,31 @@ def validate_obj_model_lv(value): ...@@ -21,23 +21,31 @@ def validate_obj_model_lv(value):
class MyModel(models.Model): class MyModel(models.Model):
""" """
All models in the app deppend of this one. All models in the app deppend of this one.
It contains the required attributes for managing eventual moderation data. It contains the required attributes for managing optionnal data moderation.
All the logic behind moderation is done in myModelSerializer All the logic behind moderation is done in myModelSerializer
""" """
moderated_by = models.ForeignKey( # store the update author
updated_by = models.ForeignKey(
User, null=True, on_delete=models.SET_NULL, related_name="+" User, null=True, on_delete=models.SET_NULL, related_name="+"
) )
moderated_on = models.DateTimeField(null=True) # store the update date (model can be updated without moderation)