Skip to content
Snippets Groups Projects
Commit 69789ad4 authored by Victor Löfgren's avatar Victor Löfgren
Browse files

Merge branch '181-documentation' of...

Merge branch '181-documentation' of gitlab.liu.se:tddd96-grupp11/teknikattan-scoring-system into 181-documentation
parents 8d02154c cf354543
No related branches found
No related tags found
1 merge request!159Resolve "Documentation"
Installation Installation
============ ============
Here we will describe how to install the application. This section will describe how to install the application.
You will need to install both the client and the server. You will need to install both the client and the server.
.. toctree:: .. toctree::
......
...@@ -5,7 +5,7 @@ In order to install the client, you will need to do the following: ...@@ -5,7 +5,7 @@ In order to install the client, you will need to do the following:
Install [Node (LTS)](https://nodejs.org/en/). Install [Node (LTS)](https://nodejs.org/en/).
Clone [teknikattan-scoring-system](https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system). Clone the git repository [teknikattan-scoring-system](https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system).
Open a terminal and navigate to the root of the cloned project. Open a terminal and navigate to the root of the cloned project.
...@@ -19,3 +19,5 @@ npm install ...@@ -19,3 +19,5 @@ npm install
You should now be ready to start the client. You should now be ready to start the client.
Try it by running `npm run start`. Try it by running `npm run start`.
A web page should open where you can see the [login page](../user_manual/login.md). A web page should open where you can see the [login page](../user_manual/login.md).
[comment]: # (Should we mention the task for starting the client?)
...@@ -5,7 +5,7 @@ In order to install the server, you will need to do the following: ...@@ -5,7 +5,7 @@ In order to install the server, you will need to do the following:
Install [Python](https://www.python.org/downloads/). Install [Python](https://www.python.org/downloads/).
Clone [teknikattan-scoring-system](https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system). Clone the git repository [teknikattan-scoring-system](https://gitlab.liu.se/tddd96-grupp11/teknikattan-scoring-system).
Open a terminal and navigate to the root of the cloned project. Open a terminal and navigate to the root of the cloned project.
...@@ -32,7 +32,7 @@ On Linux/Mac: ...@@ -32,7 +32,7 @@ On Linux/Mac:
source env/bin/activate source env/bin/activate
``` ```
Install all project dependencies: Lastly, install all project dependencies:
```bash ```bash
pip install -r requirements.txt pip install -r requirements.txt
...@@ -41,3 +41,5 @@ pip install -r requirements.txt ...@@ -41,3 +41,5 @@ pip install -r requirements.txt
You should now be ready to start the server. You should now be ready to start the server.
Try it by running `python main.py` and navigate to `localhost:5000`. Try it by running `python main.py` and navigate to `localhost:5000`.
If everything worked as it should you should see a list of all available API calls. If everything worked as it should you should see a list of all available API calls.
[comment]: # (Should we mention the task for starting the server?)
System overview System overview
=============== ===============
Here we briefly describe how the entire system works. This is a brief overview of how the entire system works.
Then we go into more detail about the client and the server. There is then more detail about the client and the server.
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
......
# Client overview # Client overview
The client is the main part of the system. The client is the main part of the system.
It is divided into 4 parts: login, admin, presentation editor and active competitions (presentations). It is divided into 4 pages: login, admin, presentation editor and active competitions (presentations).
The presentations is also further divided into four different parts: operator view, audience view, team view and judge view. The presentations is also further divided into four different views: operator view, audience view, team view and judge view.
## Competitions and Presentations
In this project competitions are often refered to when meaning un-active competitions while presentations are refered to when meaning active competitions involving multiple users and sockets connecting them.
## File structure ## File structure
All of the actual pages in the system is stored in the `client/src/pages/` folder. All of the source code for the pages in the system is stored in the `client/src/pages/` folder.
For each of the different parts there is a corresponding file that ends in Page, for example `JudgeViewPage.tsx` or `LoginPage.tsx`. For each of the different parts there is a corresponding file that ends in Page, for example `JudgeViewPage.tsx` or `LoginPage.tsx`.
This is the main file for that page. This is the main file for that page.
All of these pages also has their own and shared components, in the folder relative to the page `./components/`. All of these pages also has their own and shared components, in the folder relative to the page `./components/`.
Every React component should also have their responding test file.
## Routes
All pages have their own route which is handled in `client/src/Main.tsx`. Futhermore the admin page has one route for each of the tabs which helps when reloading the site to select the previously selected tab. There is also a route for logging in with code which makes it possible to go to for example `localhost:3000/CODE123` to automatically join a competition with that code.
## Authentication
Authentication is managed by using JWT from the API. The JWT for logging in is stored in local storage under `token`. The JWT for active presentations are stored in local storage `RoleToken` so for example the token for Operator is stored in local storage under `OperatorToken`.
## Prettier and Eslint
[Eslint](https://eslint.org/) is used to set rules for syntax, [prettier](https://prettier.io/) is then used to enforce these rules when saving a file. Eslint is set to only warn about linting warnings. These libraries have their own config files which can be used to change their behavior: `client/.eslintrc` and `client/.prettierrc`
## Redux
[Redux](https://eslint.org/) is used for state management along with the [thunk](https://github.com/reduxjs/redux-thunk) middleware which helps with asynchronous actions. Action creators are under `client/src/actions.ts`, these dispatch actions to the reducers under `client/src/reducers.ts` that update the state. The interfaces for the states is saved in each reducer along with their initial state. When updating the state in the reducers the action payload is casted to the correct type to make the store correctly typed.
## Interfaces
In `client/src/interfaces` all interfaces that are shared in the client is located. `client/src/interfaces/ApiModels.ts` and `client/src/interfaces/ApiRichModels.ts` includes all models from the api and should always be updated when editing models on the back-end. This folder also includes some more specific interfaces that are re-used in the client.
...@@ -5,19 +5,21 @@ The terms frontend and client as well as backend and server will be used interch ...@@ -5,19 +5,21 @@ The terms frontend and client as well as backend and server will be used interch
![Systemöversikt](../_static/system_overview.svg) ![Systemöversikt](../_static/system_overview.svg)
First we have the main server which is written in Python using the micro-framework Flask. First there is the main server which is written in Python using the micro-framework Flask.
Then have a fairly small Node server who's only function is to serve the React frontend pages. Then there is a fairly small Node server with only one function, to serve the React frontend pages.
Lastly we have the frontend which is written in TypeScript using React and Redux. Lastly there is the frontend which is written in TypeScript using React and Redux.
## Communication ## Communication
The frontend communicates with the backend in two ways. The frontend communicates with the backend in two ways, both of which are authorized on the server.
All of the following ways are authorized on the server to make sure that who ever tried to communicate has the correct access level. This is to make sure that whoever tries to communicate has the correct level of access.
### API ### API
API calls are used for simple functions the client wants to perform, such as getting, editing and saving data. [comment]: # (What does "that will proxy the request to the main Python server" mean?)
These are sent from the client to the backend Node server who will proxy the request to the main Python server.
API calls are used for simple functions that the client wants to perform, such as getting, editing, and saving data.
These are sent from the client to the backend Node server that will proxy the request to the main Python server.
The request will then be handled there and the response will be sent back. The request will then be handled there and the response will be sent back.
The Node server will then send them back to the client. The Node server will then send them back to the client.
......
...@@ -3,89 +3,91 @@ ...@@ -3,89 +3,91 @@
The server has two main responsibilities. The server has two main responsibilities.
The first is to handle API calls from the client to store, update and delete information, such as competitions or users. The first is to handle API calls from the client to store, update and delete information, such as competitions or users.
It also needs to make sure that only authorized people can access these. It also needs to make sure that only authorized people can access these.
The other is to sync slides, timer and answers between clients in an active competition. The other responsibility is to sync slides, timer and answers between clients in an active competition.
Both of these will be described in more detail below. Both of these will be described in more detail below.
## Receiving API calls ## Receiving API calls
An API call is a way the client can communicates with the server. An API call is a way for the client to communicate with the server.
When a request is received the server begins by authorizing it (making sure the person sending the request is allowed to access the route). When a request is received the server begins by authorizing it (making sure the person sending the request is allowed to access the route).
After that it makes sure that it got all information in the request it needed. After that it confirms that it got all information in the request that it needed.
The server will then do the thing the client requested. The server will then process the client request.
And finally it will need to generate response, usually in the form of an object from the database. Finally it generates a response, usually in the form of an object from the database.
All of these steps are described in more detail below. All of these steps are described in more detail below.
### Routes ### Routes
Each route which is possible to call is specified in the files in the `app/apis/` folder. Each existing route that can be called is specified in the files in the `app/apis/` folder.
All available routes can also be seen by navigating to `localhost:5000` after starting the server. All available routes can also be seen by navigating to `localhost:5000` after starting the server.
### Authorization ### Authorization
When the server receives an API call the first thing it does is to authorize it. When the server receives an API call it will first check that the call is authorized.
The authorization is done using JSON Web Tokens (JWT) by comparing the contents of them with what is expected. The authorization is done using JSON Web Tokens (JWT) by comparing the contents of them with what is expected.
Whenever a client logs into an account or joins a competition, it is given a JWT generated by the server, and the client will need to use this token in every subsequent request sent to the server to authenticate itself. Whenever a client logs into an account or joins a competition, it is given a JWT generated by the server, and the client will need to use this token in every subsequent request sent to the server in order to authenticate itself.
What authorization to be done on the server is specified by the `@protect_route()` decorator. The needed authorization is specified by the `@protect_route()` decorator.
This decorator specifies who is allowed to access this route, which can either be users with specific roles, or people who have joined competitions with specific views. This decorator specifies who is allowed to access this route, which can either be users with specific roles, or people that have joined competitions with specific views.
If the route is not decorated everyone is allowed to access it, the only routes currently like that is logging in as a user and joining a competition, by necessity. If the route is not decorated everyone is allowed to access it, and the only routes currently like that is, by necessity, logging in as a user and joining a competition.
#### JSON Web Tokens (JWT) #### JSON Web Tokens (JWT)
We use JSON Web Tokens (JWT) for authentication, both for API and socket events. JSON Web Tokens (JWT) are used for authentication, both for API and socket events.
A JWT is created on the server when a user logs in or connects to competition. A JWT is created on the server when a user logs in or connects to a competition.
We store some information in the JWT, which can be seen in the file `server/app/apis/auth.py`. Some information is stored in the JWT, which can be seen in the file `server/app/apis/auth.py`.
The JWT is also encrypted using the secret key defined in `server/configmodule.py`. The JWT is also encrypted using the secret key defined in `server/configmodule.py`.
(OBS: Change this key before running the server in production). (NOTE: Change this key before running the server in production).
The client can read the contents of the JWT but cannot modify them because it doesn't have access to the secret key. The client can read the contents of the JWT but cannot modify them because it doesn't have access to the secret key.
This is why the server can simply read the contents of the JWT to be sure that the client is who it says it is. This is why the server can simply read the contents of the JWT to be sure that the client is who it says it is.
### Parsing request ### Parsing request
After the request is authorized the server will need to parse contents of the request. After the request is authorized the server will need to parse the contents of the request.
The parsing is done with [reqparse](https://flask-restx.readthedocs.io/en/latest/parsing.html) from RestX (this module is deprecated and should be replaced). The parsing is done with [reqparse](https://flask-restx.readthedocs.io/en/latest/parsing.html) from RestX (this module is deprecated and should be replaced).
Each API call expects different parameters in different places and this is specified in each of the files in `app/apis/` folder, together with the route. Each API call expects different parameters in different places and this is specified in each of the files in `app/apis/` folder, together with the route.
### Handling request ### Handling request
After the request has been authorized and parsed the server needs to act on the request. After the request has been authorized and parsed the server will process the request.
What the server does of course depends on the route and given arguments, but it usually gets, edits or deletes something from the database. What it does depends on the route and the given arguments, but it usually gets, edits or deletes something from the database.
The server uses an SQL database and interfaces to it via SQLAlchemy. The server uses an SQL database and interfaces to it via SQLAlchemy.
Everything related to the database is located in the `app/database/` folder. Everything related to the database is located in the `app/database/` folder.
### Responding ### Responding
When the server is done handling the request it usually responds with an item from the database. When the server har processed the request it usually responds with an item from the database.
Converting a database object to json is done with [Marsmallow](https://marshmallow.readthedocs.io/en/stable/). Converting a database object to json is done with [Marsmallow](https://marshmallow.readthedocs.io/en/stable/).
How to do this conversion is specified in two files in in the folder `app/core/`. This conversion is specified in two files in the folder `app/core/`.
The file `schemas.py` just converts a record in the database field by field. The file `schemas.py` converts a record in the database field by field.
The file `rich_schemas.py` on the other hand converts an `id` in one table to an entire object in the another table, thus the name rich. The file `rich_schemas.py` on the other hand converts an `id` in one table to an entire object in the another table, thus the name rich.
In this way, for example, an entire competition with it's teams, codes, slides and the slides' questions and components can be returned in a single API call. In this way, for example, an entire competition with its teams, codes, slides and the slides' questions and components can be returned in a single API call.
## Active competitions ## Active competitions
Slides and timers (and answers) needs to be synced during an active presentation. Slides, timers, and answers needs to be synced during an active presentation.
This is done using SocketIO together with flask_socketio. This is done using SocketIO together with flask_socketio.
Events sent is also authorized via JWT, basically the same way as the for the API calls. Sent events are also authorized via JWT, basically the same way as the for the API calls.
But for socket events, the decorator that is used to authenticate them is `@authorize_user()`. But for socket events, the decorator that is used to authenticate them is `@authorize_user()`.
Whenever client joins a competition they will connect via sockets. Whenever a client joins a competition they will connect via sockets.
A single competition cannot be active more than once at the same time. A single competition cannot be active more than once at the same time.
This means that you will need to make a copy of a competition if you want to run multiple times at the same time. This means that you will need to make a copy of a competition if you want to run the same competition at several locations at the same time.
All of the functionality related to an active competition and sockets can be found in the file `app/core/sockets.py`. All of the functionality related to an active competition and sockets can be found in the file `app/core/sockets.py`.
The terms `active competition` and `presentation` means the same thing. The terms *active competition* and *presentation* are equivalent.
### Starting and joing presentations ### Starting and joing presentations
Whenever a client types in a code in the client it will be checked via the `api/auth/login/code` API call. Whenever a code is typed in to the client it will be checked via the `api/auth/login/code` API call.
If there is such a code and it was an operator code, the client will receive a JWT it will need to use to authenticate itself. If there is such a code and it was an operator code, the client will receive the JWT it will need to use to authenticate itself.
If there is such a code and the associated competition is active, the client will also receive a JWT, regardless if it was an operator code or not. If there is such a code and the associated competition is active, the client will also receive a JWT for its corresponding role.
Both of these cases will be handled by the default `connect` event, using the JWT received from the API call. Both of these cases will be handled by the default `connect` event, using the JWT received from the API call.
The server can see what is stored in the JWT and do different things depending on it's contents. The server can see what is stored in the JWT and do different things depending on its contents.
### Syncing between clients ### Syncing between clients
The operator will emit the `sync` event and provide either slide or timer to update it on the server. [comment]: # (What does `sync` mean? It isn't explained)
The operator will emit the `sync` event and provide either a slide or a timer to update it on the server.
The server will then send `sync` to all connected clients with the updated values, regardless of what was actually updated. The server will then send `sync` to all connected clients with the updated values, regardless of what was actually updated.
The server will also store the timer and active slide in order to `sync` clients when they join. The server will also store the timer and active slide in order to `sync` clients when they join.
The operator can also emit `end_presentation` to disconnect all clients from it's competitions. The operator can also emit `end_presentation` to disconnect all clients from its competitions.
This will also end the presentation. This will also end the presentation.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment