Verified Commit 69af78c1 authored by Florent Chehab's avatar Florent Chehab
Browse files

style(documentation): prettified folder

parent b36cc236
Pipeline #59741 passed with stages
in 5 minutes and 43 seconds
API # API
========
## Location ## Location
...@@ -7,17 +6,14 @@ The backend `Django` app, with `Django Rest Framework` acts as an API. It is ava ...@@ -7,17 +6,14 @@ The backend `Django` app, with `Django Rest Framework` acts as an API. It is ava
An automated documentation is generated and available at the URI `/api-doc`. An automated documentation is generated and available at the URI `/api-doc`.
## Authentication ## Authentication
Two authentication protocol are currently available and both lead to API response according to the rights of the corresponding user. Two authentication protocol are currently available and both lead to API response according to the rights of the corresponding user.
### Session ### Session
This is the default mode used once you are connected with the `CAS` by going to the URI `/user/login`. This is the default mode used once you are connected with the `CAS` by going to the URI `/user/login`.
### Token ### Token
A token may be associated with a user through the Django admin (`/admin`). To use it you could do: A token may be associated with a user through the Django admin (`/admin`). To use it you could do:
......
Architecture # Architecture
===============================
Visualization of the `backend` app architecture. Visualization of the `backend` app architecture.
......
Words relative to *config files* # Words relative to _config files_
=========
There exist different kinds of *config files* throughout the app and all of them are absolutely necessary, so it's good to understand what is their purpose.
There exist different kinds of _config files_ throughout the app and all of them are absolutely necessary, so it's good to understand what is their purpose.
## Django config files ## Django config files
...@@ -18,7 +15,6 @@ Django's main config file is in `backend/base_app/settings/main.py`. This file i ...@@ -18,7 +15,6 @@ Django's main config file is in `backend/base_app/settings/main.py`. This file i
Another config file is loaded through the previous one: `backend/base_app/settings/app_settings.py`. It contains app specific settings such as the `CAS` configuration and how moderation is applied. Another config file is loaded through the previous one: `backend/base_app/settings/app_settings.py`. It contains app specific settings such as the `CAS` configuration and how moderation is applied.
## Custom config files ## Custom config files
### defaults.yaml ### defaults.yaml
......
Data validation # Data validation
=============
Validation on data is performed at different levels: Validation on data is performed at different levels:
......
External data # External data
=============
`REX-DRI` interacts with several external services to provide consistent and up-to-date data to the application. `REX-DRI` interacts with several external services to provide consistent and up-to-date data to the application.
...@@ -26,14 +25,12 @@ subcommands: ...@@ -26,14 +25,12 @@ subcommands:
currencies Update currencies from fixer currencies Update currencies from fixer
``` ```
## Currencies ## Currencies
Currencies exchange rates are taken from [`fixer`](https://fixer.io/). You must provide a consistent API key in `server/envs/external_data.env` to be able to use the service properly. Currencies exchange rates are taken from [`fixer`](https://fixer.io/). You must provide a consistent API key in `server/envs/external_data.env` to be able to use the service properly.
To trigger un update of currencies, run `./manage.py update_external_data currencies`. To trigger un update of currencies, run `./manage.py update_external_data currencies`.
## UTC ## UTC
TODO TODO
......
Models, Serializers and ViewSets # Models, Serializers and ViewSets
=============
## Basics ## Basics
...@@ -10,14 +9,12 @@ To be able to transfer data to the frontend, we use the famous Django extension ...@@ -10,14 +9,12 @@ To be able to transfer data to the frontend, we use the famous Django extension
?> Make sure to look at its documentation and code if you want to now more about it. ?> Make sure to look at its documentation and code if you want to now more about it.
To put it into a nutshell, we have: To put it into a nutshell, we have:
- **Models** that handle all data and the exchanges with the database, - **Models** that handle all data and the exchanges with the database,
- **Serializers** that handle the conversion from models' data to JSON and from JSON to models' data, - **Serializers** that handle the conversion from models' data to JSON and from JSON to models' data,
- **ViewSets** that handle communication with the frontend. - **ViewSets** that handle communication with the frontend.
## In details ## In details
Since the process of creating the API was a bit repetitive, we have used a lot of [inheritance](Application/Backend/architecture.md). Since the process of creating the API was a bit repetitive, we have used a lot of [inheritance](Application/Backend/architecture.md).
...@@ -26,7 +23,7 @@ Below are the things to know about what has been implemented. ...@@ -26,7 +23,7 @@ Below are the things to know about what has been implemented.
### Models ### Models
There exist different *abstract* models from which the *concrete* models can inherit from. Those *abstract* models have different behaviors and properties built in directly. There exist different _abstract_ models from which the _concrete_ models can inherit from. Those _abstract_ models have different behaviors and properties built in directly.
- `BaseModel`: All models from the app inherit from this simple one. It should be directly used only for data that will be automatically stored and not editable. - `BaseModel`: All models from the app inherit from this simple one. It should be directly used only for data that will be automatically stored and not editable.
- `EssentialModule`: contains attribute that should be inherited from by all models that corresponds to frontend editable modules. It manages the moderation aspect. - `EssentialModule`: contains attribute that should be inherited from by all models that corresponds to frontend editable modules. It manages the moderation aspect.
...@@ -37,10 +34,9 @@ Here is the UML of their inheritance: ...@@ -37,10 +34,9 @@ Here is the UML of their inheritance:
![abstract](../../generated/abstract.svg) ![abstract](../../generated/abstract.svg)
?> :information_desk_person: To register your model in the app, you need to import it in `backend/backend_app/admin.py` and add it to the `ALL_MODELS` list. ?> :information_desk_person: To register your model in the app, you need to import it in `backend/backend_app/admin.py` and add it to the `ALL_MODELS` list.
------ ---
To define a model for the app, you should (in most cases) use one of the three base classes defined above. What remains is to define a model just like you would do for any Django Model. Here is an example (you have tons of them in app already): To define a model for the app, you should (in most cases) use one of the three base classes defined above. What remains is to define a model just like you would do for any Django Model. Here is an example (you have tons of them in app already):
...@@ -64,21 +60,19 @@ class Country(BaseModel): ...@@ -64,21 +60,19 @@ class Country(BaseModel):
:warning: Also, our use of models in the app differs from Django on one aspect. To handle different moderation settings, there is one extra **optional** attribute you can use in your model which is `moderation_level`. It can either be `0`, `1` or `2` (default value). You can find more about this parameter [here](Application/Backend/moderation_and_versioning?id=model-level). :warning: Also, our use of models in the app differs from Django on one aspect. To handle different moderation settings, there is one extra **optional** attribute you can use in your model which is `moderation_level`. It can either be `0`, `1` or `2` (default value). You can find more about this parameter [here](Application/Backend/moderation_and_versioning?id=model-level).
### Serializers ### Serializers
To go along the previously mentioned _abstract_ models, we have matching serializers that handle all the logic behind rendering the models' field to JSON, validating the data before creating/updating a model instance and handling the specific behaviors (such as moderation — that comes with all models that inherit from `EssentialModule` — and versioning — that comes with all models that inherit from `VersionedEssentialModule`).
To go along the previously mentioned *abstract* models, we have matching serializers that handle all the logic behind rendering the models' field to JSON, validating the data before creating/updating a model instance and handling the specific behaviors (such as moderation — that comes with all models that inherit from `EssentialModule` — and versioning — that comes with all models that inherit from `VersionedEssentialModule`). :warning: **95% of the _magic_ behind those not that simple behaviors are handled in a subtle, sometimes _hacky_, manner.**
:warning: **95% of the *magic* behind those not that simple behaviors are handled in a subtle, sometimes *hacky*, manner.**
:information_desk_person: All those specific behaviors should have been correctly unit-tested at this point. :information_desk_person: All those specific behaviors should have been correctly unit-tested at this point.
?> You can find more information about moderation and versioning [here](Application/Backend/moderation_and_versioning.md). ?> You can find more information about moderation and versioning [here](Application/Backend/moderation_and_versioning.md).
---- ---
To create a serializer for your model, you need to create a class that inherits from the matching parent class of your model. For instance the `Country` model inherits from the `BaseModel` *abstract* class, so the `CountrySerializer` inherits from `BaseModelSerializer`. To create a serializer for your model, you need to create a class that inherits from the matching parent class of your model. For instance the `Country` model inherits from the `BaseModel` _abstract_ class, so the `CountrySerializer` inherits from `BaseModelSerializer`.
Then you must define a `Meta` class inside the serializer that contains information about what model is the serializer for and what fields (attribute) should be taken into account. For instance for the country model we have the following serializer: Then you must define a `Meta` class inside the serializer that contains information about what model is the serializer for and what fields (attribute) should be taken into account. For instance for the country model we have the following serializer:
...@@ -126,10 +120,8 @@ class BaseModelSerializer(MySerializerWithJSON): ...@@ -126,10 +120,8 @@ class BaseModelSerializer(MySerializerWithJSON):
As shown above, when using a `custom_attr = serializers.SerializerMethodField()`, you need to define a method in the class that is named `get_custom_attr` and returns whatever you want (as soon as it can be converted to JSON). As shown above, when using a `custom_attr = serializers.SerializerMethodField()`, you need to define a method in the class that is named `get_custom_attr` and returns whatever you want (as soon as it can be converted to JSON).
?> Django-rest-framework is full a functionalities, and only a subset of them have been presented here. So feel free to have a look at [the package documentation](https://www.django-rest-framework.org/). ?> Django-rest-framework is full a functionalities, and only a subset of them have been presented here. So feel free to have a look at [the package documentation](https://www.django-rest-framework.org/).
### Viewsets ### Viewsets
The custom work performed on the generic viewsets is fairly straightforward, you can have a look at it. Anyway, just like with the serializer, you should use the corresponding viewset when inheriting: `BaseModelViewSet` or `EssentialModuleViewSet` or `VersionedEssentialModuleViewSet` or `ModuleViewSet`. The custom work performed on the generic viewsets is fairly straightforward, you can have a look at it. Anyway, just like with the serializer, you should use the corresponding viewset when inheriting: `BaseModelViewSet` or `EssentialModuleViewSet` or `VersionedEssentialModuleViewSet` or `ModuleViewSet`.
...@@ -138,10 +130,10 @@ All the parametrization of the ViewSets happens within them. ...@@ -138,10 +130,10 @@ All the parametrization of the ViewSets happens within them.
Just like in the standard use of the Django Rest Framework, you should set the following attributes: Just like in the standard use of the Django Rest Framework, you should set the following attributes:
* `queryset` (or `get_queryset`): the queryset associated with the viewset, - `queryset` (or `get_queryset`): the queryset associated with the viewset,
* `serializer_class`: what is the serializer class to use to serialize the queryset. - `serializer_class`: what is the serializer class to use to serialize the queryset.
* `viewset_permission` (defaults to `(IsAuthenticated & (IsStaff | NoDelete))`): what are the permissions associated with each viewset. If something is specified then it will be composed (`&`) with the default one. You can compose permissions with `&` (logical *and*) or `|` (logical *or*). Most permissions are defined in `backend/backend_app/permissions/app_permissions.py`, you can add yours too. - `viewset_permission` (defaults to `(IsAuthenticated & (IsStaff | NoDelete))`): what are the permissions associated with each viewset. If something is specified then it will be composed (`&`) with the default one. You can compose permissions with `&` (logical _and_) or `|` (logical _or_). Most permissions are defined in `backend/backend_app/permissions/app_permissions.py`, you can add yours too.
* `filterset_fields` (optional): more information [here](Application/Backend/models_serializers_viewsets?id=filtering) - `filterset_fields` (optional): more information [here](Application/Backend/models_serializers_viewsets?id=filtering)
?> :information_desk_person: If you set `viewset_permission`, it must be a tuple; (in our case it will often have only one attribute). ?> :information_desk_person: If you set `viewset_permission`, it must be a tuple; (in our case it will often have only one attribute).
...@@ -153,7 +145,7 @@ To do so, you must add the `end_point_route` attribute to your ViewSet class (e. ...@@ -153,7 +145,7 @@ To do so, you must add the `end_point_route` attribute to your ViewSet class (e.
!> As a result, **if you change an `api_end_pont` you will most likely need to update some JS files (see [here](Application/Frontend/redux.md)).** !> As a result, **if you change an `api_end_pont` you will most likely need to update some JS files (see [here](Application/Frontend/redux.md)).**
----- ---
Most often a viewset class will look like this: Most often a viewset class will look like this:
...@@ -185,12 +177,12 @@ class CountryDriViewSet(ModuleViewSet): ...@@ -185,12 +177,12 @@ class CountryDriViewSet(ModuleViewSet):
This filtering is achieved through [`django-filter`](https://django-filter.readthedocs.io/en/master/) package (and the [standard api provided by `django-rest-framework`](https://www.django-rest-framework.org/api-guide/filtering/)), you can further customize the filtering if you'd like to. This filtering is achieved through [`django-filter`](https://django-filter.readthedocs.io/en/master/) package (and the [standard api provided by `django-rest-framework`](https://www.django-rest-framework.org/api-guide/filtering/)), you can further customize the filtering if you'd like to.
------ ---
There exist an other way of filtering directly on the client request, **but you should use it only when there are absolutely no other alternatives or when parameters are absolutely required**. You can see this other way below: There exist an other way of filtering directly on the client request, **but you should use it only when there are absolutely no other alternatives or when parameters are absolutely required**. You can see this other way below:
* Endpoint's attributes are set to be captured in `end_point_route`, - Endpoint's attributes are set to be captured in `end_point_route`,
* We make use of them in `get_queryset`. - We make use of them in `get_queryset`.
```python ```python
class VersionViewSet(BaseModelViewSet): class VersionViewSet(BaseModelViewSet):
...@@ -208,7 +200,6 @@ class VersionViewSet(BaseModelViewSet): ...@@ -208,7 +200,6 @@ class VersionViewSet(BaseModelViewSet):
return Version.objects.get_for_object(obj) return Version.objects.get_for_object(obj)
``` ```
#### Performance #### Performance
!> **Viewsets can be huge bottlenecks for performances.** !> **Viewsets can be huge bottlenecks for performances.**
......
Moderation & Versioning in the app # Moderation & Versioning in the app
=================================
## Moderation ## Moderation
...@@ -13,20 +12,18 @@ TODO clean ...@@ -13,20 +12,18 @@ TODO clean
Model `moderation_level` can take the following values: Model `moderation_level` can take the following values:
* `0`: moderation will never be applied, - `0`: moderation will never be applied,
* `1`: moderation will be on if the global settings for moderation is turned on, - `1`: moderation will be on if the global settings for moderation is turned on,
* `2`: (default for security reasons) moderation will always be on no matter what. - `2`: (default for security reasons) moderation will always be on no matter what.
*NB: staff members, dri members and moderators won't be subject to (model-level) moderation.* _NB: staff members, dri members and moderators won't be subject to (model-level) moderation._
*NB: moderation can be moreover enforced at the object level. But that's for [another documentation part](Application/Backend/moderation_and_versioning.md).*
_NB: moderation can be moreover enforced at the object level. But that's for [another documentation part](Application/Backend/moderation_and_versioning.md)._
### Object/instance level ### Object/instance level
!> TODO !> TODO
## Versioning ## Versioning
### General idea ### General idea
......
Backend tests # Backend tests
======
*Rex-DRI* comes in with several *tests* that you can perform locally using the commands `make test_backend` and `make test_frontend` once the project is up and running (`make up`). _Rex-DRI_ comes in with several _tests_ that you can perform locally using the commands `make test_backend` and `make test_frontend` once the project is up and running (`make up`).
## General words ## General words
...@@ -13,11 +12,10 @@ Also, there are tests regarding some custom validation. ...@@ -13,11 +12,10 @@ Also, there are tests regarding some custom validation.
Finally, the initial and example loading data scripts are tested. Finally, the initial and example loading data scripts are tested.
## Documentation ## Documentation
Some usefull links to get inspired: Some useful links to get inspired:
- [General information about testing in Django](https://docs.djangoproject.com/fr/2.1/topics/testing/overview/) - [General information about testing in Django](https://docs.djangoproject.com/fr/2.1/topics/testing/overview/)
- [`pytest` documentation](https://docs.pytest.org/en/latest/) - [`pytest` documentation](https://docs.pytest.org/en/latest/)
- [`pytest-django` documentation](https://pytest-django.readthedocs.io/en/latest/) - [`pytest-django` documentation](https://pytest-django.readthedocs.io/en/latest/)
Map -- info # Map -- info
===========
## Why hosting our own maps ? ## Why hosting our own maps ?
...@@ -8,15 +6,14 @@ We decided to host our own maps in order to provide this service for free for ev ...@@ -8,15 +6,14 @@ We decided to host our own maps in order to provide this service for free for ev
This has several drawbacks: This has several drawbacks:
* The tiles are not really up to date (unless we update them regurlarly but this process is complicated and costly), - The tiles are not really up to date (unless we update them regurlarly but this process is complicated and costly),
* We will provide only a reasonable level zoom so that we don't store a huge volumetry of data. - We will provide only a reasonable level zoom so that we don't store a huge volumetry of data.
And some awesome advantages: And some awesome advantages:
* Custom styling, - Custom styling,
* Vector styles, - Vector styles,
* Free for ever :) - Free for ever :)
## Custom styling ## Custom styling
...@@ -24,7 +21,6 @@ Two styles have been derived from the ones available [here](http://editor.openma ...@@ -24,7 +21,6 @@ Two styles have been derived from the ones available [here](http://editor.openma
You can import the one in the project `server/map/styles` to update them. You can import the one in the project `server/map/styles` to update them.
!> :warning: Before editing them, on the service mentioned above, you must replace some lines. !> :warning: Before editing them, on the service mentioned above, you must replace some lines.
In `light/styles.json`, replace: In `light/styles.json`, replace:
...@@ -53,7 +49,7 @@ by: ...@@ -53,7 +49,7 @@ by:
"glyphs": "https://free.tilehosting.com/fonts/{fontstack}/{range}.pbf?key={key}", "glyphs": "https://free.tilehosting.com/fonts/{fontstack}/{range}.pbf?key={key}",
``` ```
----- ---
In `dark/styles.json`, replace: In `dark/styles.json`, replace:
......
Frontend tests # Frontend tests
======
*Rex-DRI* comes in with several *tests* that you can perform locally using the commands `make test_backend` and `make test_frontend` once the project is up and running (`make up`). _Rex-DRI_ comes in with several _tests_ that you can perform locally using the commands `make test_backend` and `make test_frontend` once the project is up and running (`make up`).
?> :information_desk_person: As of now, the frontend tests only concern specific functions and not react components. ?> :information_desk_person: As of now, the frontend tests only concern specific functions and not react components.
......
Troubleshooting # Troubleshooting
===============
## General theme / app bar color is messed up ## General theme / app bar color is messed up
?> :information_desk_person: Check that none of your imports from `material-ui` ends with `/index` (webstorm adds them sometimes and it seems to break some stuff along the way). ?> :information_desk_person: Check that none of your imports from `material-ui` ends with `/index` (webstorm adds them sometimes and it seems to break some stuff along the way).
IDE setup # IDE setup
============
The project was initially developed with VScode but can obviously be enhanced with the IDE of your choice. At the time of this writing, the use of JetBrains IDEs is highly recommended as they are really powerful IDEs (**if you are a student you can get their *ultimate* versions for free**). The project was initially developed with VScode but can obviously be enhanced with the IDE of your choice. At the time of this writing, the use of JetBrains IDEs is highly recommended as they are really powerful IDEs (**if you are a student you can get their _ultimate_ versions for free**).
In this short documentation, some configurations "issues" regarding your choice of IDE is addressed. Also, don't miss the separate documentation about the configuration of your [IDE for debugging](Technologies/debugging.md). In this short documentation, some configurations "issues" regarding your choice of IDE is addressed. Also, don't miss the separate documentation about the configuration of your [IDE for debugging](Technologies/debugging.md).
?> :information_desk_person: JetBrains IDEs are cranked with features and plugin which can make them a bit slow on some system. More than 75% of those plugin are completely useless for the project so you should dive into the settings and deactivate them. ?> :information_desk_person: JetBrains IDEs are cranked with features and plugin which can make them a bit slow on some system. More than 75% of those plugin are completely useless for the project so you should dive into the settings and deactivate them.
!> In JetBrain IDEs you should also deactivate *auto-save* since this would cause useless recompilation of the frontend (or useless restart of the backend). Check [this link](https://intellij-support.jetbrains.com/hc/en-us/community/posts/207054215-Disabling-autosave). !> In JetBrain IDEs you should also deactivate _auto-save_ since this would cause useless recompilation of the frontend (or useless restart of the backend). Check [this link](https://intellij-support.jetbrains.com/hc/en-us/community/posts/207054215-Disabling-autosave).
!> In JetBrain IDEs **You must disable `Use "safe write"...` to make sure changes are detected and the project automatically recompiles.** !> In JetBrain IDEs **You must disable `Use "safe write"...` to make sure changes are detected and the project automatically recompiles.**
## For the backend ## For the backend
For the backend the use of [`PyCharm`](https://www.jetbrains.com/pycharm/?fromMenu) is recommended. For the backend the use of [`PyCharm`](https://www.jetbrains.com/pycharm/?fromMenu) is recommended.
In this case you only need to configure the correct python interpreter: In this case you only need to configure the correct python interpreter:
* Open the settings (`CTRL + Alt + S`) go to `project: backend` and `Project interpreter`. - Open the settings (`CTRL + Alt + S`) go to `project: backend` and `Project interpreter`.
* Select `Docker` and put `registry.gitlab.utc.fr/rex-dri/rex-dri/backend:latest` for the image name. - Select `Docker` and put `registry.gitlab.utc.fr/rex-dri/rex-dri/backend:latest` for the image name.
This will give PyCharm access to the project python's dependencies. This will give PyCharm access to the project python's dependencies.
Further configuration is required to run the project (optionally in debug mode) from the IDE. This is documented [here](Technologies/debugging.md). Further configuration is required to run the project (optionally in debug mode) from the IDE. This is documented [here](Technologies/debugging.md).
## For the frontend ## For the frontend
For the frontend the use of [`WebStorm`](https://www.jetbrains.com/webstorm/?fromMenu) is recommended. For the frontend the use of [`WebStorm`](https://www.jetbrains.com/webstorm/?fromMenu) is recommended.
No special configuration is required, except you might: No special configuration is required, except you might:
* Need to install `Node.js` on your system and `yarn` (`npm install -g yarn`) - Need to install `Node.js` on your system and `yarn` (`npm install -g yarn`)
* You should install the Node.js dependencies locally (they are in the frontend docker image, but they can't be used through the IDE this time). So run `yarn install`. - You should install the Node.js dependencies locally (they are in the frontend docker image, but they can't be used through the IDE this time). So run `yarn install`.
## For general purposes ## For general purposes
......
Introduction # Introduction
=======
Welcome on the `Rex-DRI` documentation. Welcome on the `Rex-DRI` documentation.
`Rex-DRI` is a plateforme to capitalize on international exchange that outbound *Université de Technologie de Compiègne, France*. `Rex-DRI` is a plateforme to capitalize on international exchange that outbound _Université de Technologie de Compiègne, France_.
!> This app makes use notably of `Docker`, `Python`, `Django`, `Js`, `React` and `PostgreSQL`. !> This app makes use notably of `Docker`, `Python`, `Django`, `Js`, `React` and `PostgreSQL`.
If you are not familiar with these technologies, have no worries! 😊 You can find very good resources online. For instance: If you are not familiar with these technologies, have no worries! 😊 You can find very good resources online. For instance:
- Django: [a good tutorial in French](https://tutorial.djangogirls.org/fr/django_start_project/); - Django: [a good tutorial in French](https://tutorial.djangogirls.org/fr/django_start_project/);
- *More to come...* - _More to come..._
Set-up # Set-up
=======
## Git ## Git
_If you don't have `git` install on your computer, look online and install it :tongue: ._
*If you don't have `git` install on your computer, look online and install it :tongue: .*
Then, you can simply clone the project repository: Then, you can simply clone the project repository:
...@@ -14,35 +10,24 @@ Then, you can simply clone the project repository: ...@@ -14,35 +10,24 @@ Then, you can simply clone the project repository:
git clone git@gitlab.utc.fr:rex-dri/rex-dri.git && cd rex-dri git clone git@gitlab.utc.fr:rex-dri/rex-dri.git && cd rex-dri
``` ```
## Docker and docker-compose ## Docker and docker-compose
This projects takes advantage of `docker` and `docker-compose` to ease the setup process, continuous integration, deployments, etc. This projects takes advantage of `docker` and `docker-compose` to ease the setup process, continuous integration, deployments, etc.
!> If you don't have those tools on your computer, you first need to install them. You can do so by following those guides: