mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-09 19:40:19 +00:00
Rewrite documentation with MkDocs
This commit is contained in:
BIN
docs/Comptes-rendus/CR 16.01.05.pdf
Normal file
BIN
docs/Comptes-rendus/CR 16.01.05.pdf
Normal file
Binary file not shown.
BIN
docs/Comptes-rendus/CR 16.11.10.pdf
Normal file
BIN
docs/Comptes-rendus/CR 16.11.10.pdf
Normal file
Binary file not shown.
BIN
docs/Comptes-rendus/CR 16.11.16.pdf
Normal file
BIN
docs/Comptes-rendus/CR 16.11.16.pdf
Normal file
Binary file not shown.
BIN
docs/Comptes-rendus/CR 16.12.1.pdf
Normal file
BIN
docs/Comptes-rendus/CR 16.12.1.pdf
Normal file
Binary file not shown.
BIN
docs/Comptes-rendus/CR 16.12.15.pdf
Normal file
BIN
docs/Comptes-rendus/CR 16.12.15.pdf
Normal file
Binary file not shown.
BIN
docs/Comptes-rendus/CR 16.12.8.pdf
Normal file
BIN
docs/Comptes-rendus/CR 16.12.8.pdf
Normal file
Binary file not shown.
BIN
docs/archives/Architecture_et_presentation_-_Skia_2016.pdf
Normal file
BIN
docs/archives/Architecture_et_presentation_-_Skia_2016.pdf
Normal file
Binary file not shown.
1
docs/archives/TO_Skia_LoJ/.gitignore
vendored
Normal file
1
docs/archives/TO_Skia_LoJ/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_minted-Rapport/
|
BIN
docs/archives/TO_Skia_LoJ/Couvertures.odg
Normal file
BIN
docs/archives/TO_Skia_LoJ/Couvertures.odg
Normal file
Binary file not shown.
BIN
docs/archives/TO_Skia_LoJ/Couvertures.pdf
Normal file
BIN
docs/archives/TO_Skia_LoJ/Couvertures.pdf
Normal file
Binary file not shown.
13
docs/archives/TO_Skia_LoJ/Makefile
Normal file
13
docs/archives/TO_Skia_LoJ/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
CC=pdflatex
|
||||
|
||||
all: rapport clean
|
||||
|
||||
rapport: Rapport.tex
|
||||
@echo "Compiling "$<
|
||||
$(CC) -shell-escape $<
|
||||
$(CC) -shell-escape $<
|
||||
|
||||
clean:
|
||||
@echo "Cleaning folder"
|
||||
rm *.aux; rm *.log; rm *.out; rm *.toc; rm *.snm; rm *.nav; rm *.lof
|
||||
|
BIN
docs/archives/TO_Skia_LoJ/Rapport.pdf
Normal file
BIN
docs/archives/TO_Skia_LoJ/Rapport.pdf
Normal file
Binary file not shown.
490
docs/archives/TO_Skia_LoJ/Rapport.tex
Normal file
490
docs/archives/TO_Skia_LoJ/Rapport.tex
Normal file
@ -0,0 +1,490 @@
|
||||
%%
|
||||
%
|
||||
% Skia
|
||||
% skia@libskia.so
|
||||
%
|
||||
%%
|
||||
|
||||
\documentclass[a4paper]{report}
|
||||
|
||||
%packages
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[francais]{babel}
|
||||
\usepackage{graphicx}\graphicspath{{pix/}}
|
||||
\usepackage{float}
|
||||
\usepackage{scrextend}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage{color}
|
||||
\usepackage{fancyhdr}
|
||||
%Options: Sonny, Lenny, Glenn, Conny, Rejne, Bjarne, Bjornstrup
|
||||
\usepackage[Bjornstrup]{fncychap}
|
||||
\usepackage{minted}
|
||||
\usepackage[colorlinks=true,linkcolor=black]{hyperref}
|
||||
\usepackage{pdfpages}
|
||||
%\usepackage{titlesec, blindtext, color}
|
||||
|
||||
%pdf metadata
|
||||
\hypersetup{
|
||||
unicode=true,
|
||||
colorlinks=true,
|
||||
citecolor=black,
|
||||
filecolor=black,
|
||||
linkcolor=black,
|
||||
urlcolor=black,
|
||||
pdfauthor={Skia <skia@libskia.so>},
|
||||
pdftitle={},
|
||||
pdfcreator={pdftex},
|
||||
pdfsubject={},
|
||||
pdfkeywords={},
|
||||
}
|
||||
|
||||
|
||||
\definecolor{keywords}{RGB}{200,0,90}
|
||||
\definecolor{comments}{RGB}{50,50,253}
|
||||
\definecolor{red}{RGB}{160,0,0}
|
||||
\definecolor{brown}{RGB}{160,100,100}
|
||||
\definecolor{green}{RGB}{0,200,0}
|
||||
\definecolor{darkgreen}{RGB}{0,130,0}
|
||||
\definecolor{gray}{RGB}{100,100,100}
|
||||
|
||||
|
||||
%inner meta
|
||||
\title{Sith: Développement de nouvelles applications}
|
||||
\author{Florent \textsc{Jacquet}\\
|
||||
Guillaume \textsc{Renaud}}
|
||||
\date{Dernière version: \today}
|
||||
|
||||
\begin{document}
|
||||
|
||||
% \maketitle
|
||||
\includepdf[pages={1}]{Couvertures.pdf}
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\chapter*{Remerciements}
|
||||
\section*{Guillaume \textsc{Renaud}}
|
||||
\par Je remercie tout d'abord Monsieur Frédéric \textsc{Lassabe} qui nous a permis d'effectuer cette TO52 lors de notre cursus à
|
||||
l'UTBM, nous permettant ainsi de mêler nôtre travail scolaire à nôtre envie de participer à l'amélioration de la vie
|
||||
associative de l'UTBM.
|
||||
|
||||
\par Je tiens aussi à remercier Florent \textsc{Jacquet} qui m'a aidé tout au long de ce travail et à qui j'ai pu poser mes
|
||||
différentes questions pour apprendre et comprendre plus rapidement que si j'avais été seul.
|
||||
|
||||
\section*{Florent \textsc{Jacquet}}
|
||||
\par Je remercie également Frédéric \textsc{Lassabe} , non seulement pour la TO, mais également pour la précédente TW. Sans ces
|
||||
deux UV hors emploi du temps, jamais un projet comme ce site de l'AE n'aurait pu voir le jour. Cela a demandé beaucoup
|
||||
d'investissement, et il est plus qu'appréciable de pouvoir obtenir quelques crédits en retour.
|
||||
|
||||
\par Je tiens également à remercier l'ensemble de l'équipe info de l'AE qui s'est motivée ce semestre à organiser des
|
||||
réunions hebdomadaires afin de reprendre le projet du mieux possible. C'est maintenant à eux que va être confié le
|
||||
projet, et il est agréable de constater qu'ils n'ont pas attendu le dernier moment pour se pencher sur la question.
|
||||
|
||||
|
||||
\chapter*{Introduction}
|
||||
\addcontentsline{toc}{chapter}{Introduction}
|
||||
\par Après le développement de la base du nouveau site de l'AE, le projet \emph{Sith}, au Printemps 2016, la mise en
|
||||
production a pu avoir lieu avec succès fin Août 2016.
|
||||
|
||||
\par Mais le site était encore très incomplet, et il était nécessaire d'y ajouter un grand nombre de fonctionnalités
|
||||
moins critiques, celles-ci n'ayant pas de rapport avec l'argent, mais tout de même très utiles pour le fonctionnement
|
||||
de l'AE.
|
||||
|
||||
\par Parmis elles, se trouvait notamment une application de gestion des stocks, qui a été confiée à Guillaume, puisqu'elle
|
||||
concernait en premier lieu le \emph{Bureau des Festivités} et qu'il en était le président. Il était donc parmis les
|
||||
mieux placé pour évaluer le besoin et développer l'outil, d'autant qu'il fallait le concevoir depuis le début, ces
|
||||
fonctions n'étant pas du tout présentes dans l'ancien site.
|
||||
|
||||
\par Du reste, Florent a eu la responsabilité de développer les autres applications, ou bien de gérer leur développement
|
||||
lors qu'il était fait par quelqu'un d'autre.
|
||||
|
||||
\chapter{Eboutic}
|
||||
\label{sec:eboutic}
|
||||
\par Développeur principal: Florent
|
||||
|
||||
\section{But}
|
||||
\label{sub:but}
|
||||
\par Fournir une boutique en ligne, avec paiement sécurisé, compatible avec l'API de paiement du Crédit Agricole.
|
||||
\begin{itemize}
|
||||
\item Gérer les cotisations
|
||||
\item Gérer les rechargements de compte AE
|
||||
\item Gérer différents groupes de vente
|
||||
\end{itemize}
|
||||
|
||||
\section{Principaux problèmes}
|
||||
\label{sec:principaux_problemes}
|
||||
|
||||
\subsection{Interaction avec l'API}
|
||||
\label{sub:interaction_avec_l_api}
|
||||
\par C'est la principale contrainte de cette application. On doit interagir avec les serveurs du Crédit Agricole, et
|
||||
pour cela, ces derniers n'aident pas beaucoup.
|
||||
|
||||
\par Ils fournissent un PDF peu clair\footnote{disponible dans le dossier \url{doc/Etransaction/} des sources du site}
|
||||
expliquant l'implémentation d'un site marchand, en plus des nombreux autres PDF de documentation disponibles à l'adresse
|
||||
\url{https://e-transactions.avem-groupe.com/pages/global.php?page=telechargement}.
|
||||
|
||||
\par Une implémentation de référence uniquement en PHP, et contenant que peut de fonctionnalités par rapport à ce
|
||||
que dit le PDF peut aussi être obtenue, mais n'est guère utile excepté pour la vérification cryptographique de la
|
||||
signature de la réponse. Mais encore, il faut arriver à traduire les fonctions propres à PHP, et ce n'est pas toujours
|
||||
une mince affaire, mais fort heureusement, les algorithmes sont encore assez standards et l'on trouve vite de l'aide
|
||||
quant à ces fonctions.
|
||||
|
||||
\par De plus, certaines informations concernants les numéros d'identification de marchand son incohérents
|
||||
d'une documentation à l'autre, et le plus simple à ce niveau est encore de contacter le support.
|
||||
|
||||
\subsection{Accès concurrentiels}
|
||||
\label{sub:acces_concurrentiels}
|
||||
\par En production, le projet Sith tourne à l'aide d'\textbf{uWSGI}, qui s'occupe lui de gérer les différents processus du
|
||||
logiciel. Cela se traduit par des accès concurrentiels à la base de donnée lors de l'appel de deux pages simultanément
|
||||
qui ont besoin d'accèder aux mêmes ressources.
|
||||
|
||||
\par Le problème n'en est la plupart du temps pas un, mais il devient très critique lorsque la page appelée permet par
|
||||
exemple de recharger un compte AE. Il ne faut alors surtout faire l'opération en double.
|
||||
|
||||
\par Pour protéger ces accès en double, on peut alors utiliser des transactions, et \textbf{Django} fournit une
|
||||
abstraction très pratique: \verb-with transaction.atomic():-.
|
||||
|
||||
\par L'Eboutic, avec sa réponse de la banque, est très sujette à ces accès concurrents, et cela a posé quelques
|
||||
problèmes dans les débuts. La plupart ont été résolu, mais il arrive encore dans les comptoirs d'avoir une vente en
|
||||
double, sans pour autant avoir le débit du compte qui soit doublé. Cela ne pose pas foncièrement de problèmes, puisque
|
||||
le solde du compte est tout de même valide, et c'est un problème très compliqué à debugger, puisqu'il survient très
|
||||
rarement, mais il faudrait tout de même arriver à le résoudre un jour.
|
||||
|
||||
|
||||
\chapter{Le SAS}
|
||||
\label{sec:le_sas}
|
||||
\par Développeur principal: Florent
|
||||
|
||||
\section{But}
|
||||
\label{sub:but}
|
||||
\par Fournir un système de galerie de photo:
|
||||
\begin{itemize}
|
||||
\item Upload en ligne via un formulaire pour tous les cotisants.
|
||||
\item Modération pour l'équipe du SAS.
|
||||
\item Système d'identification des membres pour retrouver rapidement ses photos.
|
||||
\item Affichage des photos dans les différents album et sur la page "photo" du profil d'un utilisateur.
|
||||
\end{itemize}
|
||||
|
||||
\section{Principaux problèmes}
|
||||
\label{sec:principaux_problemes}
|
||||
|
||||
\subsection{Gestion des fichiers}
|
||||
\label{sub:gestion_des_fichiers}
|
||||
\par L'envoie en grande quantité de photos nécéssite une gestion des fichiers solide, en même temps qu'un formulaire
|
||||
d'envoie efficace, capable d'envoyer plusieurs dizaines de photos en une seule action de l'utilisateur.
|
||||
|
||||
\par L'envoie est donc fait à l'aide de requêtes AJAX pour envoyer les photos une par une et éviter alors le timeout.
|
||||
|
||||
\par Concernant les fichiers une fois envoyé, ils sont en réalité traités par la classe \verb#SithFile# qui gère tous
|
||||
les fichiers du site. Cela ne fait qu'une seule classe à développer, de même qu'un seul système de fichier avec une
|
||||
seule arborescence, ce qui est beaucoup plus robuste.
|
||||
|
||||
\par La difficulté a aussi été de permettre le déplacement des fichiers, par couper-coller, tout en faisant de même dans
|
||||
le système de fichier réel, afin d'avoir une arborescence cohérente même en cas de perte de la base de données.
|
||||
|
||||
\subsection{Optimisation des pages}
|
||||
\label{sub:optimisation_des_pages}
|
||||
\par La génération d'un grand nombre de requêtes SQL est un des principaux problèmes de ralentissement d'un site. Le
|
||||
SAS, avec ses très nombreuses photos, qui requierent une validation des droits, a posé un gros problème à ce niveau.
|
||||
|
||||
\par Certaines pages ont pu mettre jusqu'à plus de 10 secondes à générer, ce qui est inconcevable pour une galerie de
|
||||
photos, mais ce temps à pu être réduit à moins de 3 secondes.
|
||||
|
||||
\par L'astuce à été d'utiliser des actions utilisateurs, comme l'upload de nouvelles photos, pour faire plus de
|
||||
traitement que nécessaire, afin de mettre en "cache" une grande partie des actions, comme par exemple la génération des
|
||||
miniatures des albums.
|
||||
|
||||
\par Une autre technique pour gagner du temps est de mettre en cache certaines requêtes en forcant les \emph{QuerySet}
|
||||
à s'évaluer dans une \emph{list} \textbf{Python} que l'on stocke afin d'obtenir sa longueur, au lieu de lancer d'abord
|
||||
un \emph{count}, puis une itération des résultats, qui en utilisant directement l'ORM, conduit à réaliser deux
|
||||
requêtes SQL.
|
||||
|
||||
\par Enfin, le passage à \textbf{HTTP/2} permettrait d'améliorer encore les performances côté utilisateur puisqu'il n'y
|
||||
aurait plus qu'un seul \emph{socket} d'ouvert pour transférer toutes les photos d'une page par exemple, sans avoir
|
||||
pour autant à toucher au code.
|
||||
|
||||
|
||||
\chapter{Les élections}
|
||||
\label{sec:les_elections}
|
||||
\par Développeur principal: Antoine
|
||||
|
||||
\section{But}
|
||||
\label{sub:but}
|
||||
\par Fournir un système d'élections:
|
||||
\begin{itemize}
|
||||
\item Gestion des différentes élections comprenants à chaque fois une liste de postes pour lesquels les gens
|
||||
candidatent, ainsi qu'une gestion des listes, pour pouvoir classifier et répartir les candidatures.
|
||||
\item Gestion d'une page de vote, permettant aux gens autorisés de pouvoir voter.
|
||||
\item Affichage des résultats une fois le vote terminé.
|
||||
\item Pas compatible avec la législation française: trop contraignant et pas utile, puisque validation officiel en
|
||||
AG.
|
||||
\end{itemize}
|
||||
|
||||
\section{Principaux problèmes}
|
||||
\label{sec:principaux_problemes}
|
||||
|
||||
\subsection{Automatisation d'un widget particulier pour les formulaires}
|
||||
\label{sub:automatisation_d_un_widget_particulier_pour_les_formulaires}
|
||||
\par La demande est venue du \textbf{BdF} qui a voulu autoriser pour certains poste un nombre de vote supérieur à 1.
|
||||
Cela signifie que l'on passe d'un choix simple, type \verb#radio# à un choix multiple, type \verb#checkbox#, tout cela
|
||||
étant paramètrable dans l'élection.
|
||||
|
||||
\par Ce genre de choix n'étant pas disponible dans \textbf{Django} de base, il a fallut développer le \emph{widget} à
|
||||
utiliser dans le formulaire qui permette cette configuration tout en validant bien les données reçues par rapport au
|
||||
modèle, et éviter ainsi de pouvoir "tricher" en envoyant des requêtes erronées.
|
||||
|
||||
\subsection{Revue du code d'un autre développeur}
|
||||
\label{sub:revue_du_code_d_un_autre_developpeur}
|
||||
\par \emph{Antoine} étant le principal développeur de cette application, un gros travail de revue de code a dû être
|
||||
effectuer afin de garantir une certaine cohérence avec le reste du projet.
|
||||
|
||||
\par C'est un travail très long et fastidieux, car il faut bien revérifier chaque ligne, sur chaque fichier, tout en
|
||||
faisant des commentaire lorsque quelque chose ne va pas. \textbf{Gitlab} a, à ce niveau, grandement facilité la tâche, à
|
||||
l'aide de ses outils de \emph{merge request} assez avancés.
|
||||
|
||||
\par En plus du code en lui-même, il a fallut porter une attention particulière aux migrations. Ces fichiers générés
|
||||
automatiquement par \textbf{Django} sont responsables du maintient d'une base de donnée cohérente malgré les évolutions
|
||||
des modèles. Même si le code parait donc valide, il est impératif de surveiller que la chaîne de dépendance des dites
|
||||
migrations ne soit pas cassée, au risque de problèmes potentiels au moment de la mise en production, ce qui entraînent à
|
||||
coup sûr un \emph{downtime}.
|
||||
|
||||
\chapter{Les stocks}
|
||||
\label{sub:les_stocks}
|
||||
\par Développeur principal: Guillaume
|
||||
\vskip 2em
|
||||
|
||||
\par Cette application s'occupe de la gestion des stocks des comptoirs de type « BAR ». Elle permet de suivre les
|
||||
quantités restantes afin de pouvoir déterminer de manière automatisée quels sont les produits qu'il faut acheter et en
|
||||
quelle quantité.
|
||||
|
||||
\section{Liste des modèles}
|
||||
\label{sec:liste_des_modeles}
|
||||
|
||||
\subsection{Stock}
|
||||
\par Un Stock possède un nom et est lié à la classe Counter. Ainsi, chaque Comptoir peut avoir son propre Stock.
|
||||
|
||||
\subsection{StockItem}
|
||||
\par Un StockItem possède un nom, une quantité unitaire, une quantité effective, une quantité minimale et est lié à la
|
||||
classe Stock ainsi qu'à la classe ProductType. De cette manière, chaque élément appartient à un Stock et il est
|
||||
catégorisé de la même manière que les Products (qui sont les objets utilisés pour la vente dans les comptoirs).
|
||||
|
||||
\subsection{ShoppingList}
|
||||
\par Une ShoppingList possède un nom, une date, un booléen (fait ou à faire), un commentaire et est liée à un Stock.
|
||||
Chaque ShoppingList est donc liée à un Stock ce qui permet d'avoir des listes de courses spécifique à chaque comptoir.
|
||||
|
||||
\subsection{ShoppingListItem}
|
||||
\par Un ShoppingListItem possède un nom, une quantité demandée, une quantité achetée et est lié à la classe StockItem, à
|
||||
la classe ShoppingList et à la classe ProductType. Cela permet de pouvoir faire plusieurs listes de courses différentes
|
||||
en même temps et d'en garder un historique.
|
||||
|
||||
\section{Fonctionnement}
|
||||
\label{sec:fonctionnement}
|
||||
|
||||
\par Au départ, si le comptoir de type « BAR » n'a pas de stock, la seule chose qu'il est possible de faire est d'en
|
||||
créer un. Ensuite, il va falloir créer les objets StockItem en indiquant pour chacun les quantités qu'il y a dans le
|
||||
stock.
|
||||
|
||||
\par De plus, la personne (généralement le Responsable du lieu de vie) qui aura la responsabilité d'informatiser les
|
||||
stocks devra aussi définir la quantité unitaire, effective et minimale.
|
||||
\par Par exemple, les Cheeseburger vendus aux différents comptoirs sont achetés par boite de 6, la quantité unitaire
|
||||
sera donc 6, la quantité effective correspondra au nombre de boites restantes dans le stock (c'est à dire dans la
|
||||
réserve du lieu de vie, une boite sortie du stock est considérée comme consommée) et enfin, la quantité minimale servira
|
||||
de valeur seuil.
|
||||
\par Une fois l'état de la réserve retranscrit dans le site, il reste encore gérer les stocks de manière quotidienne.
|
||||
Pour ce faire, l'application se décompose en 3 parties :
|
||||
\begin{itemize}
|
||||
\item Création automatique des listes de courses
|
||||
\item Approvisionnement du stock
|
||||
\item Prise d'éléments dans le stock
|
||||
\end{itemize}
|
||||
|
||||
\par Lorsque l'on accède à la partie qui s'occupe de la gestion des listes de courses, il y a un bouton permettant de
|
||||
créer une liste de courses en fonction de l'état des stocks à cet instant, puis un premier tableau contenant les listes
|
||||
de courses qu'il faut faire et enfin un second tableau servant d'historique des listes de courses déjà effectuées.
|
||||
\par Pour chaque liste de course ainsi créée, qu'elle soit « faite » ou « à faire », il est possible de cliquer sur son
|
||||
nom pour voir le détail de ce qu'elle comprend.
|
||||
|
||||
\subsection{Création automatique des listes de courses}
|
||||
\par En cliquant sur le bouton permettant de créer une nouvelle liste de courses, il faut remplir un formulaire. Les
|
||||
informations à donner dans ce formulaire sont le nom de la liste de course (par exemple, une liste spéciale pour
|
||||
Leclerc), ensuite, apparaissent tous les StockItem ayant une quantité effective inférieure au seuil fixé par leur
|
||||
quantité minimale. Il faut donc donner pour chacun de ces éléments une quantité à acheter. Enfin, un dernier champ de
|
||||
commentaire peut être compléter, il sert à demander l'achat d'éléments qui n'apparaissent pas dans le Stock, par
|
||||
exemple, des couteaux, fourchettes ou encore tasses...
|
||||
\par Lors de la validation de ce formulaire, la liste de courses est créée et est ajoutée au tableau contenant les
|
||||
listes de courses à faire.
|
||||
|
||||
\subsection{Approvisionnement du stock}
|
||||
\par Au retour des courses, il faut ranger les produits achetés dans la réserve. À ce moment-là, il faut aussi mettre à
|
||||
jour le stock. Une opération « Mettre à jour le stock » est disponible pour chaque liste de courses du tableau « À
|
||||
faire ».
|
||||
\par En effectuant cette action, il va falloir indiquer les quantités effectivement achetées. En effet, les quantités
|
||||
demandées ne sont pas forcément celles achetées, c'est donc les quantités effectives qu'il faut ajouter au stock. Une
|
||||
fois ce formulaire validé, la liste de couses passera de l'état « à faire » à l'état « faite ».
|
||||
|
||||
\subsection{Prise d'éléments dans le stock}
|
||||
\par La réserve étant accessible aux barmen afin qu'ils puissent réapprovisionner les réfrigérateurs à tout moment,
|
||||
l'interface permettant de prendre des éléments dans le stock a été ajoutée dans les onglets de l'interface des ventes
|
||||
(là où le barman inscrit le code du compte du client qui souhaite commander quelque chose). Ainsi, en revenant de la
|
||||
réserve, le barman doit indiquer le nombre de chaque produit qu'il a rapporté.
|
||||
\par Dans un souci de simplicité pour le gérant du lieu de vie, ce formulaire de prise des éléments dans le stock est
|
||||
aussi accessible depuis son interface de gestion.
|
||||
|
||||
|
||||
\section{Améliorations à apporter}
|
||||
\label{sec:amelioration_a_apporter}
|
||||
|
||||
\begin{itemize}
|
||||
\item Il n'est pas encore possible de modifier les quantités demandées pour un ou plusieurs des produits d'une liste
|
||||
de course. Il faudrait rendre cela possible car actuellement, il faut supprimer la liste de courses et la
|
||||
refaire en changeant les quantités souhaitées.
|
||||
\item Il faudrait améliorer la manière dont on ajoute les éléments non définis en tant que StockItem dans la liste
|
||||
de course. Un objet ShoppingListItem avec une référence « Null » vers la classe StockItem pourrait être créé à
|
||||
la place de compléter le champ de commentaires.
|
||||
\item Dans les améliorations sur le long terme, il faudrait que la décrémentation des quantités de chaque élément
|
||||
dans le stock soit automatique. En repensant une partie de l'architecture de l'application, on pourrait faire en
|
||||
sorte que chaque vente faite au comptoir face diminuer les quantités restantes (cela remplacerait le formulaire
|
||||
de « Prise d'éléments dans le stock », mais cela ne serait pas applicable à tous les produits mis en vente. Par
|
||||
exemple, pour les cacahuètes que nous vendons au bol et non par paquet)
|
||||
\item Avec le système de notifications qui a été mis en place sur le site, on pourrait faire en sorte que le ou les
|
||||
responsables des lieux de vie reçoivent une notification lorsque la liste de courses contient plus de 5 éléments
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\chapter{La laverie}
|
||||
\label{sec:la_laverie}
|
||||
\par Développeur principal: Florent
|
||||
|
||||
\section{But}
|
||||
\label{sub:but}
|
||||
\par Cette application doit fournir un système de gestion de laverie. Cela comprend:
|
||||
\begin{itemize}
|
||||
\item Un système de planning et de réservation de créneaux
|
||||
\item Un système de vente de jetons de laverie, lié aux comptoirs et au compte AE, permettant aux permanenciers de
|
||||
cliquer les jetons en même temps qu'ils vérifient l'état de la cotisation.
|
||||
\item Un système d'inventaire, pour gérer les différentes machines dans les différents lieux, et gérer également le
|
||||
retour des jetons après utilisation.
|
||||
\end{itemize}
|
||||
|
||||
\section{Principaux problèmes}
|
||||
\label{sec:principaux_problemes}
|
||||
|
||||
\subsection{Génération de plannings}
|
||||
\label{sub:generation_de_plannings}
|
||||
\par Il y a là beaucoup de cas à prendre en compte. Lorsque que quelqu'un veut réserver directement un "Lavage +
|
||||
Séchage", un simple "Lavage", ou un simple "Séchage", il faut toujours vérifier la disponibilité des créneaux en
|
||||
fonction du nombre de machine de chaque type présent dans la laverie en question \footnote{Belfort ou Sevenans, en
|
||||
l'occurrence}, et cela représente vite un grand nombre de combinaisons à vérifier.
|
||||
|
||||
\par De plus, la réservation doit rester ergonomique, et s'afficher dans un format le plus lisible possible pour un
|
||||
humain. Là dessus, un tableau est le plus approprié, avec chaque jour représenté par une colonne, et chaque créneau par
|
||||
une ligne.\\
|
||||
Mais cela ne représente malheureusement pas la temporalité, et la génération du tableau devient alors plutôt compliquée,
|
||||
et d'autant plus si l'on veut qu'il soit sémantiquement correct en HTML.
|
||||
|
||||
\subsection{Gestion des timezones}
|
||||
\label{sub:gestion_des_timezones}
|
||||
\par La gestion et le stockage des crénaux implique l'utilisation de champs de type \verb#DateTime#. \textbf{Django} les
|
||||
gère très bien, particulièrement au niveau des \emph{timezones}, où ce dernier n'hésite pas à lancer un warning lorsque
|
||||
l'objet \emph{date} passé ne contient pas d'information de fuseau horaire.
|
||||
|
||||
\par Mais avec notre décalage d'une heure par rapport au temps UTC, tous les horaires se retrouvent décalés, et gérer
|
||||
cela convenablement sans sortir d'avertissement a été plutôt compliqué. La solution a été de forcer un peu partout la
|
||||
\emph{timezone} à UTC, afin de ne pas créer de décalage, mais en conservant tout de même l'information de fuseau
|
||||
horaire, et sans tout casser lors du passage à l'heure d'hiver.
|
||||
|
||||
|
||||
\chapter{La communication}
|
||||
\label{sec:la_communication}
|
||||
\par Développeur principal: Florent
|
||||
|
||||
\section{But}
|
||||
\label{sub:but}
|
||||
\par Cette application a plusieurs but:
|
||||
\begin{itemize}
|
||||
\item Donner la possibilité au responsable communication d'éditer les différents textes, messages, et pages
|
||||
statiques du site.
|
||||
\item Fournir un système de news.
|
||||
\item Fournir un système de newsletter: le Weekmail.
|
||||
\end{itemize}
|
||||
|
||||
\section{Principaux problèmes}
|
||||
\label{sec:principaux_problemes}
|
||||
|
||||
\subsection{Envoie de mails}
|
||||
\label{sub:envoie_de_mails}
|
||||
\par Un outil de \emph{newsletter} nécessite l'envoie de mail. C'est là quelque chose de relativement compliqué à
|
||||
tester, d'autant plus lorsqu'il s'agit de mailing-list contenant l'intégralité des étudiants de l'UTBM.
|
||||
|
||||
\par \textbf{Django} fournit toutefois un outil très pratique: il contient plusieurs \emph{backend} d'emails, dont entre
|
||||
autre un \emph{SMTP}, et un \emph{console}. Le \emph{SMTP} est bien évidemment utilisé en production pour envoyer
|
||||
effectivement les mails, mais il est compliqué à utiliser en développement, car il suppose que le développeur a à sa
|
||||
disposition un serveur de ce type. On utilise alors le backend \emph{console}, qui affiche simplement dans le thread
|
||||
d'execution de \textbf{Django} une version texte de l'email envoyé, avec d'une part les entêtes, d'autre part le corps
|
||||
de message.
|
||||
|
||||
\par Mais autant pour tester l'envoie d'un mail unique à une adresse unique, cela fonctionne parfaitement bien, ce n'est
|
||||
toutefois pas suffisant pour tester un envoie massif à plusieurs mailings, avec en plus encore d'autres adresses en
|
||||
\emph{Bcc} pour les gens ne faisant pas partie des mailings "classiques", mais souhaitant quand même recevoir le
|
||||
\textbf{Weekmail}.
|
||||
|
||||
\subsection{Amélioration de l'outil de recherche}
|
||||
\label{sub:amelioration_de_l_outil_de_recherche}
|
||||
\par Pour la gestion de l'AE, il est nécessaire de pouvoir rechercher et trouver efficacement n'importe quel membre, en
|
||||
tapant au choix son nom, prénom, ou surnom, voire une combinaison de ces trois champs.
|
||||
|
||||
\par Mais une fonction de recherche aussi complexe est très difficile a mettre en place efficacement sans un traitement
|
||||
préalable, d'où la nécessité d'indexer les entrées à chercher. Un indexeur étant très complexe, mais également très
|
||||
courant, il n'a pas été difficile de trouver une application déjà existante fournissant ces fonctionnalités.
|
||||
|
||||
\par Le choix s'est porté sur \textbf{Haystack}, en l'utilisant avec l'indexeur \textbf{Whoosh}, plutôt efficace pour
|
||||
des bases raisonnables, et surtout écrit en pure \textbf{Python}, donc ne nécessitant pas d'installation compliquée en
|
||||
parallèle du site.
|
||||
|
||||
\par Le résultat est plutôt satisfaisant, mais il faudrait encore améliorer les résultats en utilisant les fonctions de
|
||||
\emph{boost} pour certains champs. De plus, une certaine lenteur se fait encore sentir avec certaines recherches trop
|
||||
communes ou générales.
|
||||
|
||||
|
||||
\chapter{Conclusions personnelles}
|
||||
\section{Florent}
|
||||
\label{sec:skia}
|
||||
\par Développer de nouvelles application m'a permis d'apréhender d'autres problématiques, comme la gestion des fichiers
|
||||
dans le SAS, ou bien des contraintes de concurrence et d'atomicité sur l'Eboutic.
|
||||
|
||||
\par Mais la plus grosse partie de mon travail ce semestre a surtout été de superviser une équipe de développement
|
||||
naissante, de relire les "Merge request", et de m'assurer de la cohérence du code des contributeurs avec le reste du
|
||||
projet.
|
||||
|
||||
\par J'ai églament pu approfondir mon utilisation de Gitlab à travers ses outils de gestion de projet, de revue de code,
|
||||
et de gestion des permissions sur les différentes branches.
|
||||
|
||||
\section{Guillaume}
|
||||
\label{sec:lo_j}
|
||||
\par Je suis très heureux d'avoir pu participer à ce projet de TO52 sur le développement de modules sur le site de
|
||||
l'Association des Étudiants. J'ai pu apprendre à travailler avec un nouvel environnement informatique tout en
|
||||
contribuant au développement d'outils pour l'association dont je suis Président, le Bureau des Festivités.
|
||||
|
||||
\subsection{Django}
|
||||
\par Ayant déjà travaillé avec le framework Spring et Java durant mon stage ST40, j'ai pu m'appuyer sur des notions
|
||||
générales afin d'apprendre et de comprendre le fonctionnement du Python et de Django que je ne connaissais pas du tout.
|
||||
|
||||
\par Mon apprentissage a été assez long au départ, car il y avait beaucoup d'informations à intégrer. Django est un
|
||||
framework très pratique qui permet d'effectuer de nombreuses tâches assez rébarbatives de manière automatique certes,
|
||||
mais encore faut-il comprendre ce qu'il se passe en arrière-plan. C'est cet apprentissage qui m'a pris le plus de temps.
|
||||
|
||||
\par Mon deuxième point de difficulté a été les formulaires. Là encore, Django est très pratique dès lors qu'il s'agit
|
||||
de faire un formulaire avec tous les champs d'un même Model. Cependant, il m'a fallu de l'aide et du temps pour
|
||||
comprendre comment faire pour ajouter d'autres champs en plus de ceux du Model au formulaire et pour comprendre comment
|
||||
récupérer les valeurs associées à chacun d'eux.
|
||||
|
||||
\subsection{Git}
|
||||
\par J'ai eu aussi à apprendre le fonctionnement de Git que j'avais déjà pu manipuler quelque peu mais il me manquait
|
||||
quand même beaucoup d'éléments.
|
||||
|
||||
\par Aujourd'hui, je pense pouvoir dire que j'ai progressé dans ce domaine mais il me reste encore bien des choses à
|
||||
apprendre pour être capable de l'utiliser de manière efficace.
|
||||
|
||||
\includepdf[pages={2}]{Couvertures.pdf}
|
||||
|
||||
\end{document}
|
||||
|
18
docs/archives/TO_Skia_LoJ/slides/Makefile
Normal file
18
docs/archives/TO_Skia_LoJ/slides/Makefile
Normal file
@ -0,0 +1,18 @@
|
||||
LATEX := pdflatex
|
||||
TARGET := slide.pdf
|
||||
|
||||
.PHONY: all clean distclean
|
||||
|
||||
all: $(TARGET) clean
|
||||
|
||||
%.pdf: %.tex
|
||||
echo "Building pdf"
|
||||
$(LATEX) --shell-escape $<
|
||||
rm -f $@
|
||||
$(LATEX) --shell-escape $<
|
||||
|
||||
clean:
|
||||
rm -f *.log *.nav *.snm *.aux *.out *.toc *.pyg
|
||||
|
||||
distclean: clean
|
||||
rm -f $(TARGET)
|
133
docs/archives/TO_Skia_LoJ/slides/beamercolorthememetropolis.sty
Normal file
133
docs/archives/TO_Skia_LoJ/slides/beamercolorthememetropolis.sty
Normal file
@ -0,0 +1,133 @@
|
||||
%%
|
||||
%% This is file `beamercolorthememetropolis.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% beamercolorthememetropolis.dtx (with options: `package')
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Copyright 2015 Matthias Vogelgesang and the LaTeX community. A full list of
|
||||
%% contributors can be found at
|
||||
%%
|
||||
%% https://github.com/matze/mtheme/graphs/contributors
|
||||
%%
|
||||
%% and the original template was based on the HSRM theme by Benjamin Weiss.
|
||||
%%
|
||||
%% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
|
||||
%% International License (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
%% ---------------------------------------------------------------------------
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{beamercolorthememetropolis}[2016/02/21 Metropolis color theme]
|
||||
\RequirePackage{pgfopts}
|
||||
\pgfkeys{
|
||||
/metropolis/color/block/.cd,
|
||||
.is choice,
|
||||
transparent/.code=\metropolis@block@transparent,
|
||||
fill/.code=\metropolis@block@fill,
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/color/background/.cd,
|
||||
.is choice,
|
||||
dark/.code=\metropolis@colors@dark,
|
||||
light/.code=\metropolis@colors@light,
|
||||
}
|
||||
\newcommand{\metropolis@color@setdefaults}{
|
||||
\pgfkeys{/metropolis/color/.cd,
|
||||
background=light,
|
||||
block=transparent,
|
||||
}
|
||||
}
|
||||
\definecolor{mDarkBrown}{HTML}{604c38}
|
||||
\definecolor{mDarkTeal}{HTML}{23373b}
|
||||
\definecolor{mLightBrown}{HTML}{EB811B}
|
||||
\definecolor{mLightGreen}{HTML}{14B03D}
|
||||
\newcommand{\metropolis@colors@dark}{
|
||||
\setbeamercolor{normal text}{%
|
||||
fg=black!2,
|
||||
bg=mDarkTeal
|
||||
}
|
||||
}
|
||||
\newcommand{\metropolis@colors@light}{
|
||||
\setbeamercolor{normal text}{%
|
||||
fg=mDarkTeal,
|
||||
bg=black!2
|
||||
}
|
||||
}
|
||||
\setbeamercolor{alerted text}{%
|
||||
fg=mLightBrown
|
||||
}
|
||||
\setbeamercolor{example text}{%
|
||||
fg=mLightGreen
|
||||
}
|
||||
\setbeamercolor{titlelike}{use=normal text, parent=normal text}
|
||||
\setbeamercolor{author}{use=normal text, parent=normal text}
|
||||
\setbeamercolor{date}{use=normal text, parent=normal text}
|
||||
\setbeamercolor{institute}{use=normal text, parent=normal text}
|
||||
\setbeamercolor{structure}{use=normal text, fg=normal text.fg}
|
||||
\setbeamercolor{palette primary}{%
|
||||
use=normal text,
|
||||
fg=normal text.bg,
|
||||
bg=normal text.fg
|
||||
}
|
||||
\setbeamercolor{frametitle}{%
|
||||
use=palette primary,
|
||||
parent=palette primary
|
||||
}
|
||||
\setbeamercolor{progress bar}{%
|
||||
use=alerted text,
|
||||
fg=alerted text.fg,
|
||||
bg=alerted text.fg!50!black!30
|
||||
}
|
||||
\setbeamercolor{title separator}{
|
||||
use=progress bar,
|
||||
parent=progress bar
|
||||
}
|
||||
\setbeamercolor{progress bar in head/foot}{%
|
||||
use=progress bar,
|
||||
parent=progress bar
|
||||
}
|
||||
\setbeamercolor{progress bar in section page}{
|
||||
use=progress bar,
|
||||
parent=progress bar
|
||||
}
|
||||
\newcommand{\metropolis@block@transparent}{
|
||||
\setbeamercolor{block title}{%
|
||||
use=normal text,
|
||||
fg=normal text.fg,
|
||||
bg=
|
||||
}
|
||||
\setbeamercolor{block body}{
|
||||
bg=
|
||||
}
|
||||
}
|
||||
\newcommand{\metropolis@block@fill}{
|
||||
\setbeamercolor{block title}{%
|
||||
use=normal text,
|
||||
fg=normal text.fg,
|
||||
bg=normal text.bg!80!fg
|
||||
}
|
||||
\setbeamercolor{block body}{
|
||||
use={block title, normal text},
|
||||
bg=block title.bg!50!normal text.bg
|
||||
}
|
||||
}
|
||||
\setbeamercolor{block title alerted}{%
|
||||
use={block title, alerted text},
|
||||
bg=block title.bg,
|
||||
fg=alerted text.fg
|
||||
}
|
||||
\setbeamercolor{block title example}{%
|
||||
use={block title, example text},
|
||||
bg=block title.bg,
|
||||
fg=example text.fg
|
||||
}
|
||||
\setbeamercolor{block body alerted}{use=block body, parent=block body}
|
||||
\setbeamercolor{block body example}{use=block body, parent=block body}
|
||||
\setbeamercolor{footnote}{fg=normal text.fg!90}
|
||||
\setbeamercolor{footnote mark}{fg=.}
|
||||
\metropolis@color@setdefaults
|
||||
\ProcessPgfPackageOptions{/metropolis/color}
|
||||
\mode<all>
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `beamercolorthememetropolis.sty'.
|
283
docs/archives/TO_Skia_LoJ/slides/beamerfontthememetropolis.sty
Normal file
283
docs/archives/TO_Skia_LoJ/slides/beamerfontthememetropolis.sty
Normal file
@ -0,0 +1,283 @@
|
||||
%%
|
||||
%% This is file `beamerfontthememetropolis.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% beamerfontthememetropolis.dtx (with options: `package')
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Copyright 2015 Matthias Vogelgesang and the LaTeX community. A full list of
|
||||
%% contributors can be found at
|
||||
%%
|
||||
%% https://github.com/matze/mtheme/graphs/contributors
|
||||
%%
|
||||
%% and the original template was based on the HSRM theme by Benjamin Weiss.
|
||||
%%
|
||||
%% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
|
||||
%% International License (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
%% ---------------------------------------------------------------------------
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{beamerfontthememetropolis}[2016/02/21 Metropolis font theme]
|
||||
\RequirePackage{etoolbox}
|
||||
\RequirePackage{ifxetex}
|
||||
\RequirePackage{ifluatex}
|
||||
\RequirePackage{pgfopts}
|
||||
\ifboolexpr{bool {xetex} or bool {luatex}}{
|
||||
\RequirePackage[no-math]{fontspec}
|
||||
\newcounter{fontsnotfound}
|
||||
\newcommand{\checkfont}[1]{%
|
||||
\suppressfontnotfounderror=1%
|
||||
\font\x = "#1" at 10pt
|
||||
\selectfont
|
||||
\ifx\x\nullfont%
|
||||
\stepcounter{fontsnotfound}%
|
||||
\fi%
|
||||
\suppressfontnotfounderror=0%
|
||||
}
|
||||
|
||||
\newcommand{\iffontsavailable}[3]{%
|
||||
\setcounter{fontsnotfound}{0}%
|
||||
\expandafter\forcsvlist\expandafter%
|
||||
\checkfont\expandafter{#1}%
|
||||
\ifnum\value{fontsnotfound}=0%
|
||||
#2%
|
||||
\else%
|
||||
#3%
|
||||
\fi%
|
||||
}
|
||||
\iffontsavailable{Fira Sans Light,%
|
||||
Fira Sans Light Italic,%
|
||||
Fira Sans,%
|
||||
Fira Sans Italic}%
|
||||
{%
|
||||
\setsansfont[ItalicFont={Fira Sans Light Italic},%
|
||||
BoldFont={Fira Sans},%
|
||||
BoldItalicFont={Fira Sans Italic}]%
|
||||
{Fira Sans Light}%
|
||||
}{%
|
||||
\iffontsavailable{Fira Sans Light OT,%
|
||||
Fira Sans Light Italic OT,%
|
||||
Fira Sans OT,%
|
||||
Fira Sans Italic OT}%
|
||||
{%
|
||||
\setsansfont[ItalicFont={Fira Sans Light Italic OT},%
|
||||
BoldFont={Fira Sans OT},%
|
||||
BoldItalicFont={Fira Sans Italic OT}]%
|
||||
{Fira Sans Light OT}%
|
||||
}{%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Could not find Fira Sans fonts%
|
||||
}
|
||||
}
|
||||
}
|
||||
\iffontsavailable{Fira Mono, Fira Mono Bold}{%
|
||||
\setmonofont[BoldFont={Fira Mono Medium}]{Fira Mono}%
|
||||
}{%
|
||||
\iffontsavailable{Fira Mono OT, Fira Mono Bold OT}{%
|
||||
\setmonofont[BoldFont={Fira Mono Medium OT}]{Fira Mono OT}%
|
||||
}{%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Could not find Fira Mono fonts%
|
||||
}
|
||||
}
|
||||
}
|
||||
\AtBeginEnvironment{tabular}{%
|
||||
\addfontfeature{Numbers={Monospaced}}%
|
||||
}
|
||||
}{%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
You need to compile with XeLaTeX or LuaLaTeX to use the Fira fonts%
|
||||
}
|
||||
}
|
||||
\setbeamerfont{title}{size=\Large,%
|
||||
series=\bfseries}
|
||||
\setbeamerfont{author}{size=\small}
|
||||
\setbeamerfont{date}{size=\small}
|
||||
\setbeamerfont{section title}{size=\Large,%
|
||||
series=\bfseries}
|
||||
\setbeamerfont{block title}{size=\normalsize,%
|
||||
series=\bfseries}
|
||||
\setbeamerfont{block title alerted}{size=\normalsize,%
|
||||
series=\bfseries}
|
||||
\setbeamerfont*{subtitle}{size=\large}
|
||||
\setbeamerfont{frametitle}{size=\large,%
|
||||
series=\bfseries}
|
||||
\setbeamerfont{caption}{size=\small}
|
||||
\setbeamerfont{caption name}{series=\bfseries}
|
||||
\setbeamerfont{description item}{series=\bfseries}
|
||||
\setbeamerfont{page number in head/foot}{size=\scriptsize}
|
||||
\setbeamerfont{bibliography entry author}{size=\normalsize,%
|
||||
series=\normalfont}
|
||||
\setbeamerfont{bibliography entry title}{size=\normalsize,%
|
||||
series=\bfseries}
|
||||
\setbeamerfont{bibliography entry location}{size=\normalsize,%
|
||||
series=\normalfont}
|
||||
\setbeamerfont{bibliography entry note}{size=\small,%
|
||||
series=\normalfont}
|
||||
\setbeamerfont{standout}{size=\Large,%
|
||||
series=\bfseries}
|
||||
\pgfkeys{
|
||||
/metropolis/font/titleformat title/.cd,
|
||||
.is choice,
|
||||
regular/.code={%
|
||||
\let\metropolis@titleformat\@empty%
|
||||
\setbeamerfont{title}{shape=\normalfont}%
|
||||
},
|
||||
smallcaps/.code={%
|
||||
\let\metropolis@titleformat\@empty%
|
||||
\setbeamerfont{title}{shape=\scshape}%
|
||||
},
|
||||
allsmallcaps/.code={%
|
||||
\let\metropolis@titleformat\lowercase%
|
||||
\setbeamerfont{title}{shape=\scshape}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat title=allsmallcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
allcaps/.code={%
|
||||
\let\metropolis@titleformat\uppercase%
|
||||
\setbeamerfont{title}{shape=\normalfont}
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat title=allcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/font/titleformat subtitle/.cd,
|
||||
.is choice,
|
||||
regular/.code={%
|
||||
\let\metropolis@subtitleformat\@empty%
|
||||
\setbeamerfont{subtitle}{shape=\normalfont}%
|
||||
},
|
||||
smallcaps/.code={%
|
||||
\let\metropolis@subtitleformat\@empty%
|
||||
\setbeamerfont{subtitle}{shape=\scshape}%
|
||||
},
|
||||
allsmallcaps/.code={%
|
||||
\let\metropolis@subtitleformat\lowercase%
|
||||
\setbeamerfont{subtitle}{shape=\scshape}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat subtitle=allsmallcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
allcaps/.code={%
|
||||
\let\metropolis@subtitleformat\uppercase%
|
||||
\setbeamerfont{subtitle}{shape=\normalfont}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat subtitle=allcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/font/titleformat section/.cd,
|
||||
.is choice,
|
||||
regular/.code={%
|
||||
\let\metropolis@sectiontitleformat\@empty%
|
||||
\setbeamerfont{section title}{shape=\normalfont}%
|
||||
},
|
||||
smallcaps/.code={%
|
||||
\let\metropolis@sectiontitleformat\@empty%
|
||||
\setbeamerfont{section title}{shape=\scshape}%
|
||||
},
|
||||
allsmallcaps/.code={%
|
||||
\let\metropolis@sectiontitleformat\MakeLowercase%
|
||||
\setbeamerfont{section title}{shape=\scshape}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat section=allsmallcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
allcaps/.code={%
|
||||
\let\metropolis@sectiontitleformat\MakeUppercase%
|
||||
\setbeamerfont{section title}{shape=\normalfont}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat section=allcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/font/titleformat frame/.cd,
|
||||
.is choice,
|
||||
regular/.code={%
|
||||
\let\metropolis@frametitleformat\@empty%
|
||||
\setbeamerfont{frametitle}{shape=\normalfont}%
|
||||
},
|
||||
smallcaps/.code={%
|
||||
\let\metropolis@frametitleformat\@empty%
|
||||
\setbeamerfont{frametitle}{shape=\scshape}%
|
||||
},
|
||||
allsmallcaps/.code={%
|
||||
\let\metropolis@frametitleformat\MakeLowercase%
|
||||
\setbeamerfont{frametitle}{shape=\scshape}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat frame=allsmallcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
allcaps/.code={%
|
||||
\let\metropolis@frametitleformat\MakeUppercase%
|
||||
\setbeamerfont{frametitle}{shape=\normalfont}
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat frame=allcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/font/.cd,
|
||||
titleformattitle/.code=\pgfkeysalso{titleformat title=#1},
|
||||
titleformatsubtitle/.code=\pgfkeysalso{titleformat subtitle=#1},
|
||||
titleformatsection/.code=\pgfkeysalso{titleformat section=#1},
|
||||
titleformatframe/.code=\pgfkeysalso{titleformat frame=#1},
|
||||
}
|
||||
\newcommand{\metropolis@font@setdefaults}{
|
||||
\pgfkeys{/metropolis/font/.cd,
|
||||
titleformat title=regular,
|
||||
titleformat subtitle=regular,
|
||||
titleformat section=regular,
|
||||
titleformat frame=regular,
|
||||
}
|
||||
}
|
||||
\def\metropolis@titleformat#1{#1}
|
||||
\def\metropolis@subtitleformat#1{#1}
|
||||
\def\metropolis@sectiontitleformat#1{#1}
|
||||
\def\metropolis@frametitleformat#1{#1}
|
||||
\patchcmd{\beamer@title}%
|
||||
{\def\inserttitle{#2}}%
|
||||
{\def\inserttitle{\metropolis@titleformat{#2}}}%
|
||||
{}%
|
||||
{\PackageError{beamerfontthememetropolis}{Patching title failed}}
|
||||
\patchcmd{\beamer@subtitle}%
|
||||
{\def\insertsubtitle{#2}}%
|
||||
{\def\insertsubtitle{\metropolis@subtitleformat{#2}}}%
|
||||
{}%
|
||||
{\PackageError{beamerfontthememetropolis}{Patching subtitle failed}}
|
||||
\patchcmd{\sectionentry}
|
||||
{\def\insertsectionhead{#2}}
|
||||
{\def\insertsectionhead{\metropolis@sectiontitleformat{#2}}}
|
||||
{}
|
||||
{\PackageError{beamerfontthememetropolis}{Patching section title failed}}
|
||||
\patchcmd{\beamer@section}
|
||||
{\def\insertsectionhead{\hyperlink{Navigation\the\c@page}{#1}}}
|
||||
{\def\insertsectionhead{\hyperlink{Navigation\the\c@page}{%
|
||||
\metropolis@sectiontitleformat{#1}}}}
|
||||
{}
|
||||
{\PackageError{beamerfontthememetropolis}{Patching section title failed}}
|
||||
\patchcmd{\beamer@@frametitle}
|
||||
{\beamer@ifempty{#2}{}{%
|
||||
\gdef\insertframetitle{{#2\ifnum\beamer@autobreakcount>0\relax{}\space%
|
||||
\usebeamertemplate*{frametitle continuation}\fi}}%
|
||||
\gdef\beamer@frametitle{#2}%
|
||||
\gdef\beamer@shortframetitle{#1}%
|
||||
}}
|
||||
{\beamer@ifempty{#2}{}{%
|
||||
\gdef\insertframetitle{{\metropolis@frametitleformat{#2}\ifnum%
|
||||
\beamer@autobreakcount>0\relax{}\space%
|
||||
\usebeamertemplate*{frametitle continuation}\fi}}%
|
||||
\gdef\beamer@frametitle{#2}%
|
||||
\gdef\beamer@shortframetitle{#1}%
|
||||
}}
|
||||
{}
|
||||
{\PackageError{beamerfontthememetropolis}{Patching frame title failed}}
|
||||
\metropolis@font@setdefaults
|
||||
\ProcessPgfPackageOptions{/metropolis/font}
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `beamerfontthememetropolis.sty'.
|
281
docs/archives/TO_Skia_LoJ/slides/beamerinnerthememetropolis.sty
Normal file
281
docs/archives/TO_Skia_LoJ/slides/beamerinnerthememetropolis.sty
Normal file
@ -0,0 +1,281 @@
|
||||
%%
|
||||
%% This is file `beamerinnerthememetropolis.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% beamerinnerthememetropolis.dtx (with options: `package')
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Copyright 2015 Matthias Vogelgesang and the LaTeX community. A full list of
|
||||
%% contributors can be found at
|
||||
%%
|
||||
%% https://github.com/matze/mtheme/graphs/contributors
|
||||
%%
|
||||
%% and the original template was based on the HSRM theme by Benjamin Weiss.
|
||||
%%
|
||||
%% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
|
||||
%% International License (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
%% ---------------------------------------------------------------------------
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{beamerinnerthememetropolis}[2016/02/21 Metropolis inner theme]
|
||||
\RequirePackage{etoolbox}
|
||||
\RequirePackage{keyval}
|
||||
\RequirePackage{calc}
|
||||
\RequirePackage{pgfopts}
|
||||
\RequirePackage{tikz}
|
||||
\pgfkeys{
|
||||
/metropolis/inner/sectionpage/.cd,
|
||||
.is choice,
|
||||
none/.code=\metropolis@disablesectionpage,
|
||||
simple/.code={\metropolis@enablesectionpage
|
||||
\setbeamertemplate{section page}[simple]},
|
||||
progressbar/.code={\metropolis@enablesectionpage
|
||||
\setbeamertemplate{section page}[progressbar]},
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/inner/subsectionpage/.cd,
|
||||
.is choice,
|
||||
none/.code=\metropolis@disablesubsectionpage,
|
||||
simple/.code={\metropolis@enablesubsectionpage
|
||||
\setbeamertemplate{section page}[simple]},
|
||||
progressbar/.code={\metropolis@enablesubsectionpage
|
||||
\setbeamertemplate{section page}[progressbar]},
|
||||
}
|
||||
\newcommand{\metropolis@inner@setdefaults}{
|
||||
\pgfkeys{/metropolis/inner/.cd,
|
||||
sectionpage=progressbar,
|
||||
subsectionpage=none
|
||||
}
|
||||
}
|
||||
\setbeamertemplate{title page}{
|
||||
\begin{minipage}[b][\paperheight]{\textwidth}
|
||||
\ifx\inserttitlegraphic\@empty\else\usebeamertemplate*{title graphic}\fi
|
||||
\vfill%
|
||||
\ifx\inserttitle\@empty\else\usebeamertemplate*{title}\fi
|
||||
\ifx\insertsubtitle\@empty\else\usebeamertemplate*{subtitle}\fi
|
||||
\usebeamertemplate*{title separator}
|
||||
\ifx\beamer@shortauthor\@empty\else\usebeamertemplate*{author}\fi
|
||||
\ifx\insertdate\@empty\else\usebeamertemplate*{date}\fi
|
||||
\ifx\insertinstitute\@empty\else\usebeamertemplate*{institute}\fi
|
||||
\vfill
|
||||
\vspace*{1mm}
|
||||
\end{minipage}
|
||||
}
|
||||
\def\maketitle{%
|
||||
\ifbeamer@inframe
|
||||
\titlepage
|
||||
\else
|
||||
\frame[plain,noframenumbering]{\titlepage}
|
||||
\fi
|
||||
}
|
||||
\def\titlepage{%
|
||||
\usebeamertemplate{title page}
|
||||
}
|
||||
\setbeamertemplate{title graphic}{
|
||||
\vbox to 0pt {
|
||||
\vspace*{2em}
|
||||
\inserttitlegraphic%
|
||||
}%
|
||||
\nointerlineskip%
|
||||
}
|
||||
\setbeamertemplate{title}{
|
||||
\raggedright%
|
||||
\linespread{1.0}%
|
||||
\inserttitle%
|
||||
\par%
|
||||
\vspace*{0.5em}
|
||||
}
|
||||
\setbeamertemplate{subtitle}{
|
||||
\insertsubtitle%
|
||||
\par%
|
||||
\vspace*{0.5em}
|
||||
}
|
||||
\setbeamertemplate{title separator}{
|
||||
\begin{tikzpicture}
|
||||
\draw[fg, fill=fg] (0,0) rectangle (\textwidth, 0.4pt);
|
||||
\end{tikzpicture}%
|
||||
\par%
|
||||
}
|
||||
\setbeamertemplate{author}{
|
||||
\vspace*{2em}
|
||||
\insertauthor%
|
||||
\par%
|
||||
\vspace*{0.25em}
|
||||
}
|
||||
\setbeamertemplate{date}{
|
||||
\insertdate%
|
||||
\par%
|
||||
}
|
||||
\setbeamertemplate{institute}{
|
||||
\vspace*{3mm}
|
||||
\insertinstitute%
|
||||
\par%
|
||||
}
|
||||
\defbeamertemplate{section page}{simple}{
|
||||
\begin{center}
|
||||
\usebeamercolor[fg]{section title}
|
||||
\usebeamerfont{section title}
|
||||
\insertsectionhead\par
|
||||
\ifx\insertsubsection\@empty\else
|
||||
\usebeamercolor[fg]{subsection title}
|
||||
\usebeamerfont{subsection title}
|
||||
\insertsubsection
|
||||
\fi
|
||||
\end{center}
|
||||
}
|
||||
\defbeamertemplate{section page}{progressbar}{
|
||||
\centering
|
||||
\begin{minipage}{22em}
|
||||
\raggedright
|
||||
\usebeamercolor[fg]{section title}
|
||||
\usebeamerfont{section title}
|
||||
\insertsectionhead\\[-1ex]
|
||||
\usebeamertemplate*{progress bar in section page}
|
||||
\par
|
||||
\ifx\insertsubsection\@empty\else%
|
||||
\usebeamercolor[fg]{subsection title}%
|
||||
\usebeamerfont{subsection title}%
|
||||
\insertsubsection
|
||||
\fi
|
||||
\end{minipage}
|
||||
\par
|
||||
\vspace{\baselineskip}
|
||||
}
|
||||
\newcommand{\metropolis@disablesectionpage}{
|
||||
\AtBeginSection{
|
||||
% intentionally empty
|
||||
}
|
||||
}
|
||||
\newcommand{\metropolis@enablesectionpage}{
|
||||
\AtBeginSection{
|
||||
\ifbeamer@inframe
|
||||
\sectionpage
|
||||
\else
|
||||
\frame[plain,c,noframenumbering]{\sectionpage}
|
||||
\fi
|
||||
}
|
||||
}
|
||||
\setbeamertemplate{subsection page}{%
|
||||
\usebeamertemplate*{section page}
|
||||
}
|
||||
\newcommand{\metropolis@disablesubsectionpage}{
|
||||
\AtBeginSubsection{
|
||||
% intentionally empty
|
||||
}
|
||||
}
|
||||
\newcommand{\metropolis@enablesubsectionpage}{
|
||||
\AtBeginSubsection{
|
||||
\ifbeamer@inframe
|
||||
\subsectionpage
|
||||
\else
|
||||
\frame[plain,c,noframenumbering]{\subsectionpage}
|
||||
\fi
|
||||
}
|
||||
}
|
||||
\newlength{\metropolis@progressonsectionpage}
|
||||
\setbeamertemplate{progress bar in section page}{
|
||||
\setlength{\metropolis@progressonsectionpage}{%
|
||||
\textwidth * \ratio{\insertframenumber pt}{\inserttotalframenumber pt}%
|
||||
}%
|
||||
\begin{tikzpicture}
|
||||
\draw[bg, fill=bg] (0,0) rectangle (\textwidth, 0.4pt);
|
||||
\draw[fg, fill=fg] (0,0) rectangle (\metropolis@progressonsectionpage, 0.4pt);
|
||||
\end{tikzpicture}%
|
||||
}
|
||||
\def\inserttotalframenumber{100}
|
||||
\newlength{\metropolis@blocksep}
|
||||
\newlength{\metropolis@blockadjust}
|
||||
\setlength{\metropolis@blocksep}{0.75ex}
|
||||
\setlength{\metropolis@blockadjust}{0.25ex}
|
||||
\providecommand{\metropolis@strut}{%
|
||||
\vphantom{ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz()}%
|
||||
}
|
||||
\newcommand{\metropolis@block}[1]{
|
||||
\par\vskip\medskipamount%
|
||||
\setlength{\parskip}{0pt}
|
||||
\ifbeamercolorempty[bg]{block title#1}{%
|
||||
\begin{beamercolorbox}[rightskip=0pt plus 4em]{block title#1}}{%
|
||||
\ifbeamercolorempty[bg]{block title}{%
|
||||
\begin{beamercolorbox}[rightskip=0pt plus 4em]{block title#1}%
|
||||
}%
|
||||
{%
|
||||
\begin{beamercolorbox}[
|
||||
sep=\dimexpr\metropolis@blocksep-\metropolis@blockadjust\relax,
|
||||
leftskip=\metropolis@blockadjust,
|
||||
rightskip=\dimexpr\metropolis@blockadjust plus 4em\relax
|
||||
]{block title#1}%
|
||||
}}%
|
||||
\usebeamerfont*{block title#1}%
|
||||
\metropolis@strut%
|
||||
\insertblocktitle%
|
||||
\metropolis@strut%
|
||||
\end{beamercolorbox}%
|
||||
\nointerlineskip%
|
||||
\ifbeamercolorempty[bg]{block body#1}{%
|
||||
\begin{beamercolorbox}[vmode]{block body#1}}{
|
||||
\ifbeamercolorempty[bg]{block body}{%
|
||||
\begin{beamercolorbox}[vmode]{block body#1}%
|
||||
}{%
|
||||
\begin{beamercolorbox}[sep=\metropolis@blocksep, vmode]{block body#1}%
|
||||
\vspace{-\metropolis@parskip}
|
||||
}}%
|
||||
\usebeamerfont{block body#1}%
|
||||
\setlength{\parskip}{\metropolis@parskip}%
|
||||
}
|
||||
\setbeamertemplate{block begin}{\metropolis@block{}}
|
||||
\setbeamertemplate{block alerted begin}{\metropolis@block{ alerted}}
|
||||
\setbeamertemplate{block example begin}{\metropolis@block{ example}}
|
||||
\setbeamertemplate{block end}{\end{beamercolorbox}\vspace*{0.2ex}}
|
||||
\setbeamertemplate{block alerted end}{\end{beamercolorbox}\vspace*{0.2ex}}
|
||||
\setbeamertemplate{block example end}{\end{beamercolorbox}\vspace*{0.2ex}}
|
||||
\setbeamertemplate{itemize items}{\textbullet}
|
||||
\setbeamertemplate{caption label separator}{: }
|
||||
\setbeamertemplate{caption}[numbered]
|
||||
\setbeamertemplate{footnote}{%
|
||||
\parindent 0em\noindent%
|
||||
\raggedright
|
||||
\usebeamercolor{footnote}\hbox to 0.8em{\hfil\insertfootnotemark}\insertfootnotetext\par%
|
||||
}
|
||||
\newlength{\metropolis@parskip}
|
||||
\setlength{\metropolis@parskip}{0.5em}
|
||||
\setlength{\parskip}{\metropolis@parskip}
|
||||
\linespread{1.15}
|
||||
\define@key{beamerframe}{c}[true]{% centered
|
||||
\beamer@frametopskip=0pt plus 1fill\relax%
|
||||
\beamer@framebottomskip=0pt plus 1fill\relax%
|
||||
\beamer@frametopskipautobreak=0pt plus .4\paperheight\relax%
|
||||
\beamer@framebottomskipautobreak=0pt plus .6\paperheight\relax%
|
||||
\def\beamer@initfirstlineunskip{}%
|
||||
}
|
||||
\providebool{metropolis@standout}
|
||||
\define@key{beamerframe}{standout}[true]{%
|
||||
\booltrue{metropolis@standout}
|
||||
\begingroup
|
||||
\setkeys{beamerframe}{c}
|
||||
\setkeys{beamerframe}{noframenumbering}
|
||||
\ifbeamercolorempty[bg]{palette primary}{
|
||||
\setbeamercolor{background canvas}{
|
||||
use=palette primary,
|
||||
bg=-palette primary.fg
|
||||
}
|
||||
}{
|
||||
\setbeamercolor{background canvas}{
|
||||
use=palette primary,
|
||||
bg=palette primary.bg
|
||||
}
|
||||
}
|
||||
\centering
|
||||
\usebeamercolor[fg]{palette primary}
|
||||
\usebeamerfont{standout}
|
||||
}
|
||||
\apptocmd{\beamer@reseteecodes}{%
|
||||
\ifbool{metropolis@standout}{
|
||||
\endgroup
|
||||
\boolfalse{metropolis@standout}
|
||||
}{}
|
||||
}{}{}
|
||||
\metropolis@inner@setdefaults
|
||||
\ProcessPgfPackageOptions{/metropolis/inner}
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `beamerinnerthememetropolis.sty'.
|
126
docs/archives/TO_Skia_LoJ/slides/beamerouterthememetropolis.sty
Normal file
126
docs/archives/TO_Skia_LoJ/slides/beamerouterthememetropolis.sty
Normal file
@ -0,0 +1,126 @@
|
||||
%%
|
||||
%% This is file `beamerouterthememetropolis.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% beamerouterthememetropolis.dtx (with options: `package')
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Copyright 2015 Matthias Vogelgesang and the LaTeX community. A full list of
|
||||
%% contributors can be found at
|
||||
%%
|
||||
%% https://github.com/matze/mtheme/graphs/contributors
|
||||
%%
|
||||
%% and the original template was based on the HSRM theme by Benjamin Weiss.
|
||||
%%
|
||||
%% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
|
||||
%% International License (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
%% ---------------------------------------------------------------------------
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{beamerouterthememetropolis}[2016/02/21 Metropolis outer theme]
|
||||
\RequirePackage{etoolbox}
|
||||
\RequirePackage{calc}
|
||||
\RequirePackage{pgfopts}
|
||||
\pgfkeys{
|
||||
/metropolis/outer/numbering/.cd,
|
||||
.is choice,
|
||||
none/.code=\setbeamertemplate{frame numbering}[none],
|
||||
counter/.code=\setbeamertemplate{frame numbering}[counter],
|
||||
fraction/.code=\setbeamertemplate{frame numbering}[fraction],
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/outer/progressbar/.cd,
|
||||
.is choice,
|
||||
none/.code={%
|
||||
\setbeamertemplate{headline}[plain]
|
||||
\setbeamertemplate{frametitle}[plain]
|
||||
\setbeamertemplate{footline}[plain]
|
||||
},
|
||||
head/.code={\pgfkeys{/metropolis/outer/progressbar=none}
|
||||
\addtobeamertemplate{headline}{}{%
|
||||
\usebeamertemplate*{progress bar in head/foot}
|
||||
}
|
||||
},
|
||||
frametitle/.code={\pgfkeys{/metropolis/outer/progressbar=none}
|
||||
\addtobeamertemplate{frametitle}{}{%
|
||||
\usebeamertemplate*{progress bar in head/foot}
|
||||
}
|
||||
},
|
||||
foot/.code={\pgfkeys{/metropolis/outer/progressbar=none}
|
||||
\addtobeamertemplate{footline}{}{%
|
||||
\usebeamertemplate*{progress bar in head/foot}%
|
||||
}
|
||||
},
|
||||
}
|
||||
\newcommand{\metropolis@outer@setdefaults}{
|
||||
\pgfkeys{/metropolis/outer/.cd,
|
||||
numbering=counter,
|
||||
progressbar=none,
|
||||
}
|
||||
}
|
||||
\setbeamertemplate{navigation symbols}{}
|
||||
\defbeamertemplate{frame footer}{none}{}
|
||||
\defbeamertemplate{frame footer}{custom}[1]{ #1 }
|
||||
\defbeamertemplate{frame numbering}{none}{}
|
||||
\defbeamertemplate{frame numbering}{counter}{\insertframenumber}
|
||||
\defbeamertemplate{frame numbering}{fraction}{
|
||||
\insertframenumber/\inserttotalframenumber
|
||||
}
|
||||
\defbeamertemplate{headline}{plain}{}
|
||||
\defbeamertemplate{footline}{plain}{%
|
||||
\begin{beamercolorbox}[wd=\textwidth, sep=3ex]{footline}%
|
||||
\usebeamerfont{page number in head/foot}%
|
||||
\usebeamertemplate*{frame footer}
|
||||
\hfill%
|
||||
\usebeamertemplate*{frame numbering}
|
||||
\end{beamercolorbox}%
|
||||
}
|
||||
\newlength{\metropolis@frametitle@padding}
|
||||
\setlength{\metropolis@frametitle@padding}{2.2ex}
|
||||
\newcommand{\metropolis@frametitlestrut@start}{
|
||||
\rule{0pt}{\metropolis@frametitle@padding +%
|
||||
\totalheightof{%
|
||||
\ifcsdef{metropolis@frametitleformat}{\metropolis@frametitleformat X}{X}%
|
||||
}%
|
||||
}%
|
||||
}
|
||||
\newcommand{\metropolis@frametitlestrut@end}{
|
||||
\rule[-\metropolis@frametitle@padding]{0pt}{\metropolis@frametitle@padding}
|
||||
}
|
||||
\defbeamertemplate{frametitle}{plain}{%
|
||||
\nointerlineskip%
|
||||
\begin{beamercolorbox}[%
|
||||
wd=\paperwidth,%
|
||||
sep=0pt,%
|
||||
leftskip=\metropolis@frametitle@padding,%
|
||||
rightskip=\metropolis@frametitle@padding,%
|
||||
]{frametitle}%
|
||||
\metropolis@frametitlestrut@start\insertframetitle\metropolis@frametitlestrut@end%
|
||||
\end{beamercolorbox}%
|
||||
}
|
||||
\newlength{\metropolis@progressinheadfoot}
|
||||
\setbeamertemplate{progress bar in head/foot}{
|
||||
\nointerlineskip
|
||||
\setlength{\metropolis@progressinheadfoot}{%
|
||||
\paperwidth * \ratio{\insertframenumber pt}{\inserttotalframenumber pt}%
|
||||
}%
|
||||
\begin{beamercolorbox}[wd=\paperwidth]{progress bar in head/foot}
|
||||
\begin{tikzpicture}
|
||||
\draw[bg, fill=bg] (0,0) rectangle (\paperwidth, 0.4pt);
|
||||
\draw[fg, fill=fg] (0,0) rectangle (\metropolis@progressinheadfoot, 0.4pt);
|
||||
\end{tikzpicture}%
|
||||
\end{beamercolorbox}
|
||||
}
|
||||
\AtBeginDocument{%
|
||||
\apptocmd{\appendix}{%
|
||||
\pgfkeys{%
|
||||
/metropolis/outer/.cd,
|
||||
numbering=none,
|
||||
progressbar=none}
|
||||
}{}{}
|
||||
}
|
||||
\metropolis@outer@setdefaults
|
||||
\ProcessPgfPackageOptions{/metropolis/outer}
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `beamerouterthememetropolis.sty'.
|
105
docs/archives/TO_Skia_LoJ/slides/beamerthememetropolis.sty
Normal file
105
docs/archives/TO_Skia_LoJ/slides/beamerthememetropolis.sty
Normal file
@ -0,0 +1,105 @@
|
||||
%%
|
||||
%% This is file `beamerthememetropolis.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% beamerthememetropolis.dtx (with options: `package')
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Copyright 2015 Matthias Vogelgesang and the LaTeX community. A full list of
|
||||
%% contributors can be found at
|
||||
%%
|
||||
%% https://github.com/matze/mtheme/graphs/contributors
|
||||
%%
|
||||
%% and the original template was based on the HSRM theme by Benjamin Weiss.
|
||||
%%
|
||||
%% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
|
||||
%% International License (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
%% ---------------------------------------------------------------------------
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{beamerthememetropolis}
|
||||
[2016/02/21 v1.1 Metropolis Beamer theme]
|
||||
\RequirePackage{etoolbox}
|
||||
\RequirePackage{pgfopts}
|
||||
\pgfkeys{/metropolis/.cd,
|
||||
.search also={
|
||||
/metropolis/inner,
|
||||
/metropolis/outer,
|
||||
/metropolis/color,
|
||||
/metropolis/font,
|
||||
}
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/titleformat plain/.cd,
|
||||
.is choice,
|
||||
regular/.code={%
|
||||
\let\metropolis@plaintitleformat\@empty%
|
||||
\setbeamerfont{standout}{shape=\normalfont}%
|
||||
},
|
||||
smallcaps/.code={%
|
||||
\let\metropolis@plaintitleformat\@empty%
|
||||
\setbeamerfont{standout}{shape=\scshape}%
|
||||
},
|
||||
allsmallcaps/.code={%
|
||||
\let\metropolis@plaintitleformat\MakeLowercase%
|
||||
\setbeamerfont{standout}{shape=\scshape}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat plain=allsmallcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
allcaps/.code={%
|
||||
\let\metropolis@plaintitleformat\MakeUppercase%
|
||||
\setbeamerfont{standout}{shape=\normalfont}%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
Be aware that titleformat plain=allcaps can lead to problems%
|
||||
}
|
||||
},
|
||||
}
|
||||
\pgfkeys{
|
||||
/metropolis/titleformat/.code=\pgfkeysalso{
|
||||
font/titleformat title=#1,
|
||||
font/titleformat subtitle=#1,
|
||||
font/titleformat section=#1,
|
||||
font/titleformat frame=#1,
|
||||
titleformat plain=#1,
|
||||
}
|
||||
}
|
||||
\pgfkeys{/metropolis/.cd,
|
||||
usetitleprogressbar/.code=\pgfkeysalso{outer/progressbar=frametitle},
|
||||
noslidenumbers/.code=\pgfkeysalso{outer/numbering=none},
|
||||
usetotalslideindicator/.code=\pgfkeysalso{outer/numbering=fraction},
|
||||
nosectionslide/.code=\pgfkeysalso{inner/sectionpage=none},
|
||||
darkcolors/.code=\pgfkeysalso{color/background=dark},
|
||||
blockbg/.code=\pgfkeysalso{color/block=fill, inner/block=fill},
|
||||
}
|
||||
\newcommand{\metropolis@setdefaults}{
|
||||
\pgfkeys{/metropolis/.cd,
|
||||
titleformat plain=regular,
|
||||
}
|
||||
}
|
||||
\useinnertheme{metropolis}
|
||||
\useoutertheme{metropolis}
|
||||
\usecolortheme{metropolis}
|
||||
\usefonttheme{metropolis}
|
||||
\AtEndPreamble{%
|
||||
\@ifpackageloaded{pgfplots}{%
|
||||
\RequirePackage{pgfplotsthemetol}
|
||||
}{}
|
||||
}
|
||||
\newcommand{\metroset}[1]{\pgfkeys{/metropolis/.cd,#1}}
|
||||
\def\metropolis@plaintitleformat#1{#1}
|
||||
\newcommand{\plain}[2][]{%
|
||||
\PackageWarning{beamerthememetropolis}{%
|
||||
The syntax `\plain' may be deprecated in a future version of Metropolis.
|
||||
Please use a frame with [standout] instead.
|
||||
}
|
||||
\begin{frame}[standout]{#1}
|
||||
\metropolis@plaintitleformat{#2}
|
||||
\end{frame}
|
||||
}
|
||||
\newcommand{\mreducelistspacing}{\vspace{-\topsep}}
|
||||
\metropolis@setdefaults
|
||||
\ProcessPgfOptions{/metropolis}
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `beamerthememetropolis.sty'.
|
123
docs/archives/TO_Skia_LoJ/slides/pgfplotsthemetol.sty
Normal file
123
docs/archives/TO_Skia_LoJ/slides/pgfplotsthemetol.sty
Normal file
@ -0,0 +1,123 @@
|
||||
%%
|
||||
%% This is file `pgfplotsthemetol.sty',
|
||||
%% generated with the docstrip utility.
|
||||
%%
|
||||
%% The original source files were:
|
||||
%%
|
||||
%% pgfplotsthemetol.dtx (with options: `package')
|
||||
%% ---------------------------------------------------------------------------
|
||||
%% Copyright 2015 Matthias Vogelgesang and the LaTeX community. A full list of
|
||||
%% contributors can be found at
|
||||
%%
|
||||
%% https://github.com/matze/mtheme/graphs/contributors
|
||||
%%
|
||||
%% and the original template was based on the HSRM theme by Benjamin Weiss.
|
||||
%%
|
||||
%% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
|
||||
%% International License (https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
%% ---------------------------------------------------------------------------
|
||||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesPackage{pgfplotsthemetol}
|
||||
[2015/06/16 PGFplots colors based on Paul Tol's SRON technical note]
|
||||
\definecolor{TolDarkPurple}{HTML}{332288}
|
||||
\definecolor{TolDarkBlue}{HTML}{6699CC}
|
||||
\definecolor{TolLightBlue}{HTML}{88CCEE}
|
||||
\definecolor{TolLightGreen}{HTML}{44AA99}
|
||||
\definecolor{TolDarkGreen}{HTML}{117733}
|
||||
\definecolor{TolDarkBrown}{HTML}{999933}
|
||||
\definecolor{TolLightBrown}{HTML}{DDCC77}
|
||||
\definecolor{TolDarkRed}{HTML}{661100}
|
||||
\definecolor{TolLightRed}{HTML}{CC6677}
|
||||
\definecolor{TolLightPink}{HTML}{AA4466}
|
||||
\definecolor{TolDarkPink}{HTML}{882255}
|
||||
\definecolor{TolLightPurple}{HTML}{AA4499}
|
||||
\pgfplotscreateplotcyclelist{mbarplot cycle}{%
|
||||
{draw=TolDarkBlue, fill=TolDarkBlue!70},
|
||||
{draw=TolLightBrown, fill=TolLightBrown!70},
|
||||
{draw=TolLightGreen, fill=TolLightGreen!70},
|
||||
{draw=TolDarkPink, fill=TolDarkPink!70},
|
||||
{draw=TolDarkPurple, fill=TolDarkPurple!70},
|
||||
{draw=TolDarkRed, fill=TolDarkRed!70},
|
||||
{draw=TolDarkBrown, fill=TolDarkBrown!70},
|
||||
{draw=TolLightRed, fill=TolLightRed!70},
|
||||
{draw=TolLightPink, fill=TolLightPink!70},
|
||||
{draw=TolLightPurple, fill=TolLightPurple!70},
|
||||
{draw=TolLightBlue, fill=TolLightBlue!70},
|
||||
{draw=TolDarkGreen, fill=TolDarkGreen!70},
|
||||
}
|
||||
\pgfplotscreateplotcyclelist{mlineplot cycle}{%
|
||||
{TolDarkBlue, mark=*, mark size=1.5pt},
|
||||
{TolLightBrown, mark=square*, mark size=1.3pt},
|
||||
{TolLightGreen, mark=triangle*, mark size=1.5pt},
|
||||
{TolDarkBrown, mark=diamond*, mark size=1.5pt},
|
||||
}
|
||||
\pgfplotsset{
|
||||
compat=1.9,
|
||||
mlineplot/.style={
|
||||
mbaseplot,
|
||||
xmajorgrids=true,
|
||||
ymajorgrids=true,
|
||||
major grid style={dotted},
|
||||
axis x line=bottom,
|
||||
axis y line=left,
|
||||
legend style={
|
||||
cells={anchor=west},
|
||||
draw=none
|
||||
},
|
||||
cycle list name=mlineplot cycle,
|
||||
},
|
||||
mbarplot base/.style={
|
||||
mbaseplot,
|
||||
bar width=6pt,
|
||||
axis y line*=none,
|
||||
},
|
||||
mbarplot/.style={
|
||||
mbarplot base,
|
||||
ybar,
|
||||
xmajorgrids=false,
|
||||
ymajorgrids=true,
|
||||
area legend,
|
||||
legend image code/.code={%
|
||||
\draw[#1] (0cm,-0.1cm) rectangle (0.15cm,0.1cm);
|
||||
},
|
||||
cycle list name=mbarplot cycle,
|
||||
},
|
||||
horizontal mbarplot/.style={
|
||||
mbarplot base,
|
||||
xmajorgrids=true,
|
||||
ymajorgrids=false,
|
||||
xbar stacked,
|
||||
area legend,
|
||||
legend image code/.code={%
|
||||
\draw[#1] (0cm,-0.1cm) rectangle (0.15cm,0.1cm);
|
||||
},
|
||||
cycle list name=mbarplot cycle,
|
||||
},
|
||||
mbaseplot/.style={
|
||||
legend style={
|
||||
draw=none,
|
||||
fill=none,
|
||||
cells={anchor=west},
|
||||
},
|
||||
x tick label style={
|
||||
font=\footnotesize
|
||||
},
|
||||
y tick label style={
|
||||
font=\footnotesize
|
||||
},
|
||||
legend style={
|
||||
font=\footnotesize
|
||||
},
|
||||
major grid style={
|
||||
dotted,
|
||||
},
|
||||
axis x line*=bottom,
|
||||
},
|
||||
disable thousands separator/.style={
|
||||
/pgf/number format/.cd,
|
||||
1000 sep={}
|
||||
},
|
||||
}
|
||||
\endinput
|
||||
%%
|
||||
%% End of file `pgfplotsthemetol.sty'.
|
BIN
docs/archives/TO_Skia_LoJ/slides/slide.pdf
Normal file
BIN
docs/archives/TO_Skia_LoJ/slides/slide.pdf
Normal file
Binary file not shown.
158
docs/archives/TO_Skia_LoJ/slides/slide.tex
Normal file
158
docs/archives/TO_Skia_LoJ/slides/slide.tex
Normal file
@ -0,0 +1,158 @@
|
||||
\documentclass[10pt]{beamer}
|
||||
\beamertemplatenavigationsymbolsempty
|
||||
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{default}
|
||||
|
||||
\usepackage{graphicx}
|
||||
\graphicspath{{pictures/}}
|
||||
|
||||
\usepackage[french]{babel}
|
||||
\usepackage[T1]{fontenc}
|
||||
|
||||
\usetheme{metropolis}
|
||||
%\usecolortheme{dove}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{Université de Technologie de Belfort-Montbéliard\\
|
||||
Département informatique}
|
||||
\vskip 4em
|
||||
\begin{center}
|
||||
{\LARGE Développement de nouveaux modules sur le projet Sith}\\
|
||||
\end{center}
|
||||
\vskip 4em
|
||||
Florent \textsc{Jacquet}\\
|
||||
Guillaume \textsc{Renaud}\\
|
||||
{\scriptsize TO52 - A16}
|
||||
\end{frame}
|
||||
|
||||
\begin{frame}
|
||||
\frametitle{Sommaire}
|
||||
\tableofcontents
|
||||
\end{frame}
|
||||
|
||||
\section{Les nouvelles applications}
|
||||
\subsection{Eboutic}
|
||||
\begin{frame}[fragile]\frametitle{Eboutic}
|
||||
\begin{itemize}
|
||||
\item Fournir une boutique
|
||||
\item Paiement en ligne en lien avec l'API du Credit Agricole
|
||||
\item Gestion des cotisations et rechargements
|
||||
\item Attention aux accès concurrentiels: pas visibles pendant le développement, car mono-thread, mais problèmes
|
||||
à la mise en production
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\subsection{Le SAS}
|
||||
\begin{frame}[fragile]\frametitle{Le SAS - Stock à Souvenirs}
|
||||
\begin{itemize}
|
||||
\item Galerie de photos
|
||||
\item Upload simple pour tout le monde, même pour plusieurs dizaines de photos
|
||||
\item Modération et gestion des droits basée sur la gestion des fichiers, ce qui a permis d'améliorer ces
|
||||
derniers
|
||||
\item Problèmes d'optimisation de certaines pages qui mettaient plus de 9 secondes à générer (plus que 2s
|
||||
maintenant)
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\subsection{Les élections}
|
||||
\begin{frame}[fragile]\frametitle{Les élections}
|
||||
\begin{itemize}
|
||||
\item Grosse partie "gestion": c'est Sli qui a principalement développé l'application
|
||||
\item Revue des \textsc{merges request} et choix de design
|
||||
\item Problèmatique de législation vite ignorées puisque validation officielle en AG
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\subsection{La laverie}
|
||||
\begin{frame}[fragile]\frametitle{La laverie}
|
||||
\begin{itemize}
|
||||
\item Gestion d'un planning de reservation en prenant bien en compte les différents états (hors-service, ...) de
|
||||
chaque machine
|
||||
\item Génération de formulaires dynamiques en fonction des réservations (factory design pattern)
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\subsection{La communication}
|
||||
\begin{frame}[fragile]\frametitle{La communication}
|
||||
\begin{itemize}
|
||||
\item Dynamise le site avec tous les textes paramètrables
|
||||
\item Fourni un système de news
|
||||
\item Fourni une newsletter
|
||||
\end{itemize}
|
||||
\begin{itemize}
|
||||
\item Envoie de mails en masse
|
||||
\item Beaucoup de templates
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\section{La gestion des stocks}
|
||||
|
||||
\subsection{Fonctionnement}
|
||||
\begin{frame}[fragile]{Fonctionnement}
|
||||
\begin{itemize}
|
||||
\item Création automatique des listes de courses
|
||||
\item Approvisionnement des stocks
|
||||
\item Prise d'éléments dans le stock
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\subsection{Améliorations et difficultés}
|
||||
\begin{frame}[fragile]\frametitle{Améliorations et difficultés}
|
||||
\begin{itemize}
|
||||
\item Mise à jour quantité liste de courses
|
||||
\item Mise à jour automatique du stock selon les ventes
|
||||
\item Ajout au système de notifications
|
||||
\end{itemize}
|
||||
\textbf{Difficultés}
|
||||
\begin{itemize}
|
||||
\item Découverte du design pattern "factory" pour les formulaires dynamiques
|
||||
\item Apprentissage de Python, en plus du framework
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\section{Le rôle de mainteneur}
|
||||
|
||||
\subsection{Réviser les merge requests}
|
||||
\begin{frame}[fragile]\frametitle{Réviser les merge requests}
|
||||
\begin{itemize}
|
||||
\item Long et fastidieux
|
||||
\item Nécessaire pour maintenir une base de code cohérente
|
||||
\item Permet de retrouver les bugs des nouveaux contributeurs
|
||||
\item Oriente les contributeurs sur la bonne voie et la marche à suivre avec Django/Jinja2/etc...
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
\subsection{Gestion des bugs, des tickets, de la mise en production...}
|
||||
\begin{frame}[fragile]\frametitle{Gestion des bugs, des tickets, de la mise en production...}
|
||||
\begin{itemize}
|
||||
\item Ouverture/fermeture des tickets
|
||||
\item Mailing list/IRC
|
||||
\item Mise en production, gestion des migrations
|
||||
\item Restauration de la base de tests régulièrement
|
||||
\end{itemize}
|
||||
\par Organisation de la passation
|
||||
\end{frame}
|
||||
|
||||
\section{Conclusion}
|
||||
\begin{frame}[fragile]\frametitle{Conclusion}
|
||||
\begin{itemize}
|
||||
\item Apprentissage Django/Git
|
||||
\item Nouvelle mise en pratique des concepts de base de données relationnelles
|
||||
\item Utilisation poussée de Gitlab
|
||||
\item Formation de nouveaux contributeurs
|
||||
\end{itemize}
|
||||
\end{frame}
|
||||
|
||||
|
||||
\begin{frame}[fragile]
|
||||
\begin{center}
|
||||
\textbf{Merci de votre attention}\\
|
||||
Questions?\\
|
||||
Remarques?\\
|
||||
\end{center}
|
||||
\end{frame}
|
||||
|
||||
\end{document}
|
1
docs/archives/TW_Skia/.gitignore
vendored
Normal file
1
docs/archives/TW_Skia/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_minted-Rapport
|
13
docs/archives/TW_Skia/Makefile
Normal file
13
docs/archives/TW_Skia/Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
CC=pdflatex
|
||||
|
||||
all: rapport clean
|
||||
|
||||
rapport: Rapport.tex
|
||||
@echo "Compiling "$<
|
||||
$(CC) -shell-escape $<
|
||||
$(CC) -shell-escape $<
|
||||
|
||||
clean:
|
||||
@echo "Cleaning folder"
|
||||
rm *.aux; rm *.log; rm *.out; rm *.toc; rm *.snm; rm *.nav; rm *.lof
|
||||
|
BIN
docs/archives/TW_Skia/Rapport.pdf
Normal file
BIN
docs/archives/TW_Skia/Rapport.pdf
Normal file
Binary file not shown.
683
docs/archives/TW_Skia/Rapport.tex
Normal file
683
docs/archives/TW_Skia/Rapport.tex
Normal file
@ -0,0 +1,683 @@
|
||||
%%
|
||||
%
|
||||
% Skia
|
||||
% skia@libskia.so
|
||||
%
|
||||
%%
|
||||
|
||||
\documentclass[a4paper]{report}
|
||||
|
||||
%packages
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[francais]{babel}
|
||||
\usepackage{graphicx}\graphicspath{{pix/}}
|
||||
\usepackage{float}
|
||||
\usepackage{scrextend}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage{color}
|
||||
\usepackage{fancyhdr}
|
||||
%Options: Sonny, Lenny, Glenn, Conny, Rejne, Bjarne, Bjornstrup
|
||||
\usepackage[Bjornstrup]{fncychap}
|
||||
\usepackage{minted}
|
||||
\usepackage[colorlinks=true,linkcolor=black]{hyperref}
|
||||
\usepackage{pdfpages}
|
||||
\usepackage{titlesec, blindtext, color}
|
||||
|
||||
%pdf metadata
|
||||
\hypersetup{
|
||||
unicode=true,
|
||||
colorlinks=true,
|
||||
citecolor=black,
|
||||
filecolor=black,
|
||||
linkcolor=black,
|
||||
urlcolor=black,
|
||||
pdfauthor={Skia <skia@libskia.so>},
|
||||
pdftitle={},
|
||||
pdfcreator={pdftex},
|
||||
pdfsubject={},
|
||||
pdfkeywords={},
|
||||
}
|
||||
|
||||
|
||||
\definecolor{keywords}{RGB}{200,0,90}
|
||||
\definecolor{comments}{RGB}{50,50,253}
|
||||
\definecolor{red}{RGB}{160,0,0}
|
||||
\definecolor{brown}{RGB}{160,100,100}
|
||||
\definecolor{green}{RGB}{0,200,0}
|
||||
\definecolor{darkgreen}{RGB}{0,130,0}
|
||||
\definecolor{gray}{RGB}{100,100,100}
|
||||
|
||||
|
||||
%inner meta
|
||||
\title{Architecture de Sith: le nouveau site AE}
|
||||
\author{Skia (Florent JACQUET)}
|
||||
\date{Dernière version: \today}
|
||||
|
||||
\begin{document}
|
||||
|
||||
\maketitle
|
||||
|
||||
\tableofcontents
|
||||
|
||||
\chapter{Introduction}
|
||||
\par Il y a longtemps, au début des années 2000, l'Association des Étudiants a mis en place un site internet qui n'a eu de
|
||||
cesse d'évoluer au fil des ans. Grâce aux différents contributeurs qui s'y sont plongés, et qui ont pu y ajouter leurs
|
||||
bouts de code plus ou moins utiles, le site possède désormais un ensemble de fonctionnalités impressionnant.
|
||||
\par De la comptabilité à la gestion de la laverie, en passant par le forum ou le Matmatronch', le site de l'AE prend
|
||||
actuellement en charge la quasi totalité de la gestion de l'argent, et c'est là un de ses rôles les plus importants.
|
||||
\par Mais les vieilles technologies qu'il emploie, et l'entretien plus ou moins aléatoire qu'il a subit, en font un
|
||||
outil très difficile à maintenir à l'heure actuelle, et le besoin d'une refonte s'imposait de plus en plus.
|
||||
\par Le choix de technologies récentes, maintenues, et éprouvées a donc été fait, et le développement a pu commencer dès
|
||||
Novembre 2015, avec l'objectif d'une mise en production dans l'été 2016, au moins dans une version incluant
|
||||
l'intégralité des fonctions liées à l'argent, qui sont les plus critiques.
|
||||
\par Soutenant les projets libres, j'ai décidé de placer le projet sous licence MIT, assurant ainsi une pérénité aux
|
||||
source. Si quelqu'un dans le futur souhaite le relicencier sous GPL (ou autre licence plus restrictive que la MIT, voire
|
||||
contagieuse), cela reste possible, mais je n'impose au départ que très peu de restrictions \footnote{La seule condition
|
||||
en réalité, est de toujours garder à sa place une copie de la licence originale, à savoir le fichier LICENSE à la racine
|
||||
du site.} .
|
||||
|
||||
\chapter{Les technologies}
|
||||
\label{cha:les_technologies}
|
||||
\par C'est là un des choix les plus important lors d'un tel projet, puisqu'il se fait au début, et qu'il n'est ensuite plus
|
||||
possible de revenir en arrière. Le PHP vieillissant, et
|
||||
piègeux\footnote{\url{https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/}} a donc été mis de côté au profit
|
||||
d'un language plus stable, le \emph{Python} dans sa version 3.
|
||||
|
||||
\section{Python3}
|
||||
\label{sec:python3}
|
||||
\par Le site étant développé en \emph{Python}, il est impératif d'avoir un environnement de développement approprié à ce
|
||||
language. L'outil \verb#virtualenv# permet d'installer un environnement \emph{Python} de manière locale, sans avoir besoin des
|
||||
droits root pour installer des packages. De plus cela permet d'avoir sur sa machine plusieurs environnements différents,
|
||||
adaptés à chaque projet, avec chacun des versions différentes des même paquets.
|
||||
\par La procédure pour installer son \verb#virtualenv# est décrite dans le fichier \verb#README# situé à la racine du
|
||||
projet.
|
||||
|
||||
\section{Django}
|
||||
\label{sec:django}
|
||||
\par \emph{Django} est un framework web pour \emph{Python}, apparu en 2005, et fournissant un grand nombre de
|
||||
fonctionnalités pour développer un site rapidement et simplement. Cela inclut entre autre un serveur Web, pour les
|
||||
échanges HTTP, un parseur d'URL, pour le routage des différentes URI du site, un ORM\footnote{Object-Relational Mapper}
|
||||
pour la gestion de la base de donnée, ou encore un moteur de templates, pour les rendus HTML.
|
||||
\par La version 1.8 de \emph{Django} a été choisie pour le développement de ce projet, car c'est une version LTS (Long Term
|
||||
Support), c'est à dire qu'elle restera stable et maintenue plus longtemps que les autres (au moins jusqu'en Avril 2018).
|
||||
\par La documentation est disponible à cette addresse: \url{https://docs.djangoproject.com/en/1.8/}. Bien que ce rapport
|
||||
présente dans les grandes lignes le fonctionnement de \emph{Django}, il n'est pas et ne se veut pas exhaustif, et la
|
||||
documentation restera donc toujours la référence à ce sujet.
|
||||
|
||||
\subsection{Le fichier de management et l'organisation d'un projet}
|
||||
\label{sub:Le fichier de management et l'organisation d'un projet}
|
||||
|
||||
\par Lors de la création d'un projet \emph{Django}, plusieurs fichiers sont créés. Ces fichiers sont essentiels pour le projet,
|
||||
mais ne contiennent en général pas de code à proprement parler. Ce n'est pas là qu'on y développe quoi que ce soit.
|
||||
|
||||
\subsubsection{manage.py}
|
||||
\label{ssub:manage.py}
|
||||
|
||||
\par Le fichier \verb-manage.py-, situé à la racine, permet de lancer toutes les tâches d'administration du site. Parmis
|
||||
elles:
|
||||
\begin{itemize}
|
||||
\item \textbf{startapp} \\
|
||||
Créer une application.
|
||||
\item \textbf{makemigrations} \\
|
||||
Parser les modèles pour créer les fichiers de migration de la base de donnée.
|
||||
\item \textbf{migrate} \\
|
||||
Appliquer les migrations sur la base de données.
|
||||
\item \textbf{runserver} \\
|
||||
Pour lancer le serveur Web, et donc le site en lui même, dans une version de développement.
|
||||
\item \textbf{makemessages} \\
|
||||
Pour générer les fichiers de traduction, dans le dossier \verb#locale#.
|
||||
\item \textbf{compilemessages} \\
|
||||
Pour compiler les fichiers de traduction, dans le dossier \verb#locale#, et passer de \verb#django.po# à
|
||||
\verb#django.mo#, sa version binaire, optimisée pour l'utilisation.
|
||||
\end{itemize}
|
||||
|
||||
\subsubsection{Un premier dossier}
|
||||
\label{ssub:Un premier dossier}
|
||||
\par Un premier dossier est toujours créé, du nom du projet, et contenant plusieurs fichiers: \verb#settings.py#,
|
||||
\verb#urls.py#, et \verb#wsgi.py#.
|
||||
|
||||
\par \verb#settings.py# est un fichier \emph{Python} servant à définir un grand nombre de constantes paramètrant le
|
||||
fonctionnement du site. L'avantage par rapport à un fichier de configuration classique est que ce dernier est
|
||||
executable, et on peut donc y mettre de la logique, afin d'avoir des paramètres dynamiques.
|
||||
|
||||
\par \verb#urls.py# est le fichier principale contenant les routes du site, c'est à dire les URLs existantes. Il se
|
||||
charge en général d'inclure les fichiers \verb#urls.py# de chaque application afin de garder une architecture modulaire
|
||||
et simple.
|
||||
|
||||
\par \verb#wsgi.py# contient quant à lui les paramètres pour la mise en production du site en tant qu'application WSGI
|
||||
(Web Server Gateway Interface) pour tourner derrière un serveur Web.
|
||||
|
||||
\subsection{Organisation d'une application}
|
||||
\label{sub:organisation_d_une_application}
|
||||
\par Lorsque l'on créer une application avec \verb#./manage.py startapp#, on obtient une fois de plus un dossier type.
|
||||
On trouve dans celui-ci un certain nombre de fichiers:
|
||||
\begin{itemize}
|
||||
\item \textbf{\_\_init\_\_.py} \\
|
||||
Permet de définir le dossier comme un package \emph{Python}. Ce fichier est généralement vide.
|
||||
\item \textbf{models.py} \\
|
||||
C'est là que l'on définit tous les modèles, c'est à dire toutes les classes qui définissent des tables dans la base
|
||||
de donnée.
|
||||
\item \textbf{views.py} \\
|
||||
Les vues y sont définies.
|
||||
\item \textbf{admin.py} \\
|
||||
C'est là que l'on déclare quels modèles doivent apparaîtrent dans l'interface fournie par le module
|
||||
d'administration.
|
||||
\item \textbf{tests.py} \\
|
||||
Ce dernier fichier sert à écrire les tests fonctionnels, unitaires, ou d'intégation à l'aide de la librairie de
|
||||
test de \emph{Django}.
|
||||
\item \textbf{migrations} \\
|
||||
Ce dossier sert à stocker les fichiers de migration de la base de donnée générés par \verb#./manage.py makemigrations#.
|
||||
\end{itemize}
|
||||
\vskip 1em
|
||||
\par On rajoute par la suite généralement plusieurs fichiers:
|
||||
\begin{itemize}
|
||||
\item \textbf{urls.py} \\
|
||||
Pour y définir toutes les URLs de l'application, et ensuite inclure ce fichier dans le fichier \verb#urls.py#
|
||||
global au projet.
|
||||
\item \textbf{templates} \\
|
||||
Celui-ci est un dossier, et on y remet en général un sous dossier du nom de l'application afin de s'en servir de
|
||||
namespace pour les templates.
|
||||
\end{itemize}
|
||||
\vskip 1em
|
||||
\par Dans le cas où un fichier \emph{Python} deviendrait trop gros ou trop complexe, il est toujours possible de le diviser en
|
||||
plusieurs fichiers que l'on met dans un dossier du même nom que ce fichier de départ, et contenant en plus un fichier
|
||||
\verb#__init__.py#. De plus, pour faciliter les imports depuis ce dossier, on peut mettre dans \verb#__init__.py# la
|
||||
ligne\footnote{Un exemple est disponible dans l'application core}:
|
||||
\mint{python}|from .[nom_de_fichier_sans_le_.py] import *|
|
||||
|
||||
\subsection{Les modèles avec l'ORM}
|
||||
\label{sub:les_modèles_avec_l_orm}
|
||||
|
||||
\subsubsection{Le modèle en lui même}
|
||||
\label{ssub:Le modèle en lui même}
|
||||
\par Rien ne vaudra un bon exemple pour comprendre comment sont construits les modèles avec \emph{Django}:
|
||||
\begin{addmargin}[-7em]{0em}
|
||||
\begin{minted}{python}
|
||||
class Club(models.Model): # (1)
|
||||
"""
|
||||
The Club class, made as a tree to allow nice tidy organization
|
||||
""" # (2)
|
||||
name = models.CharField(_('name'), max_length=30) # (3)
|
||||
parent = models.ForeignKey('Club', related_name='children', null=True, blank=True) # (4)
|
||||
unix_name = models.CharField(_('unix name'), max_length=30, unique=True,
|
||||
validators=[ # (5)
|
||||
validators.RegexValidator(
|
||||
r'^[a-z0-9][a-z0-9._-]*[a-z0-9]$',
|
||||
_('Enter a valid unix name. This value may contain only '
|
||||
'letters, numbers ./-/_ characters.')
|
||||
),
|
||||
],
|
||||
error_messages={ # (6)
|
||||
'unique': _("A club with that unix name already exists."),
|
||||
},
|
||||
)
|
||||
address = models.CharField(_('address'), max_length=254)
|
||||
email = models.EmailField(_('email address'), unique=True)
|
||||
owner_group = models.ForeignKey(Group, related_name="owned_club",
|
||||
default=settings.SITH_GROUP_ROOT_ID) # (7)
|
||||
edit_groups = models.ManyToManyField(Group, related_name="editable_club", blank=True) # (8)
|
||||
view_groups = models.ManyToManyField(Group, related_name="viewable_club", blank=True)
|
||||
home = models.OneToOneField(SithFile, related_name='home_of_club', verbose_name=_("home"), null=True, blank=True,
|
||||
on_delete=models.SET_NULL) # (9)
|
||||
\end{minted}
|
||||
\end{addmargin}
|
||||
\par Explications:
|
||||
\begin{description}
|
||||
\item[(1)] Un modèle hérite toujours de \verb#models.Model#. Il peut y avoir des intermédiaires, mais \verb#Model#
|
||||
sera toujours en haut.
|
||||
\item[(2)] Toujours penser à commenter le modèle.
|
||||
\item[(3)] Un premier attribut: \verb#name#, de type \verb#CharField#. Il constitue une colonne dans la base de
|
||||
donnée une fois que \verb#./manage.py migrate# a été appliqué.
|
||||
\item[(4)] Une \verb#ForeignKey#, l'une des relations les plus utilisées. \verb#related_name# précise le nom qui sert
|
||||
de retour vers cette classe depuis la classe pointée. Ici, elle est même récursive, puisque l'on pointe vers la
|
||||
classe que l'on est en train de définir, ce qui donne au final une structure d'arbre.
|
||||
\item[(5)] On peut toujours préciser des \verb#validators#, afin que le modèle soit contraint, et que \emph{Django}
|
||||
maintienne toujours des informations cohérentes dans la base.
|
||||
\item[(6)] Un message d'erreur peut être précisé pour expliciter à l'utilisateur les problèmes rencontrés.
|
||||
\item[(7)] On utilise ici le champ \verb#default# pour préciser une valeur par défaut au modèle, et celui-ci est
|
||||
affecté à une valeur contenue dans les \verb#settings# de \emph{Django}.
|
||||
\item[(8)] Les \verb#ManyToManyField# permettent de générer automatiquement une table intermédiaire de manière
|
||||
transparente afin d'avoir des relations doubles dans les deux classes mises en jeu.
|
||||
\item[(9)] Le \verb#OneToOneField# est très utilisé pour étendre une table avec des informations supplémentaires
|
||||
sans toucher à la table originale.
|
||||
\item[PRIMARY KEY] Les plus observateurs d'entre vous auront remarqué qu'il n'y a pas ici de \verb#PRIMARY KEY# de précisé. En
|
||||
effet, \emph{Django} s'en occupe automatiquement en rajoutant un champ \verb#id# jouant ce rôle. On peut alors y
|
||||
accèder en l'appelant par son nom, \verb#id# la plupart du temps, sauf s'il a été personnalisé, ou bien par
|
||||
l'attribut générique \verb#pk#, toujours présent pour désigner la \verb#PRIMARY KEY# d'un modèle, quelle qu'elle
|
||||
soit.
|
||||
\end{description}
|
||||
|
||||
\subsubsection{Les migrations}
|
||||
\label{ssub:Les migrations}
|
||||
\par Les migrations sont à lancer à chaque fois que l'on modifie un modèle. Elles permettent de conserver la base de
|
||||
donnée tout en la faisant évoluer dans sa structure, pour ajouter ou supprimer une colonne dans une table par exemple.
|
||||
\par Lancer la commande \verb#./manage.py makemigrations [nom de l'appli]# va permettre de générer un fichier \emph{Python}
|
||||
automatiquement, qui sera mis à la suite des précédents, et qui sera appliqué sur la base au moment du lancement de
|
||||
\verb#./manage.py migrate#.
|
||||
|
||||
\subsection{Les vues}
|
||||
\label{sub:les_vues}
|
||||
\par Les vues sont les parties de code s'occupant de l'interface avec l'utilisateur. Elles sont appelées par les URLs,
|
||||
et renvoient des réponses HTTP en fonction du traitement effectué.
|
||||
|
||||
\subsubsection{Les URL}
|
||||
\label{ssub:Les URL}
|
||||
\par Les URLs sont définies par application, et centralisées dans le dossier du projet. Il s'agit à chaque fois d'une
|
||||
liste d'appel à la fonction \verb#url()#, qui comprends toujours une expression rationnelle décrivant l'URL, une
|
||||
fonction passée en tant que callback qui sera appelé au moment où l'URL est résolue, et enfin un nom, permettant de s'y
|
||||
référer dans les fonctions de résolution inverse, comme dans les templates par exemple. Nous détaillerons cette
|
||||
utilisation plus tard.
|
||||
|
||||
\par Pour garder une organisation claire, les URLs sont classées par espaces de noms (namespace) afin d'avoir à éviter
|
||||
de préfixer tous les noms pour s'y retrouver. Le namespace d'une URL est généralement le même nom que celui de
|
||||
l'application dans laquelle elle se trouve.
|
||||
|
||||
\subsubsection{Les fonctions de vue}
|
||||
\label{ssub:Les fonctions de vue}
|
||||
\par Une fonction de vue prend toujours en paramètre une variable \verb#request# et renvoie toujours un objet
|
||||
\verb#HTTPResponse#, contenant un code de retour HTTP, ainsi qu'une chaîne de caractères contenant la réponse en elle
|
||||
même.
|
||||
\par Entre temps, le traitement des informations permet de mettre à jour, de créer, ou de supprimer les objets définis
|
||||
dans les modèles, par le biais des paramètres passé dans la requête. Ainsi, on peut accèder aux informations des
|
||||
variables \verb#GET# et \verb#POST# très facilement en appelant respectivement \verb#request.GET['ma_clef']# et
|
||||
\verb#request.POST['ma_clef']#, ces deux variables fonctionnant comme des dictionnaires.
|
||||
|
||||
\subsubsection{Des vues basées sur des classes}
|
||||
\label{ssub:Des vues basées sur des classes}
|
||||
\par Les vues avec \emph{Django} peuvent aussi être définies comme des classes. Elles héritent alors à ce moment là
|
||||
toutes de la classe \verb#View#, mais ont toutefois souvent beaucoup d'intermédiaires et n'héritent donc pas directement
|
||||
de cette dernière.
|
||||
\par L'avantage de ces vues sous forme de classe est de pouvoir séparer toute la chaîne de traitement entre les
|
||||
différentes méthodes, et ainsi permettre, en jouant avec l'héritage, de fournir alors très peu d'informations à la
|
||||
classe, tout en lui permettant d'effectuer un travail correct.
|
||||
\par Ainsi, on retrouve de base, dans les filles de \verb#View#, un grand nombre de classes prédéfinies pour la plupart
|
||||
des comportement. \verb#DetailView#, \verb#CreateView#, \verb#ListView#, sont quelques exemples de classes renvoyant
|
||||
respectivement un objet en détails, un formulaire pour créer un nouvel objet, et enfin une liste d'objets. Il existe
|
||||
cependant un bon nombre de ces vues fournissant d'autres fonctionnalités, et si malgré tout, aucune ne peut convenir, il
|
||||
reste possible de se baser sur l'une d'elle et surcharger l'une de ses fonctions pour l'adapter à ses besoins.
|
||||
\par L'écosystème des \verb#class-based views# étant toutefois assez complexe et riche, un site web a été créé afin
|
||||
d'offrir un bon résumé de la situation: \emph{Classy class-based views}, accessible à l'adresse
|
||||
\url{http://ccbv.co.uk/}.
|
||||
|
||||
\section{Jinja2}
|
||||
\label{sec:jinja2}
|
||||
\par \emph{Jinja2} est un moteur de template écrit en \emph{Python} qui s'inspire fortement de la syntaxe des templates de
|
||||
\emph{Django}, mais qui apporte toutefois son lot d'améliorations non négligeables. En effet, l'ajout des macros, par
|
||||
exemple, permet de factoriser une grande partie du code.
|
||||
\par Un moteur de template permet de générer du contenu textuel de manière procédural en fonction des données à
|
||||
afficher. Cela permet en pratique de pouvoir inclure du code proche de \emph{Python} dans la syntaxe au milieu d'un
|
||||
document contenant principalement du HTML. Ainsi, si on a une liste d'objets, on peut facilement executer une boucle
|
||||
\verb#for# afin de faire afficher simplement tous les objets selon le même format.
|
||||
\noindent De même, il est facile d'inclure un \verb#if# pour décider à l'execution d'afficher ou non un lien en fonction
|
||||
des droits que l'utilisateur possède sur le site, par exemple.
|
||||
\par En plus des structures conditionnelles classiques, un moteur de templates permet de formater des données plus
|
||||
simplement, comme par exemple des dates, en fonction de la langue actuellement utilisée par l'utilisateur.
|
||||
\par Enfin, bien utilisés, les templates permettent d'utiliser des fonctions d'inclusion, ce qui permet de hiérarchiser
|
||||
les fichiers, et de s'assurer de l'unité de certaines parties du site. Ainsi, les \emph{headers}, les \emph{footers}, et
|
||||
autres menus que l'on retrouve sur toutes les pages du site sont définis chacun dans un seul fichier et inclus dans
|
||||
tous les autres.
|
||||
|
||||
\subsection{Exemple de template Jinja2}
|
||||
\label{sub:exemple_de_template_jinja2}
|
||||
|
||||
\begin{addmargin}[-4em]{0em}
|
||||
\begin{minted}{jinja}
|
||||
{% extends "core/base.jinja" %} {# (1) #}
|
||||
|
||||
{% block title %} {# (2) #}
|
||||
{% trans user_name=user.get_display_name() %}{{ user_name }}'s tools{% endtrans %} {# (3) #}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{% trans %}User Tools{% endtrans %}</h3> {# (4) #}
|
||||
<p><a href="{{ url('core:user_profile', user_id=request.user.id) }}{# (5) #}">
|
||||
{% trans %}Back to profile{% endtrans %}</a>
|
||||
</p>
|
||||
|
||||
<h4>{% trans %}Sith management{% endtrans %}</h4>
|
||||
<ul>
|
||||
{% if user.is_root %} {# (6) #}
|
||||
<li><a href="{{ url('core:group_list') }}">{% trans %}Groups{% endtrans %}</a></li>
|
||||
{% endif %}
|
||||
{% if user.is_in_group(settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
|
||||
<li><a href="{{ url('accounting:bank_list') }}">Accounting</a></li>
|
||||
{% endif %}
|
||||
{% if user.is_board_member or user.is_root %}
|
||||
<li><a href="{{ url('subscription:subscription') }}">Subscriptions</a></li>
|
||||
<li><a href="{{ url('counter:admin_list') }}">Counters management</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
|
||||
<h4>{% trans %}Club tools{% endtrans %}</h4>
|
||||
<ul>
|
||||
{% for m in user.memberships.filter(end_date=None).all() %} {# (7) #}
|
||||
<li><a href="{{ url('club:tools', club_id=m.club.id) }}">{{ m.club }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
\end{minted}
|
||||
\end{addmargin}
|
||||
|
||||
\begin{description}
|
||||
\item[(1)] Nous faisons ici une extension d'un template existant afin de bénéficier des blocs déjà défini, et afin
|
||||
d'intégrer le contenu de ce template dans celui déjà défini.
|
||||
\item[(2)] \verb#title# est un bloc défini dans le template \verb#base.jinja#. Le redéfinir joue alors le même rôle
|
||||
qu'une surcharge de méthode dans de l'héritage, et permet de remplacer le contenu du bloc, tout en conservant sa
|
||||
place dans le template parent.
|
||||
\item[(3)] La variable \verb#user# faisant ici partie du contexte, nous pouvons donc appeler une de ses méthodes
|
||||
pour obtenir un contenu dynamiquement.
|
||||
\item[(4)] Les blocs \verb#{% trans %}# et \verb#{% endtrans %}# permettent de définir les chaînes qui vont ensuite
|
||||
être traduites.
|
||||
\item[(5)] L'appel à la fonction \verb#url()# permet de résoudre la route afin d'obtenir l'adresse appropriée en
|
||||
fonction des arguments passé. Cette fonction fait généralement partie du contexte global, et est donc accessible
|
||||
dans tous les templates.
|
||||
\item[(6)] Les structures conditionnelles permettent d'afficher ou pas un élément en fonction de la valeur d'une
|
||||
variable ou du retour d'une fonction.
|
||||
\item[(7)] Le \verb#for# permet, comme en Python, d'itérer sur les éléments d'une liste. Ici, on fait même une
|
||||
requête via l'ORM de \emph{Django} en utilisant un filtre pour obtenir directement des valeurs depuis la base de
|
||||
donnée de manière transparente.
|
||||
\end{description}
|
||||
|
||||
\subsection{Le contexte}
|
||||
\label{sub:le_contexte}
|
||||
\par Le contexte dans lequel le template s'execute influe beaucoup sur la capacité de \emph{Jinja} à s'adapter
|
||||
dynamiquement au contenu. Plus on a de variables disponibles, plus on va pouvoir générer un contenu s'y adaptant.
|
||||
\par Il est possible de définir le contexte global, et donc ce qui est accessible dans tous les templates, comme il est
|
||||
possible d'ajouter manuellement et spécifiquement des variables au contexte pour un template particulier, dans une vue
|
||||
particulière.
|
||||
|
||||
\chapter{Organisation du projet}
|
||||
\label{cha:organisation_du_projet}
|
||||
|
||||
\par Après cette présentations des différentes technologies employées dans le projet, passons maintenant à une partie plus
|
||||
spécifique à Sith en lui même.
|
||||
|
||||
\section{Les options spécifiques}
|
||||
\label{sec:les_options_sp_cifiques}
|
||||
|
||||
\subsection{Django-jinja}
|
||||
\label{sub:django_jinja}
|
||||
\par \emph{Jinja} n'étant pas inclus de base dans \emph{Django}, le paquet \emph{Django-jinja} a été mis en place afin
|
||||
de bénéficier au mieux des performances de chacun, comme les filtres personnalisés de \emph{Django} dans la puissance des
|
||||
macros de \emph{Jinja}. Tout cela se trouve dans la variable \verb#TEMPLATES#.
|
||||
\par \emph{Jinja} a été ajouté afin de s'occuper uniquement des fichiers ayant l'extension \verb#.jinja# dans les
|
||||
dossiers \verb#templates# de chaque application.
|
||||
\par Un certain nombre de variables et fonctions ont été ajoutés au contexte global. Parmis elles, l'ensemble des
|
||||
filtres que \emph{Django} fournit, mais aussi un filtre pour passer de \emph{Markdown} à \emph{HTML}, l'ensemble du
|
||||
contenu de \verb#settings#, et enfin des fonction utiles dont voici la liste:
|
||||
\begin{itemize}
|
||||
\item \textbf{can\_edit\_prop} permet, en fonction d'une variable \verb#user# et d'un objet, de savoir si
|
||||
l'utilisateur donnée peut modifier les propriétés de cet objet.
|
||||
\item \textbf{can\_edit} permet, en fonction d'une variable \verb#user# et d'un objet, de savoir si
|
||||
l'utilisateur donnée peut éditer l'objet.
|
||||
\item \textbf{can\_view} permet, en fonction d'une variable \verb#user# et d'un objet, de savoir si
|
||||
l'utilisateur donnée peut voir l'objet.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Le fichier \textbf{settings\_custom.py}}
|
||||
\label{sub:le_fichier_settings_custom.py}
|
||||
\par Afin de faciliter la configuration des différentes instances du projet, un fichier \verb#settings_custom.py# a été
|
||||
créé à côté de \verb#settings.py#. Celui-ci est automatiquement inclu à la fin de \verb#settings.py#, pour que tout ce
|
||||
qui est défini dans le \verb#_custom# vienne remplacer les valeurs par défaut. Seul \verb#settings.py# est versionné
|
||||
dans \emph{Git}, et est exhaustif concernant la configuration et les variables requises. \verb#settings_custom.py# quant
|
||||
à lui n'est même pas obligatoire, et s'il existe, ne peut contenir que quelques variables qui diffèrent de la
|
||||
configuration par défaut\footnote{Typiquement, ajouter \textbf{DEBUG=True} }.
|
||||
|
||||
|
||||
\section{Les commandes ajoutées}
|
||||
\label{sec:les_commandes_ajoutees}
|
||||
\par Si cela ne suffit pas, il est possible d'enrichir de nouvelles commandes le script \verb#manage.py#. Cela a été fait
|
||||
pour \emph{Sith}, afin de pouvoir très rapidement déployer un environnement en ayant déjà les quelques données
|
||||
nécéssaires au fonctionnement du projet, comme le groupe \verb#root# par exemple.
|
||||
|
||||
\subsection{setup}
|
||||
\label{sub:setup}
|
||||
\par La fonction \verb#setup# s'occupe simplement de supprimer le fichier \verb#db.sqlite3#, qui est le fichier de base
|
||||
de donnée utilisé pour le développement, et relance une procédure de migration pour reconstruire une base de donnée
|
||||
propre avant de la peupler.
|
||||
\par Cette commande permet donc de s'assurer que la base de donnée utilisée est neuve et non corrompue.
|
||||
|
||||
\subsection{populate}
|
||||
\label{sub:populate}
|
||||
\par \verb#populate# permet de remplir la base de donnée avec dans un premier temps les données \textbf{nécessaires} au
|
||||
bon fonctionnement du site. Cela comprend notamment un superutilisateur, les groupes définis dans
|
||||
\verb#settings.SITH_GROUP_*_ID#, dont le groupe \verb#root# fait partie, une première page de Wiki, ainsi qu'un club
|
||||
racine, l'AE dans notre cas.
|
||||
\par Cette fonction prend un éventuel argument, \verb#--prod#, qui lui permet de mettre en place le strict minimum
|
||||
énoncé précédemment. Sinon, elle continue en ajoutant un certain nombre de données pratiques pour le développement,
|
||||
comme un certain nombre d'utilisateurs avec différents droits, de nouvelles pages dans le Wiki, de nouveaux clubs, des
|
||||
comptoirs et des produits, ou encore des données de comptabilité.
|
||||
\par L'argument \verb#--prod# peut, en outre, être passé directement depuis la fonction \verb#setup#.
|
||||
|
||||
\chapter{Les applications}
|
||||
\label{cha:les_applications}
|
||||
\par Chaque application va être détaillée ici. Cela permet de mettre en valeur le rôle de chacune, et de signaler les
|
||||
éventuelles particularités qui peuvent s'y trouver.
|
||||
|
||||
\section{Core}
|
||||
\label{sec:core}
|
||||
\subsection{Résumé}
|
||||
\label{sub:resume}
|
||||
\par L'application \emph{Core} est de loin la plus importante de toutes. C'est elle qui gère les utilisateurs ainsi que
|
||||
leurs droits. Le CMS y est aussi définit pour tout ce qui est pages de Wiki, pages statiques, ou l'ajout du filtre
|
||||
\verb#markdown# pour les templates.
|
||||
|
||||
\subsection{Liste des modèles}
|
||||
\label{sub:liste_des_modeles}
|
||||
\begin{itemize}
|
||||
\item \textbf{Group} \\
|
||||
Ce modèle se subdivise en deux: RealGroup et MetaGroup, décrivants respectivement un vrai groupe géré à la main
|
||||
dans la liste des groupes, et un meta-groupe, géré automatiquement, en général par les clubs, ou bien par les
|
||||
cotisations.
|
||||
\item \textbf{User} \\
|
||||
Le modèle des utilisateurs, qui est ensuite décliné ou référencé dans beaucoup d'applications pour les
|
||||
utilisations spécifiques. C'est toutefois ici que sont déclarés les fonctions de gestion des droits des
|
||||
utilisateurs, afin de pouvoir les utiliser partout ailleurs.
|
||||
\item \textbf{AnonymousUser} \\
|
||||
Cette classe n'est pas un modèle stocké en base, puisqu'elle sert à instancier la variable \verb#user#
|
||||
lorsqu'aucun utilisateur n'est connecté au site.
|
||||
\item \textbf{Page} \\
|
||||
Décrit une entité page, servant dans le Wiki ou pour les pages statiques du site. Cette classe s'occupe des
|
||||
méta-données de la page, comme ses droits, mais son contenu est en réalité stocké dans un objet \verb#PageRev#.
|
||||
\item \textbf{PageRev} \\
|
||||
Décrit une révision de page. Utiliser une autre classe avec une \verb#ForeignKey# permet de gérer facilement un
|
||||
historique des révisions.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{La gestion des droits}
|
||||
\label{sub:la_gestion_des_droits}
|
||||
\par La gestion des droits est implémentée de manière globale dans l'application Core.
|
||||
\par On trouve en effet dans \verb#views/__init__.py# un certain nombre de mixins \footnote{Un mixin est, dans
|
||||
\emph{Django}, un terme désignant une classe abstraite qui ne peut pas servir de parente seule. Elle permet de
|
||||
surcharger certaines méthodes d'une autre classe abstraite afin de l'adapter à un comportement plus spécifique, mais
|
||||
reste totalement inutile quand elle est seule. La gestion des droits est un bon exemple puisqu'elle ne s'occupe pas
|
||||
vraiment de traitement des données comme les autres vues le feraient, elle permet simplement d'ajouter une condition à une
|
||||
autre classe où cette dernière renverrait un \emph{403 Forbidden} } s'occupant de cela, en se basant sur
|
||||
un modèle général permettant de rendre compatible rapidement n'importe quel modèle que l'on voudrait protéger. Il suffit
|
||||
alors de déclarer dans la classe une certain nombre de méthodes et/ou d'attributs, le reste étant simplement déjà pris
|
||||
en charge par les mixins suivants:
|
||||
\begin{itemize}
|
||||
\item \textbf{CanCreateMixin} \\
|
||||
Cette classe est à mettre en parente d'une classe héritant de \verb#CreateView#, afin d'empêcher quelqu'un
|
||||
n'ayant pas les droits de créer un objet.\\
|
||||
Méthode correspondante à créer dans les modèles: \\
|
||||
\verb#def can_be_created_by(user):# \\
|
||||
(Attention, ce n'est pas une méthode prenant \verb#self# en premier paramètre!)
|
||||
\item \textbf{CanEditPropMixin} \\
|
||||
Cette classe protège l'objet pour l'édition avancée. Par exemple: éditer les droits sur une page, ou éditer les
|
||||
droits accordé à un utilisateur. \\
|
||||
Attribut correspondant à créer dans les modèles: \\
|
||||
\verb#owner_group = models.ForeignKey(Group, # \\
|
||||
\verb# related_name="owned_user", default=settings.SITH_GROUP_ROOT_ID)# \\
|
||||
Méthode correspondante à créer dans les modèles: \\
|
||||
\verb#def is_owned_by(self, user):# \\
|
||||
\item \textbf{CanEditMixin} \\
|
||||
Cette classe protège l'objet pour l'édition non avancée. Par exemple: éditer une page, ou éditer le profil d'un
|
||||
utilisateur. \\
|
||||
Attribut correspondant à créer dans les modèles: \\
|
||||
\verb#edit_groups = models.ManyToManyField(Group, # \\
|
||||
\verb# related_name="editable_user", blank=True)# \\
|
||||
Méthode correspondante à créer dans les modèles: \\
|
||||
\verb#def can_be_edited_by(self, user):# \\
|
||||
\item \textbf{CanViewMixin} \\
|
||||
Cette classe protège l'objet pour la vue. Par exemple: consulter une page, ou voir le profil d'un utilisateur. \\
|
||||
Attribut correspondant à créer dans les modèles: \\
|
||||
\verb#view_groups = models.ManyToManyField(Group, # \\
|
||||
\verb# related_name="viewable_user", blank=True)# \\
|
||||
Méthode correspondante à créer dans les modèles: \\
|
||||
\verb#def can_be_viewed_by(self, user):# \\
|
||||
\end{itemize}
|
||||
\par Pour savoir si l'on doit implémenter les méthodes, les attributs, ou les deux, il faut simplement se poser la
|
||||
question de savoir si l'objet en question requiert une gestion des droits à l'échelle de la classe ou à l'échelle de
|
||||
l'objet, et si cette gestion peut être calculé par de la logique.
|
||||
\par Si on a besoin d'une gestion pour la classe, ou si du code peut être implémenter pour déterminer qui peut avoir tel
|
||||
droit, alors la méthode suffira. Mais si on a besoin d'une gestion au niveau de l'objet, alors il faudra certainement
|
||||
recourir aux attributs.
|
||||
\par Exemples:
|
||||
\begin{itemize}
|
||||
\item Les comptes en banque sont gérés uniquement par les personnes faisant partie du groupe \verb#admin-compta#.
|
||||
Ils ont donc tous les mêmes droits, c'est une gestion au niveau de la classe, donc les méthodes suffisent.
|
||||
\item Les classeurs de comptabilité sont gérés par les trésoriers des clubs, ils n'ont pas tous les mêmes droits,
|
||||
mais cela peut tout de même se calculer en fonction des postes dans les clubs correspondants. On a donc besoin
|
||||
des méthodes uniquement.
|
||||
\item Les pages n'appartiennent pas forcément à un club, ni à une quelconque entité, mais ont tout de même besoin de
|
||||
gestion des droits au niveau de l'objet. L'ajout des attributs est donc nécessaire pour pouvoir gérer cela au
|
||||
cas par cas.
|
||||
\end{itemize}
|
||||
|
||||
\section{Subscription}
|
||||
\label{sec:subscription}
|
||||
\subsection{Résumé}
|
||||
\label{sub:resume}
|
||||
\par Cette application ajoute le support des cotisations. Elle fournit également les interfaces de cotisation.
|
||||
|
||||
\subsection{Liste des modèles}
|
||||
\label{sub:liste_des_modeles}
|
||||
\begin{itemize}
|
||||
\item \textbf{Subscription} \\
|
||||
Un modèle cotisation, pour stocker ces dernières. Cette classe fait automatiquement les calculs de début et de
|
||||
fin de cotisation en fonction de la date du jour, du type de cotisation, et de la durée en semestre de
|
||||
cotisation.
|
||||
\end{itemize}
|
||||
|
||||
\section{Accounting}
|
||||
\label{sec:accounting}
|
||||
\subsection{Résumé}
|
||||
\label{sub:resume}
|
||||
\par Cette application sert à gérer la comptabilité. Elle est architecturée de façon hiérarchique, avec en haut, les
|
||||
comptes bancaires réels, qui contiennent eux des comptes de clubs, permettant de les diviser en plusieurs petits comptes
|
||||
en interne, et enfin les classeurs de trésorerie, propres à chaque compte club, permettant de faire les comptes en
|
||||
triant par semestre.
|
||||
\par De plus, cette application définit un nouveau type de champ dans la base de donnée: le champ \verb#CurrencyField#,
|
||||
permettant de stocker de valeurs monétaires.
|
||||
|
||||
\subsection{Liste des modèles}
|
||||
\label{sub:liste_des_modeles}
|
||||
\begin{itemize}
|
||||
\item \textbf{BankAccount} \\
|
||||
Le modèle des comptes bancaires.
|
||||
\item \textbf{ClubAccount} \\
|
||||
Le modèle des comptes clubs.
|
||||
\item \textbf{GeneralJournal} \\
|
||||
Le modèle des classeurs de comptabilité, généralement semestriels, mais ils peuvent toutefois fonctionner en
|
||||
année pour les activités plus longues comme le Gala.
|
||||
\item \textbf{AccountingType} \\
|
||||
Le modèle pour stocker les types comptables, servant à remplir les opérations.
|
||||
\item \textbf{SimpleAccountingType} \\
|
||||
Le modèle pour stocker les types comptables simplifiés, servant à remplir les opérations.
|
||||
\item \textbf{Label} \\
|
||||
Le modèle pour stocker les étiquettes, servant à classifer les opérations.
|
||||
\item \textbf{Operation} \\
|
||||
Le modèle des opérations, servant à remplir les classeurs comptables. Une opération peut être un débit ou un
|
||||
crédit, et permet ensuite d'éditer des factures, par exemple.
|
||||
\end{itemize}
|
||||
|
||||
\section{Counter}
|
||||
\label{sec:counter}
|
||||
\subsection{Résumé}
|
||||
\label{sub:resume}
|
||||
\par Cette application s'occupe de la gestion des comptoirs. Elle définit ainsi des produits, et ajoute également le
|
||||
support du compte AE pour les utilisateurs.
|
||||
|
||||
\subsection{Liste des modèles}
|
||||
\label{sub:liste_des_modeles}
|
||||
\begin{itemize}
|
||||
\item \textbf{Customer} \\
|
||||
Ce modèle étend l'utilisateur pour lui rajouter un compte AE. Il est lié à la classe \verb#User# par un
|
||||
\verb#OneToOneField#.
|
||||
\item \textbf{ProductType} \\
|
||||
Ce modèle ajoute des types de produits afin de catégoriser ces derniers.
|
||||
\item \textbf{Product} \\
|
||||
Ce modèle décrit les produits pouvant être vendus dans les différents comptoirs.
|
||||
\item \textbf{Counter} \\
|
||||
Ce modèle décrit les comptoirs, qui permettent de générer des recharges de compte et des ventes de produits.
|
||||
\item \textbf{Refilling} \\
|
||||
Ce modèle permet de stocker les rechargements de compte.
|
||||
\item \textbf{Selling} \\
|
||||
Ce modèle permet d'enregistrer toutes les ventes de produits.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
\section{Club}
|
||||
\label{sec:club}
|
||||
\subsection{Résumé}
|
||||
\label{sub:resume}
|
||||
\par Cette application permet de générer les clubs et les adhésions des utilisateurs à ceux-ci.
|
||||
|
||||
\subsection{Liste des modèles}
|
||||
\label{sub:liste_des_modeles}
|
||||
\begin{itemize}
|
||||
\item \textbf{Club} \\
|
||||
Le modèle des clubs.
|
||||
\item \textbf{Membership} \\
|
||||
Le modèle des adhésions. Stocker cela dans un modèle à part permet de conserver un historique des personnes
|
||||
ayant eu un rôle quelconque dans un club quelconque.
|
||||
\end{itemize}
|
||||
|
||||
|
||||
|
||||
\chapter{Conclusion}
|
||||
\par Encore une fois, ce rapport ne se veut absolument pas exhaustif sur quoi que ce soit.
|
||||
\par Concernant \emph{Python}, \emph{Django}, ou \emph{Jinja}, les documentations respectives sont toujours très bien
|
||||
faites, et permettront de répondre à toutes les questions techniques concernant les technologies.
|
||||
\par Concernant le projet \emph{Sith} en lui-même, ce rapport n'est pas non plus exhaustif. Pour cela, lire le code des
|
||||
différentes sections sera le meilleur moyen de comprendre le fonctionnement des différentes applications. Pour obtenir plus
|
||||
rapidement un résumé à jour des sources, le fichier \verb#Doxyfile# présent à la racine du site permet de regénérer de
|
||||
la documentation exhaustive rapidement à l'aide de \emph{Doxygen} (voir la section correspondante dans le README).
|
||||
\par J'espère toutefois que même s'il n'est pas complet, ce rapport permettra à tout futur contributeur de rentrer plus
|
||||
rapidement dans le projet.
|
||||
\par L'idéal serait également de maintenir à jour ce rapport du mieux possible en même temps que le développement
|
||||
avance, même si je ne me fais guère d'illusions en pratique.
|
||||
|
||||
\section{Pour le futur...}
|
||||
\label{sec:pour_le_futur}
|
||||
\par En l'état actuel des choses, un grand nombre d'éléments sont encore manquants au site:
|
||||
\begin{itemize}
|
||||
\item Une API REST pour pouvoir facilement intégrer d'autres outils autour du site.
|
||||
\item Du CSS, pour qu'il soit un peu plus joli à regarder.
|
||||
\item Du Javascript, et particulièrement de l'AJAX pour améliorer l'ergonomie de certaines pages.
|
||||
\item Un grand nombre de vues pour aider à gérer les données plus efficacement, ou à les gérer tout court dans
|
||||
certains cas.
|
||||
\item Quelques applications utiles à qui existent sur le site actuel, mais que je n'ai pas encore eu le temps de
|
||||
développer: un forum, une galerie de photos, une gestion basique des fichiers pour uploader des documents dans
|
||||
les pages ou le forum, un système de news, une newsletter (Weekmail), une gestion des sondages et des élections,
|
||||
etc...
|
||||
\end{itemize}
|
||||
|
||||
\section{Apports personnels}
|
||||
\label{sec:apports_personnels}
|
||||
\par Même s'il est vrai que j'ai beaucoup appris en développant ce site, cela reste avant tout un travail de quantité
|
||||
plus que de qualité: on définit des modèles, puis les vues correspondantes en terminant par les templates, et on répète
|
||||
l'opération pour l'application suivante.
|
||||
\par Mais j'ai tout de même pu mettre en place de l'intégration continue pour ce projet, ce qui a été certes, très
|
||||
rapide, mais toutefois très enrichissant, étant donnée que ces méthodes sont très en vogue ces derniers temps.
|
||||
\par J'ai également pu me familiariser d'avantage avec le fonctionnement d'un ORM, et la magie noire que cela permet de
|
||||
faire \footnote{Je trouve toujours magique de pouvoir faire une requête SQL au milieu d'un template sans que cela
|
||||
paraisse affreux :)}.
|
||||
\par Enfin, j'espère que ce projet va, en plus d'être correctement mené au bout, pouvoir être repris par la suite par
|
||||
les futurs membres de l'équipe info de l'AE, et pourquoi pas par d'autres contributeurs...
|
||||
\vskip 3em
|
||||
\emph{Skia <skia@libskia.so> - 2016}
|
||||
|
||||
\end{document}
|
||||
|
303
docs/explanation/conventions.md
Normal file
303
docs/explanation/conventions.md
Normal file
@ -0,0 +1,303 @@
|
||||
Cette page traite des conventions utilisées dans le développement du site.
|
||||
|
||||
## Langue
|
||||
|
||||
Les noms, de fonctions, de classe, de fichiers et de dossiers sont en anglais.
|
||||
De même, les commentaires et les docstrings sont rédigés en anglais.
|
||||
|
||||
En revanche la documentation est rédigée en français.
|
||||
En effet, les développeurs et les développeuses
|
||||
qui ont été, sont et seront amenés à travailler
|
||||
sur le site sont presque tous des francophones.
|
||||
Or, la bonne compréhension prime.
|
||||
Une documentation, qui se doit d'utiliser au mieux
|
||||
les mots justes, compris de manière juste,
|
||||
gagne à être écrite en langue vernaculaire,
|
||||
lorsqu'on est assuré qu'une coopération
|
||||
internationale est peu probable.
|
||||
|
||||
De la sorte, on s'assure au mieux que les
|
||||
rédacteurs et rédactrices s'expriment bien
|
||||
et que, réciproquement, les lecteurs et lectrices
|
||||
comprennent au mieux.
|
||||
|
||||
A ce titre, on ne vous en voudra pas
|
||||
si vous rédigez des commentaires ou des docstrings
|
||||
en français.
|
||||
|
||||
En revanche, le code en lui-même doit
|
||||
rester impérativement en anglais ;
|
||||
les instructions étant en langue anglaise,
|
||||
introduire des mots français au milieu crée un
|
||||
contraste qui nuit à la compréhension.
|
||||
|
||||
De manière générale, demandez-vous juste à qui vous êtes en train d'écrire :
|
||||
|
||||
- si vous écrivez pour la machine, c'est en anglais
|
||||
- si vous écrivez pour des êtres humains, c'est en français
|
||||
|
||||
## Gestion de version
|
||||
|
||||
Le projet utilise Git pour gérer les versions et
|
||||
GitHub pour héberger le dépôt distant.
|
||||
|
||||
L'arbre possède deux branches protégées : `master` et `taiste`.
|
||||
|
||||
`master` est la branche contenant le code tel qu'il
|
||||
tourne effectivement sur le vrai site de l'AE.
|
||||
Celle-ci doit, autant que faire se peut, rester impeccable.
|
||||
|
||||
`taiste` est la branche de référence pour le développement.
|
||||
Cette dernière est régulièrement déployée sur le
|
||||
site de test.
|
||||
Elle permet de s'assurer que les diverses modifications
|
||||
fonctionnent bien entre elles et fonctionnent bien
|
||||
sur le serveur, avant d'être envoyées sur master.
|
||||
|
||||
### Gestion des branches
|
||||
|
||||
Toutes les modifications appliquées sur `taiste`
|
||||
doivent se faire via des Pull Requests
|
||||
depuis les différentes branches de développement.
|
||||
Toutes les modifications appliquées sur `master`
|
||||
doivent se faire via des Pull Requests
|
||||
depuis `taiste`, ou bien depuis une branche
|
||||
de *hotfix*, dans le cas où il faut réparer
|
||||
un bug urgent apparu de manière impromptue.
|
||||
|
||||
Aucun `push` direct n'est admis, ni sur l'une,
|
||||
ni sur l'autre branche.
|
||||
|
||||
En obligeant à passer par des PR,
|
||||
on s'assure qu'au moins une autre personne
|
||||
aura lu votre code et que les outils de test
|
||||
et de vérification de code auront validé vos modifications.
|
||||
|
||||
Par extension du mode de travail par PR,
|
||||
les branches `master` et `taiste` ne peuvent
|
||||
recevoir du code que sous la forme de merge commits.
|
||||
|
||||
De plus, ces branches doivent recevoir, mais jamais donner
|
||||
(à part entre elles).
|
||||
Lorsqu'une modification a été effectuée sur `taiste`
|
||||
et que vous souhaitez la récupérer dans une de vos
|
||||
branches, vous devez procéder par `rebase`,
|
||||
et non par `merge`.
|
||||
|
||||
En d'autres termes, vous devez respecter les deux règles suivantes :
|
||||
|
||||
1. Les branches `master` et `taiste` ne doivent contenir que des merge commits
|
||||
2. Seules les branches `master` et `taiste` peuvent contenir des merge commits
|
||||
|
||||
=== "Bien ✔️"
|
||||
|
||||
```mermaid
|
||||
gitGraph:
|
||||
commit id: "initial commit"
|
||||
branch bar
|
||||
checkout main
|
||||
checkout bar
|
||||
commit id: "baz"
|
||||
checkout main
|
||||
merge bar id: "Merge branch bar"
|
||||
branch foo
|
||||
commit id: "foo a"
|
||||
commit id: "foo b"
|
||||
commit id: "foo c"
|
||||
checkout main
|
||||
merge foo id: "Merge branch foo"
|
||||
```
|
||||
|
||||
=== "Pas bien ❌"
|
||||
|
||||
```mermaid
|
||||
gitGraph:
|
||||
commit
|
||||
branch bar
|
||||
branch foo
|
||||
commit id: "foo a"
|
||||
commit id: "foo b"
|
||||
checkout main
|
||||
checkout bar
|
||||
commit id: "baz"
|
||||
checkout main
|
||||
merge bar id: "Merge branch bar"
|
||||
checkout foo
|
||||
merge main id: "Merge branch main"
|
||||
commit id: "foo c"
|
||||
checkout main
|
||||
merge foo id: "Merge branch foo"
|
||||
```
|
||||
|
||||
## Style de code
|
||||
|
||||
### Conventions de nommage
|
||||
|
||||
Les conventions de nommage sont celles de la
|
||||
[PEP8](https://peps.python.org/pep-0008/) :
|
||||
|
||||
- les classes sont en PascalCase (ex: `class SacredGraal`)
|
||||
- les constantes sont en MACRO_CASE (ex: `FAVOURITE_COLOUR = "blue"`)
|
||||
- les fonctions et les variables sont en snake_case (ex: `swallow_origin = "african"`)
|
||||
- les fichiers et dossiers contenant du code sont en snake_case
|
||||
- les fichiers et les dossiers contenant de la documentation sont en kebab-case
|
||||
|
||||
En parallèle de la casse, les règles
|
||||
de formatage du code sont celles du formateur Ruff.
|
||||
Ces règles sont automatiquement appliquées quand
|
||||
vous faites tourner Ruff, donc vous n'avez pas à trop
|
||||
vous poser de questions de ce côté-là.
|
||||
|
||||
En ce qui concerne les autres langages utilisés
|
||||
(Jinja, SCSS, Javascript), nous n'avons pas fixé
|
||||
de convention à suivre.
|
||||
Pour SCSS et Javascript, vous pouvez utiliser
|
||||
Prettier, avec sa configuration par défaut,
|
||||
qui est plutôt bonne.
|
||||
|
||||
### Qualité du code
|
||||
|
||||
Pour s'assurer de la qualité du code, Ruff est également utilisé.
|
||||
|
||||
Tout comme pour le format, Ruff doit tourner avant chaque commit.
|
||||
|
||||
!!!note "to edit or not to edit"
|
||||
|
||||
Vous constaterez sans doute que `ruff format` modifie votre code,
|
||||
mais que `ruff check` vous signale juste une liste
|
||||
d'erreurs sans rien modifier.
|
||||
|
||||
En effet, `ruff format` ne s'occupe que de la forme du code,
|
||||
alors que `ruff check` regarde la logique du code.
|
||||
Si Ruff modifiait automatiquement la logique du code,
|
||||
ça serait un coup à introduire plus de bugs que ça n'en résoud.
|
||||
|
||||
Il existe cependant certaines catégories d'erreurs que Ruff
|
||||
peut réparer de manière sûre.
|
||||
Pour appliquer ces réparations, faites :
|
||||
|
||||
```bash
|
||||
ruff check --fix
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
La documentation est écrite en markdown, avec les fonctionnalités
|
||||
offertes par MkDocs, MkDocs-material et leurs extensions.
|
||||
|
||||
La documentation est intégralement en français, à l'exception
|
||||
des exemples, qui suivent les conventions données plus haut.
|
||||
|
||||
### Découpage
|
||||
|
||||
La séparation entre les différentes parties de la documentation se fait
|
||||
en suivant la méthodologie [Diataxis](https://diataxis.fr/).
|
||||
On compte quatre sections :
|
||||
|
||||
1. Explications : parlez dans cette section de ce qui est bon à savoir
|
||||
sans que ça touche aux détails précis de l'implémentation.
|
||||
Si vous parlez de *pourquoi* un choix a été fait ou que vous montrez
|
||||
grossièrement les contours d'une partie du projet, c'est une explication.
|
||||
2. Tutoriels : parlez dans cette section d'étapes précises
|
||||
ou de détails d'implémentation qu'un nouveau développeur
|
||||
doit suivre pour commencer à travailler sur le projet.
|
||||
3. Utilisation : parlez dans cette section de méthodes utiles
|
||||
pour un développeur qui a déjà pris en main le projet.
|
||||
Voyez cette partie comme un livre de recettes de cuisine.
|
||||
4. Référence : parlez dans cette section des détails d'implémentation du projet.
|
||||
En réalité, vous n'aurez pas besoin de beaucoup vous pencher dessus,
|
||||
puisque cette partie est composée presque uniquement
|
||||
des docstrings présents dans le code.
|
||||
|
||||
Pour plus de détails, lisez directement la documentation de Diataxis,
|
||||
qui expose ces concepts de manière beaucoup plus complète.
|
||||
|
||||
### Style
|
||||
|
||||
Votre markdown doit être composé de lignes courtes ;
|
||||
à partir de 88 caractères, c'est trop long.
|
||||
Si une phrase est trop longue pour tenir sur une ligne,
|
||||
vous pouvez l'écrire sur plusieurs.
|
||||
|
||||
Une ligne ne peut pas contenir plus d'une seule phrase.
|
||||
Dit autrement, quand vous finissez une phrase,
|
||||
faites systématiquement un saut de ligne.
|
||||
|
||||
=== "Bien ✔️"
|
||||
|
||||
```markdown linenums="1"
|
||||
First shalt thou take out the Holy Pin,
|
||||
then shalt thou count to three, no more, no less.
|
||||
Three shalt be the number thou shalt count,
|
||||
and the number of the counting shalt be three.
|
||||
Four shalt thou not count, neither count thou two,
|
||||
excepting that thou then proceed to three.
|
||||
Five is right out.
|
||||
Once the number three, being the third number, be reached,
|
||||
then lobbest thou thy Holy Hand Grenade of Antioch towards thou foe,
|
||||
who being naughty in My sight, shall snuff it.
|
||||
```
|
||||
|
||||
=== "Pas bien ❌"
|
||||
|
||||
```markdown linenums="1"
|
||||
First shalt thou take out the Holy Pin, then shalt thou count to three, no more, no less. Three shalt be the number thou shalt count, and the number of the counting shalt be three. Four shalt thou not count, neither count thou two, excepting that thou then proceed to three. Five is right out. Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch towards thou foe, who being naughty in My sight, shall snuff it.
|
||||
```
|
||||
|
||||
À noter que ces deux exemples donnent le même résultat
|
||||
dans la documentation générée.
|
||||
Mais la version avec de courtes lignes est beaucoup
|
||||
plus facile à modifier et à versioner.
|
||||
|
||||
!!!warning "Grammaire et orthographe"
|
||||
|
||||
Ca peut paraitre évident dit comme ça, mais c'est toujours bon à rappeler :
|
||||
évitez de faire des fautes de français.
|
||||
Relisez vous quand vous avez fini d'écrire.
|
||||
|
||||
|
||||
### Docstrings
|
||||
|
||||
Les docstrings sont écrits en suivant la norme
|
||||
[Google](https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
|
||||
et les fonctionnalités de [Griffe](https://mkdocstrings.github.io/griffe/docstrings/).
|
||||
|
||||
Ils doivent être explicites sur ce que la fonction accomplit,
|
||||
mais ne pas parler de comment elle le fait.
|
||||
Un bon docstring est celui qui dit exactement
|
||||
ce qu'il faut pour qu'on puisse savoir comment
|
||||
utiliser la fonction ou la classe documentée sans avoir à lire son code.
|
||||
|
||||
Tout comme les pédales d'une voiture :
|
||||
pour pouvoir conduire, vous avez juste besoin
|
||||
de savoir ce qui se passe quand vous appuyez dessus.
|
||||
La connaissance de la mécanique interne est inutile dans ce cadre.
|
||||
|
||||
N'hésitez pas à mettre des examples dans vos docstrings.
|
||||
|
||||
## Pourquoi une partie du projet ne respecte pas ces conventions ?
|
||||
|
||||
Parce que le projet est vieux.
|
||||
Le commit initial date du 18 novembre 2015.
|
||||
C'était il y a presque dix ans au moment
|
||||
où ces lignes sont écrites.
|
||||
Au début, on ne se posait pas forcément
|
||||
ce genre de questions.
|
||||
Puis le projet a grandi, de manière sédimentaire,
|
||||
fonctionnalité après fonctionnalité,
|
||||
développé par des personnes n'ayant pas toutes la
|
||||
même esthétique.
|
||||
|
||||
On retrouve dans le code ces inspirations diverses
|
||||
de personnes variées à travers une décennie.
|
||||
Au bout d'un moment, il est bon de se poser
|
||||
et de normaliser les choses.
|
||||
|
||||
De ce côté-là, une première pierre a été posée
|
||||
en novembre 2018, avec l'utilisation d'un formateur.
|
||||
Il convient de poursuivre ce travail d'unification.
|
||||
|
||||
Cependant, là où on peut reformater automatiquement
|
||||
du code, il faut y aller à la main pour retravailler
|
||||
un style de code.
|
||||
C'est un travail de fourmi qui prendra du temps.
|
101
docs/explanation/index.md
Normal file
101
docs/explanation/index.md
Normal file
@ -0,0 +1,101 @@
|
||||
## Objectifs
|
||||
|
||||
Le but de ce projet est de fournir à
|
||||
l'Association des Étudiants de l'UTBM
|
||||
une plate-forme pratique et centralisée de ses services.
|
||||
Le Sith de l'AE tient à jour le registre des cotisations
|
||||
à l'association, prend en charge la trésorerie,
|
||||
les ventes de produits et services,
|
||||
la diffusion d’événements,
|
||||
la gestion de la laverie et bien plus encore.
|
||||
|
||||
C'est un projet bénévole qui tire ses origines des années 2000.
|
||||
Il s'agit de la troisième version du site de l'AE.
|
||||
Son développement a commencé en 2015.
|
||||
C'est une réécriture complète en rupture totale
|
||||
des deux versions qui l'ont précédée.
|
||||
|
||||
## Pourquoi réécrire le site
|
||||
|
||||
L'ancienne version du site, sobrement baptisée
|
||||
[ae2](https://github.com/ae-utbm/sith2),
|
||||
présentait un nombre impressionnant de fonctionnalités.
|
||||
Il avait été écrit en PHP et se basait
|
||||
sur son propre framework maison.
|
||||
|
||||
Malheureusement, son entretien était plus ou
|
||||
moins hasardeux et son framework reposait
|
||||
sur des principes assez différents de ce qui se fait
|
||||
aujourd'hui, rendant la maintenance difficile.
|
||||
De plus, la version de PHP qu'il utilisait
|
||||
était plus que dépréciée et à l'heure de l'arrivée de PHP 7
|
||||
et de sa non-rétrocompatibilité il était vital de faire
|
||||
quelque chose.
|
||||
Il a donc été décidé de le réécrire.
|
||||
|
||||
## La philosophie initiale
|
||||
|
||||
Pour éviter les erreurs du passé,
|
||||
ce projet met l'accent sur la maintenabilité.
|
||||
Le choix des technologies ne s'est donc pas
|
||||
fait uniquement sur le fait qu'elle soit récentes,
|
||||
mais également sur leur robustesse,
|
||||
leur fiabilité et leur potentiel à être maintenu
|
||||
loin dans le futur.
|
||||
|
||||
La maintenabilité passe également par le
|
||||
choix minutieux des dépendances qui doivent,
|
||||
elles aussi, passer l'épreuve du temps
|
||||
pour éviter qu'elles ne mettent le projet en danger.
|
||||
|
||||
Cela passe également par la minimisation
|
||||
des frameworks employés de manière à réduire un maximum
|
||||
les connaissances nécessaires pour contribuer
|
||||
au projet et donc simplifier la prise en main.
|
||||
La simplicité est à privilégier si elle est possible.
|
||||
|
||||
Le projet doit être simple à installer et à déployer.
|
||||
|
||||
Le projet étant à destination d'étudiants,
|
||||
il est préférable de minimiser les ressources
|
||||
utilisées par l'utilisateur final.
|
||||
Il faut qu'il soit au maximum économe en bande
|
||||
passante et calcul côté client.
|
||||
|
||||
Le projet est un logiciel libre et est sous licence GPL.
|
||||
Aucune dépendance propriétaire n'est acceptée.
|
||||
|
||||
## La philosophie, 10 ans plus tard
|
||||
|
||||
Malgré la bonne volonté et le travail colossal
|
||||
fourni par les developpeurs de la version actuelle
|
||||
du projet, force est de constater que nombre d'erreurs
|
||||
ont malheureusement été commises :
|
||||
usage complexe et excessif de certains mécanismes OO,
|
||||
réécriture maison de fonctionnalités de Django,
|
||||
système de gestion des permissions rigide et coûteux
|
||||
en requête à la base de données...
|
||||
|
||||
Mais malgré tout ça, le site tourne.
|
||||
En effet, force est de constater que le pari initial
|
||||
de choisir un framework stable et durable a payé.
|
||||
Aujourd'hui encore, Django est activement maintenu,
|
||||
ses mises à jour sont régulières sans pour autant
|
||||
nécessiter beaucoup de changements lors des changements
|
||||
de version majeure.
|
||||
|
||||
Quant aux erreurs qui ont été commises,
|
||||
que celui qui n'a jamais reconsidéré a posteriori
|
||||
que ce qui lui semblait une bonne architecture
|
||||
était en fait un ensemble brinquebalant,
|
||||
leur jette la première pierre.
|
||||
|
||||
La solidité des fondations ayant été prouvée
|
||||
par l'épreuve du temps,
|
||||
le travail restant à accomplir n'est
|
||||
pas de réécrire encore une fois le site
|
||||
en utilisant encore d'autres technologies,
|
||||
mais plutôt de raboter les surcouches du site,
|
||||
pour refixer le plus solidement possiblement
|
||||
le projet sur ces fondations.
|
||||
|
355
docs/explanation/technos.md
Normal file
355
docs/explanation/technos.md
Normal file
@ -0,0 +1,355 @@
|
||||
Bien choisir ses technologies est crucial
|
||||
puisqu'une fois que le projet est suffisamment avancé,
|
||||
il est très difficile voir impossible de revenir en arrière.
|
||||
|
||||
En novembre 2015, plusieurs choix se présentaient :
|
||||
|
||||
- Continuer avec du PHP
|
||||
- S'orienter vers un langage web
|
||||
plus moderne et à la mode comme le Python ou le Ruby
|
||||
- Baser le site sur un framework Javascript
|
||||
|
||||
Le PHP 5, bientôt 7, de l'époque
|
||||
étant assez discutable comme
|
||||
[cet article le montre](https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/),
|
||||
et l'ancien site ayant laissé un goût amer
|
||||
à certains développeurs, celui-ci a été mis de côté.
|
||||
|
||||
L'écosystème Javascript étant à peine naissant
|
||||
et les frameworks allant et venant en seulement quelques mois,
|
||||
il était impossible de prédire avec certitude
|
||||
si ceux-ci passeraient l'épreuve du temps,
|
||||
il était inconcevable de tout parier là-dessus.
|
||||
|
||||
Ne restait plus que le Python et le Ruby
|
||||
avec les frameworks Django et Ruby On Rails.
|
||||
Ruby ayant une réputation d'être très "cutting edge",
|
||||
c'est Python, un langage bien implanté
|
||||
et ayant fait ses preuves, qui a été retenu.
|
||||
|
||||
Il est à noter que réécrire le site avec un framework PHP
|
||||
comme Laravel ou Symphony eut aussi été possible,
|
||||
ces deux technologies étant assez matures et robustes
|
||||
au moment où le développement a commencé.
|
||||
Cependant, il aurait été potentiellement
|
||||
fastidieux de maintenir en parallèle deux
|
||||
versions de PHP sur le serveur durant toute
|
||||
la durée du développement.
|
||||
Il faut aussi prendre en compte que nous étions
|
||||
à ce moment dégoûtés du PHP.
|
||||
|
||||
## Backend
|
||||
|
||||
### Python 3
|
||||
|
||||
[Site officiel](https://www.python.org/)
|
||||
|
||||
Le python est un langage de programmation
|
||||
interprété multi paradigme sorti en 1991.
|
||||
Il est très populaire dans de nombreux domaines
|
||||
pour sa simplicité d'utilisation,
|
||||
sa polyvalence, sa sécurité ainsi
|
||||
que sa grande communauté de développeur.
|
||||
Sa version 3, non rétro compatible avec sa version 2,
|
||||
a été publiée en 2008.
|
||||
|
||||
!!!note
|
||||
|
||||
Puisque toutes les dépendances du backend sont
|
||||
des packages Python,
|
||||
elles sont toutes ajoutées directement
|
||||
dans le fichier **pyproject.toml**,
|
||||
à la racine du projet.
|
||||
|
||||
### Django
|
||||
|
||||
[Site officiel](https://www.djangoproject.com/)
|
||||
|
||||
[Documentation](https://docs.djangoproject.com/en/latest/)
|
||||
|
||||
Django est un framework web pour Python apparu en 2005.
|
||||
Il fournit un grand nombre de fonctionnalités
|
||||
pour développer un site rapidement et simplement.
|
||||
Cela inclut entre autre un serveur Web de développement,
|
||||
un parseur d'URLs pour le routage,
|
||||
un ORM (Object-Relational Mapper)
|
||||
pour la gestion de la base de donnée,
|
||||
un cadre pour l'écriture et l'exécution des tests,
|
||||
de nombreux utilitaires pour l'internationalisation,
|
||||
les zones horaires et autres,
|
||||
une interface web d'administration aisément configurable,
|
||||
un moteur de templates pour le rendu HTML...
|
||||
|
||||
Django propose une version LTS (Long Term Support)
|
||||
qui reste stable et est maintenu sur des cycles plus longs.
|
||||
Ce sont ces versions qui sont utilisées.
|
||||
|
||||
|
||||
### PostgreSQL / SQLite3
|
||||
|
||||
[Site officiel PostgreSQL](https://www.postgresql.org/)
|
||||
|
||||
[Site officiel SQLite](https://www.sqlite.org/index.html>)
|
||||
|
||||
Comme la majorité des sites internet,
|
||||
le Sith de l'AE enregistre ses données
|
||||
dans une base de données.
|
||||
Nous utilisons une base de donnée relationnelle
|
||||
puisque c'est la manière typique d'utiliser
|
||||
Django et c'est ce qu'utilise son ORM.
|
||||
|
||||
Le principal à retenir ici est :
|
||||
|
||||
- Sur la version de production, nous utilisons PostgreSQL,
|
||||
c'est cette version qui doit fonctionner en priorité.
|
||||
C'est un système de gestion de base de données fiable,
|
||||
puissant, activement maintenu, et particulièrement
|
||||
bien supporté par Django.
|
||||
- Sur les versions de développement,
|
||||
pour faciliter l'installation du projet,
|
||||
nous utilisons la technologie SQLite3
|
||||
qui ne requiert aucune installation spécifique
|
||||
(la librairie `sqlite` est incluse dans les
|
||||
librairies par défaut de Python).
|
||||
Certaines instructions ne sont pas supportées
|
||||
par cette technologie et il est parfois nécessaire
|
||||
d'installer PostgreSQL pour le développement
|
||||
de certaines parties du site
|
||||
(cependant, ces parties sont rares, et vous pourriez
|
||||
même ne jamais en rencontrer une).
|
||||
|
||||
PostgreSQL est-ce avec quoi le site doit fonctionner.
|
||||
Cependant, pour permettre aux développeurs
|
||||
de travailler en installant le moins de dépendances
|
||||
possible sur leur ordinateur,
|
||||
il est également désirable de chercher la compatibilité
|
||||
avec SQLite.
|
||||
Aussi, dans la mesure du possible, nous
|
||||
cherchons à ce que les interactions avec la base
|
||||
de données fonctionnent avec l'un comme avec l'autre.
|
||||
|
||||
Heureusement, et grâce à l'ORM de Django, cette
|
||||
double compatibilité est presque toujours possible.
|
||||
|
||||
## Frontend
|
||||
|
||||
### Jinja2
|
||||
|
||||
[Site officiel](https://jinja.palletsprojects.com/en/latest/)
|
||||
|
||||
Jinja2 est un moteur de template écrit en Python
|
||||
qui s'inspire fortement de la syntaxe des templates de Django.
|
||||
Ce moteur apporte toutefois son lot d'améliorations non négligeables.
|
||||
Il permet par exemple l'ajout de macros,
|
||||
sortes de fonctions écrivant du HTML.
|
||||
|
||||
Un moteur de templates permet de générer
|
||||
du contenu textuel de manière procédural
|
||||
en fonction des données à afficher,
|
||||
cela permet de pouvoir inclure du code proche du Python
|
||||
dans la syntaxe au milieu d'un document
|
||||
contenant principalement du HTML.
|
||||
On peut facilement faire des boucles ou des conditions
|
||||
ainsi même que de l'héritage de templates.
|
||||
|
||||
!!!note
|
||||
le rendu est fait côté serveur,
|
||||
si on souhaite faire des modifications côté client,
|
||||
il faut utiliser du Javascript, rien ne change à ce niveau-là.
|
||||
|
||||
### jQuery
|
||||
|
||||
[Site officiel](https://jquery.com/)
|
||||
|
||||
jQuery est une bibliothèque JavaScript
|
||||
libre et multiplateforme créée pour faciliter
|
||||
l'écriture de scripts côté client
|
||||
dans le code HTML des pages web.
|
||||
La première version est lancée en janvier 2006 par John Resig.
|
||||
|
||||
C'est une vieille technologie et certains
|
||||
feront remarquer à juste titre que le Javascript
|
||||
moderne permet d'utiliser assez simplement
|
||||
la majorité de ce que fournit jQuery
|
||||
sans rien avoir à installer.
|
||||
Cependant, de nombreuses dépendances du projet
|
||||
utilisent encore jQuery qui est toujours
|
||||
très implanté aujourd'hui.
|
||||
Le sucre syntaxique qu'offre cette librairie
|
||||
reste très agréable à utiliser et économise
|
||||
parfois beaucoup de temps.
|
||||
Ça fonctionne et ça fonctionne très bien.
|
||||
C'est maintenu et pratique.
|
||||
|
||||
|
||||
### AlpineJS
|
||||
|
||||
[Site officiel](https://alpinejs.dev/)
|
||||
|
||||
AlpineJS est une librairie légère et minimaliste
|
||||
permettant le rendu dynamique d'éléments sur une page
|
||||
web, code de manière déclarative.
|
||||
La librairie est décrite par ses créateurs comme :
|
||||
"un outil robuste et minimal pour composer un comportement directement dans vos balises".
|
||||
|
||||
Alpine permet d'accomplir la plupart du temps le même résultat qu'un usage des fonctionnalités
|
||||
de base des plus gros frameworks Javascript,
|
||||
mais est beaucoup plus léger, un peu plus facile à prendre en main
|
||||
et ne s'embarrasse pas d'un DOM virtuel.
|
||||
Grâce à son architecture, il est extrêmement
|
||||
bien adapté pour un usage dans un site multipage.
|
||||
C'est une technologie simple et puissante qui se veut comme le jQuery du web moderne.
|
||||
|
||||
### Sass
|
||||
|
||||
[Site officiel](https://sass-lang.com/)
|
||||
|
||||
Sass (Syntactically Awesome Stylesheets) est un langage dynamique
|
||||
de génération de feuilles CSS apparu en 2006.
|
||||
C'est un langage de CSS "amélioré" qui permet
|
||||
l'ajout de variables
|
||||
(à une époque où le CSS ne les supportait pas),
|
||||
de fonctions, mixins ainsi qu'une syntaxe pour
|
||||
imbriquer plus facilement et proprement les règles
|
||||
sur certains éléments.
|
||||
Le Sass est traduit en CSS directement côté serveur
|
||||
et le client ne reçoit que du CSS.
|
||||
|
||||
C'est une technologie stable,
|
||||
mature et pratique qui ne nécessite pas énormément
|
||||
d'apprentissage.
|
||||
|
||||
### Fontawesome
|
||||
|
||||
[Site officiel](https://fontawesome.com>)
|
||||
|
||||
Fontawesome regroupe tout un ensemble
|
||||
d'icônes libres de droits utilisables facilement
|
||||
sur n'importe quelle page web.
|
||||
Ils sont simples à modifier puisque modifiables
|
||||
via le CSS et présentent l'avantage de fonctionner
|
||||
sur tous les navigateurs contrairement
|
||||
à un simple icône unicode qui s'affiche
|
||||
lui différemment selon la plate-forme.
|
||||
|
||||
!!!note
|
||||
|
||||
C'est une dépendance capricieuse qui évolue très vite
|
||||
et qu'il faut très souvent mettre à jour.
|
||||
|
||||
!!!warning
|
||||
|
||||
Il a été décidé de **ne pas utiliser**
|
||||
de CDN puisque le site ralentissait régulièrement.
|
||||
Il est préférable de fournir cette dépendance avec le site.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Git
|
||||
|
||||
[Site officiel](https://git-scm.com/)
|
||||
|
||||
Git est un logiciel de gestion de versions écrit par
|
||||
Linus Torvalds pour les besoins du noyau linux en 2005.
|
||||
C'est ce logiciel qui remplace svn anciennement
|
||||
utilisé pour gérer les sources du projet
|
||||
(rappelez vous, l'ancien site date d'avant 2005).
|
||||
Git est plus complexe à utiliser,
|
||||
mais est bien plus puissant,
|
||||
permet de gérer plusieurs versions en parallèle
|
||||
et génère des codebases vraiment plus légères puisque
|
||||
seules les modifications sont enregistrées
|
||||
(contrairement à svn qui garde une copie
|
||||
de la codebase par version).
|
||||
|
||||
Git s'étant imposé comme le principal outil de gestion de versions,
|
||||
sa communauté est très grande et sa documentation très fournie.
|
||||
Il est également aisé de trouver des outils avec une interface graphique,
|
||||
qui simplifient grandement son usage.
|
||||
|
||||
### GitHub
|
||||
|
||||
[Site officiel](https://github.com)
|
||||
|
||||
[Page github du Pôle Informatique de l'AE](https://github.com/ae-utbm/)
|
||||
|
||||
Github est un service web d'hébergement et de gestion de développement de logiciel.
|
||||
C'est une plate-forme avec interface web permettant de déposer du code géré avec Git
|
||||
offrant également de l'intégration continue et du déploiement automatique.
|
||||
C'est au travers de cette plate-forme que le Sith de l'AE est géré.
|
||||
|
||||
### Sentry
|
||||
|
||||
[Site officiel](https://sentry.io)
|
||||
|
||||
[Instance de l'AE](https://ae2.utbm.fr)
|
||||
|
||||
Sentry est une plate-forme libre qui permet
|
||||
de se tenir informé des bugs qui ont lieu sur le site.
|
||||
À chaque crash du logiciel (erreur 500),
|
||||
une erreur est envoyée sur la plate-forme
|
||||
et il est indiqué précisément à quelle ligne de code
|
||||
celle-ci a eu lieu, à quelle heure, combien de fois,
|
||||
avec quel navigateur la page a été visitée et même éventuellement
|
||||
un commentaire de l'utilisateur qui a rencontré le bug.
|
||||
|
||||
C'est un outil incroyablement pratique
|
||||
pour savoir tout ce qui ne fonctionne pas,
|
||||
et surtout pour récolter toutes les informations
|
||||
nécessaires à la réparation des bugs.
|
||||
|
||||
### Poetry
|
||||
|
||||
[Utiliser Poetry](https://python-poetry.org/docs/basic-usage/)
|
||||
|
||||
Poetry est un utilitaire qui permet de créer et gérer
|
||||
des environnements Python de manière simple et intuitive.
|
||||
Il permet également de gérer et mettre à jour
|
||||
le fichier de dépendances.
|
||||
|
||||
L'avantage d'utiliser poetry
|
||||
(et les environnements virtuels en général)
|
||||
est de pouvoir gérer plusieurs projets différents
|
||||
en parallèle puisqu'il permet d'avoir sur sa
|
||||
machine plusieurs environnements différents et
|
||||
donc plusieurs versions d'une même dépendance
|
||||
dans plusieurs projets différents sans impacter
|
||||
le système sur lequel le tout est installé.
|
||||
|
||||
Poetry possède également l'avantage par rapport à un simple venv
|
||||
que les versions exactes de toutes les dépendances,
|
||||
y compris celles utilisées par d'autres dépendances,
|
||||
sont consignées dans un fichier `.lock`.
|
||||
On est donc sûr et certain que deux environnements virtuels
|
||||
configurés avec le même fichier lock utiliseront
|
||||
exactement les mêmes versions des mêmes dépendances,
|
||||
y compris si celles-ci ne sont pas indiquées explicitement.
|
||||
|
||||
Les dépendances utilisées par poetry sont déclarées
|
||||
dans le fichier `pyproject.toml`,
|
||||
situé à la racine du projet.
|
||||
|
||||
### Ruff
|
||||
|
||||
[Site officiel](https://astral.sh/ruff)
|
||||
|
||||
Pour faciliter la lecture du code,
|
||||
il est toujours appréciable d'avoir
|
||||
une norme d'écriture cohérente.
|
||||
C'est généralement à l'étape de relecture
|
||||
des modifications par les autres contributeurs
|
||||
que sont repérées ces fautes de normes
|
||||
qui se doivent d'être corrigées pour le bien commun.
|
||||
|
||||
Imposer une norme est très fastidieux,
|
||||
que ce soit pour ceux qui relisent
|
||||
ou pour ceux qui écrivent.
|
||||
C'est pour cela que nous utilisons Ruff,
|
||||
qui est un formateur et linter automatique de code.
|
||||
Une fois l'outil lancé, il parcourt la codebase
|
||||
pour y repérer les fautes de norme et les erreurs de logique courantes
|
||||
et les corrige automatiquement (quand c'est possible)
|
||||
sans que l'utilisateur ait à s'en soucier.
|
||||
Bien installé, il peut effectuer ce travail
|
||||
à chaque sauvegarde d'un fichier dans son éditeur,
|
||||
ce qui est très agréable pour travailler.
|
59
docs/howto/direnv.md
Normal file
59
docs/howto/direnv.md
Normal file
@ -0,0 +1,59 @@
|
||||
Pour éviter d'avoir à sourcer l'environnement
|
||||
à chaque fois qu'on rentre dans le projet,
|
||||
il est possible d'utiliser l'utilitaire [direnv](https://direnv.net/).
|
||||
|
||||
Comme pour beaucoup de choses, il faut commencer par l'installer :
|
||||
|
||||
=== "Linux"
|
||||
|
||||
=== "Debian/Ubuntu"
|
||||
|
||||
```bash
|
||||
sudo apt install direnv
|
||||
```
|
||||
|
||||
=== "Arch Linux"
|
||||
|
||||
```bash
|
||||
sudo pacman -S direnv
|
||||
```
|
||||
|
||||
=== "macOS"
|
||||
|
||||
```bash
|
||||
brew install direnv
|
||||
```
|
||||
|
||||
Puis on configure :
|
||||
|
||||
=== "bash"
|
||||
|
||||
```bash
|
||||
echo 'eval "$(direnv hook bash)"' >> ~/.bashrc
|
||||
exit # On redémarre le terminal
|
||||
```
|
||||
|
||||
=== "zsh"
|
||||
|
||||
```zsh
|
||||
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
|
||||
exit # On redémarre le terminal
|
||||
```
|
||||
|
||||
=== "nu"
|
||||
|
||||
Désolé, par `direnv hook` pour `nu`
|
||||
|
||||
Une fois le terminal redémarré, dans le répertoire du projet :
|
||||
```bash
|
||||
direnv allow .
|
||||
```
|
||||
|
||||
Une fois que cette configuration a été appliquée,
|
||||
aller dans le dossier du site applique automatiquement
|
||||
l'environnement virtuel.
|
||||
Ça peut faire gagner pas mal de temps.
|
||||
|
||||
Direnv est un utilitaire très puissant
|
||||
et qui peut s'avérer pratique dans bien des situations,
|
||||
n'hésitez pas à aller vous renseigner plus en détail sur celui-ci.
|
354
docs/howto/migrations.md
Normal file
354
docs/howto/migrations.md
Normal file
@ -0,0 +1,354 @@
|
||||
## Qu'est-ce qu'une migration ?
|
||||
|
||||
Une migration est un fichier Python qui contient
|
||||
des instructions pour modifier la base de données.
|
||||
Une base de données évolue au cours du temps,
|
||||
et les migrations permettent de garder une trace
|
||||
de ces modifications.
|
||||
|
||||
Grâce à elles, on peut également apporter des modifications
|
||||
à la base de données sans être obligées de la recréer.
|
||||
On applique seulement les modifications nécessaires.
|
||||
|
||||
## Appliquer les migrations
|
||||
|
||||
Pour appliquer les migrations, exécutez la commande suivante :
|
||||
|
||||
```bash
|
||||
python ./manage.py migrate
|
||||
```
|
||||
|
||||
Vous remarquerez peut-être que cette commande
|
||||
a été utilisée dans la section
|
||||
[Installation](../tutorial/install.md).
|
||||
En effet, en partant d'une base de données vierge
|
||||
et en appliquant toutes les migrations, on arrive
|
||||
à l'état actuel de la base de données.
|
||||
Logique.
|
||||
|
||||
Si vous utilisez cette commande sur une base de données
|
||||
sur laquelle toutes les migrations ont été appliquées,
|
||||
elle ne fera rien.
|
||||
|
||||
Si vous utilisez cette commande sur une base de données
|
||||
sur laquelle seule une partie des migrations ont été appliquées,
|
||||
seules les migrations manquantes seront appliquées.
|
||||
|
||||
## Créer une migration
|
||||
|
||||
Pour créer une migration, exécutez la commande suivante :
|
||||
|
||||
```bash
|
||||
python ./manage.py makemigrations
|
||||
```
|
||||
|
||||
Cette commande comparera automatiquement le contenu
|
||||
des classes de modèles et le comparera avec les
|
||||
migrations déjà appliquées.
|
||||
A partir de cette comparaison, elle générera
|
||||
automatiquement une nouvelle migration.
|
||||
|
||||
!!! note
|
||||
|
||||
La commande `makemigrations` ne fait que
|
||||
générer les fichiers de migration.
|
||||
Elle ne modifie pas la base de données.
|
||||
Pour appliquer la migration, n'oubliez pas la
|
||||
commande `migrate`.
|
||||
|
||||
Un fichier de migration ressemble à ça :
|
||||
|
||||
```python
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
# liste des autres migrations à appliquer avant celle-ci
|
||||
]
|
||||
|
||||
operations = [
|
||||
# liste des opérations à appliquer sur la db
|
||||
]
|
||||
```
|
||||
|
||||
Grâce à la liste des dépendances, Django sait dans
|
||||
quel ordre les migrations doivent être appliquées.
|
||||
Grâce à la liste des opérations, Django sait quelles
|
||||
sont les opérations à appliquer durant cette migration.
|
||||
|
||||
## Revenir à une migration antérieure
|
||||
|
||||
Lorsque vous développez, il peut arriver que vous vouliez
|
||||
revenir à une migration antérieure.
|
||||
Pour cela, il suffit d'appliquer la commande `migrate`
|
||||
en spécifiant le nom de la migration à laquelle vous
|
||||
voulez revenir :
|
||||
|
||||
```bash
|
||||
python ./manage.py migrate <application> <numéro de la migration>
|
||||
```
|
||||
|
||||
Par exemple, si vous voulez revenir à la migration `0001_initial`
|
||||
de l'application `customer`, vous pouvez exécuter la commande suivante :
|
||||
|
||||
```bash
|
||||
python ./manage.py migrate customer 0001
|
||||
```
|
||||
|
||||
## Customiser une migration
|
||||
|
||||
Il peut arriver que vous ayez besoin de modifier
|
||||
le fichier de migration généré par Django.
|
||||
Par exemple, si vous voulez exécuter un script Python
|
||||
lors de l'application de la migration.
|
||||
|
||||
Dans ce cas, vous pouvez trouver les fichiers de migration
|
||||
dans le dossier `migrations` de chaque application.
|
||||
Vous pouvez modifier le fichier Python correspondant
|
||||
à la migration que vous voulez modifier.
|
||||
|
||||
Ajoutez l'opération que vous voulez effectuer
|
||||
dans l'attribut `operations` de la classe `Migration`.
|
||||
|
||||
Par exemple :
|
||||
|
||||
```python
|
||||
from django.db import migrations
|
||||
|
||||
def forwards_func(apps, schema_editor):
|
||||
print("Appplication de la migration")
|
||||
|
||||
def reverse_func(apps, schema_editor):
|
||||
print("Annulation de la migration")
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forwards_func, reverse_func),
|
||||
]
|
||||
```
|
||||
|
||||
!!! warning "Script d'annulation de la migration"
|
||||
|
||||
Lorsque vous incluez un script Python dans une migration,
|
||||
incluez toujours aussi un script d'annulation,
|
||||
sinon Django ne pourra pas annuler la migration
|
||||
après son application.
|
||||
|
||||
Vous ne pourrez donc pas revenir à un état antérieur
|
||||
de la db, à moins de la recréer de zéro.
|
||||
|
||||
## Fusionner des migrations
|
||||
|
||||
Quand on travaille sur une fonctionnalité
|
||||
qui nécessite une modification de la base de données,
|
||||
les fichiers de migration sont comme toute chose :
|
||||
on peut se rendre compte que les changements
|
||||
apportés pourraient être meilleurs.
|
||||
|
||||
Par exemple, supposons que nous voulons créer un modèle
|
||||
représentant une UE suivie par un étudiant
|
||||
(ne demandez pas pourquoi on voudrait faire ça,
|
||||
c'est juste pour l'exemple).
|
||||
Un tel modèle aurait besoin des informations suivantes :
|
||||
|
||||
- l'utilisateur
|
||||
- le code de l'UE
|
||||
|
||||
On écrirait donc, dans l'application `pedagogy` :
|
||||
```python
|
||||
from django.db import models
|
||||
from core.models import User
|
||||
|
||||
class UserUe(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
ue = models.CharField(max_length=10)
|
||||
```
|
||||
|
||||
Et nous aurions le fichier de migration suivant :
|
||||
```python
|
||||
from django.db import migrations, models
|
||||
from django.conf import settings
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
("pedagogy", "0003_alter_uv_language"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="UserUe",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("ue", models.CharField(max_length=10)),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
On finit son travail, on soumet la PR.
|
||||
Mais là, quelqu'un fait remarquer qu'il existe déjà
|
||||
un modèle pour représenter une UE.
|
||||
On modifie donc le modèle :
|
||||
|
||||
```python
|
||||
from django.db import models
|
||||
from core.models import User
|
||||
from pedagogy.models import UV
|
||||
|
||||
class UserUe(models.Model):
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||
ue = models.ForeignKey(UV, on_delete=models.CASCADE)
|
||||
```
|
||||
|
||||
On refait la commande `makemigrations` et on obtient :
|
||||
```python
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("pedagogy", "0004_userue"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="userue",
|
||||
name="ue",
|
||||
field=models.ForeignKey(
|
||||
on_delete=models.deletion.CASCADE, to="pedagogy.uv"
|
||||
),
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
Sauf que maintenant, nous avons deux fichiers de migration,
|
||||
alors qu'en réalité, on ne souhaite faire qu'une seule migration,
|
||||
une fois qu'on aura expédié le code en prod.
|
||||
Certes, ça fonctionnerait d'appliquer les deux, mais ça pose
|
||||
un problème d'encombrement.
|
||||
|
||||
Plus il y a de fichiers de migrations, plus il y a de migrations
|
||||
à résoudre au moment de l'installation du projet chez quelqu'un,
|
||||
plus c'est embêtant à gérer et plus Django prendra du temps
|
||||
à résoudre les migrations.
|
||||
|
||||
C'est pourquoi il est bon de respecter le principe :
|
||||
une PR = un fichier de migration maximum par application.
|
||||
|
||||
Nous voulons donc fusionner les deux, pour n'en garder qu'une.
|
||||
Pour ça, deux manières de procéder :
|
||||
|
||||
- le faire à la main
|
||||
- utiliser la commande squashmigrations
|
||||
|
||||
Pour la méthode manuelle, on ne pourrait pas vous dire exhaustivement
|
||||
comment faire.
|
||||
Mais ne vous inquiétez pas, ce n'est pas très dur.
|
||||
Regardez bien quelles sont les instructions utilisées par django
|
||||
pour les opérations de migrations,
|
||||
et avec un peu d'astuce et quelques copier-coller,
|
||||
vous vous en sortirez comme des chefs.
|
||||
|
||||
Pour la méthode `squashmigrations`, exécutez la commande
|
||||
|
||||
```bash
|
||||
python ./manage.py squasmigrations <app> <migration de début (incluse)> <migration de fin (incluse)>
|
||||
```
|
||||
|
||||
Par exemple, dans notre cas, ça donnera :
|
||||
|
||||
```bash
|
||||
python ./manage.py squasmigrations pedagogy 0004 0005
|
||||
```
|
||||
|
||||
La commande vous donnera ceci :
|
||||
```python
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
replaces = [("pedagogy", "0004_userue"), ("pedagogy", "0005_alter_userue_ue")]
|
||||
|
||||
dependencies = [
|
||||
("pedagogy", "0003_alter_uv_language"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="UserUe",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"ue",
|
||||
models.ForeignKey(
|
||||
on_delete=models.deletion.CASCADE, to="pedagogy.uv"
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=models.deletion.CASCADE,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
Vous pouvez alors supprimer les deux autres fichiers.
|
||||
|
||||
Vous remarquerez peut-être la présence de la ligne suivante :
|
||||
```python
|
||||
replaces = [("pedagogy", "0004_userue"), ("pedagogy", "0005_alter_userue_ue")]
|
||||
```
|
||||
|
||||
Cela sert à dire que cette migration doit être appliquée
|
||||
à la place des deux autres.
|
||||
Une fois que vous aurez supprimé les deux fichiers,
|
||||
supprimez également cette ligne.
|
||||
|
||||
!!!warning
|
||||
|
||||
Django sait quelles migrations ont été appliquées,
|
||||
en les stockant dans une table de la db.
|
||||
Si une migration est enregistrée en db, sans que le fichier
|
||||
de migration correspondant existe,
|
||||
la commande `migrate` échoue.
|
||||
|
||||
Quand vous faites un `squashmigrations`,
|
||||
pensez donc à appliquer la commande `migrate`
|
||||
juste après (mais avant la suppression des anciens fichiers),
|
||||
pour que Django supprime de la base de données
|
||||
les migrations devenues inutiles.
|
27
docs/howto/prod.md
Normal file
27
docs/howto/prod.md
Normal file
@ -0,0 +1,27 @@
|
||||
## Configurer Sentry
|
||||
|
||||
Pour connecter l'application à une instance de sentry (ex: https://sentry.io),
|
||||
il est nécessaire de configurer la variable `SENTRY_DSN`
|
||||
dans le fichier `settings_custom.py`.
|
||||
Cette variable est composée d'un lien complet vers votre projet sentry.
|
||||
|
||||
## Récupérer les statiques
|
||||
|
||||
Nous utilisons du SCSS dans le projet.
|
||||
En environnement de développement (`DEBUG=True`),
|
||||
le SCSS est compilé à chaque fois que le fichier est demandé.
|
||||
Pour la production, le projet considère
|
||||
que chacun des fichiers est déjà compilé et,
|
||||
pour ce faire, il est nécessaire
|
||||
d'utiliser les commandes suivantes dans l'ordre :
|
||||
|
||||
```bash
|
||||
python ./manage.py collectstatic # Pour récupérer tous les fichiers statiques
|
||||
python ./manage.py compilestatic # Pour compiler les fichiers SCSS qu'ils contiennent
|
||||
```
|
||||
|
||||
!!!tip
|
||||
|
||||
Le dossier où seront enregistrés ces fichiers
|
||||
statiques peut être changé en modifiant la variable
|
||||
`STATIC_ROOT` dans les paramètres.
|
280
docs/howto/querysets.md
Normal file
280
docs/howto/querysets.md
Normal file
@ -0,0 +1,280 @@
|
||||
L'ORM de Django est puissant, très puissant, non par parce qu'il
|
||||
est performant (après tout, ce n'est qu'une interface, le gros du boulot,
|
||||
c'est la db qui le fait), mais parce qu'il permet d'écrire
|
||||
de manière relativement simple un grand panel de requêtes.
|
||||
|
||||
De manière générale, puisqu'un ORM est un système
|
||||
consistant à manipuler avec un code orienté-objet
|
||||
une db relationnelle (c'est-à-dire deux paradigmes
|
||||
qui ne fonctionnent absolument pas pareil),
|
||||
on rencontre un des deux problèmes suivants :
|
||||
|
||||
- soit l'ORM n'offre pas assez d'abstraction,
|
||||
auquel cas, quand on veut faire des requêtes
|
||||
plus complexes qu'un `select` avec un `where`,
|
||||
on s'emmêle les pinceaux et on se dit que
|
||||
ça aurait été plus simple de le faire directement
|
||||
en SQL.
|
||||
- soit l'ORM offre trop d'abstraction,
|
||||
auquel cas, on a tendance à ne pas prêter
|
||||
assez attention aux requêtes envoyées en base
|
||||
de données et on finit par se rendre compte
|
||||
que les temps d'attente explosent
|
||||
parce qu'on envoie trop de requêtes.
|
||||
|
||||
Django est dans ce deuxième cas.
|
||||
|
||||
C'est pourquoi nous ne parlerons pas ici
|
||||
de son fonctionnement exact ni de toutes les fonctions
|
||||
que l'on peut utiliser
|
||||
(la doc officielle fait déjà ça mieux que nous),
|
||||
mais plutôt des pièges courants
|
||||
et des astuces pour les éviter.
|
||||
|
||||
## Les `N+1 queries`
|
||||
|
||||
### Le problème
|
||||
|
||||
Normalement, quand on veut récupérer une liste,
|
||||
on fait une requête et c'est fini.
|
||||
Mais des fois, ça n'est pas si simple.
|
||||
Par exemple, supposons que nous voulons
|
||||
récupérer les 100 utilisateurs les plus riches,
|
||||
avec leurs informations client :
|
||||
|
||||
```python
|
||||
from core.models import User
|
||||
|
||||
for user in User.objects.order_by("-customer__amount")[:100]:
|
||||
print(user.customer.amount)
|
||||
```
|
||||
|
||||
Combien de requêtes le bout de code suivant effectue-t-il ?
|
||||
101\.
|
||||
En deux pauvres lignes de code, nous avons demandé
|
||||
à la base de données d'effectuer 101 requêtes.
|
||||
Une requête toute seule n'est déjà une opération anodine,
|
||||
alors je vous laisse imaginer ce que ça donne pour 101.
|
||||
|
||||
Si vous ne comprenez pourquoi ce nombre, c'est très simple :
|
||||
|
||||
- Une requête pour sélectionner nos 100 utilisateurs
|
||||
- Une requête supplémentaire pour récupérer les informations
|
||||
client de chaque utilisateur, soit 100 requêtes.
|
||||
|
||||
En effet, les informations client sont stockées dans une
|
||||
autre table, mais le fait d'établir un lien de clef
|
||||
étrangère permet de manipuler `customer`
|
||||
comme si c'était un membre à part entière de `User`.
|
||||
|
||||
Il est à noter cependant, que Django n'effectue une requête
|
||||
que pour le premier accès à un membre d'une relation
|
||||
de clef étrangère.
|
||||
Toutes les fois suivantes, l'objet est déjà là,
|
||||
et django le récupère :
|
||||
|
||||
```python
|
||||
from core.models import User
|
||||
|
||||
# l'utilisateur le plus riche
|
||||
user = User.objects.order_by("-customer__amount").first() # <-- requête db
|
||||
print(user.customer.amount) # <-- requête db
|
||||
print(user.customer.account_id) # on a déjà récupéré `customer`, donc pas de requête
|
||||
```
|
||||
|
||||
Ce n'est donc pas gravissime si vous faites cette
|
||||
erreur quand vous manipulez un seul objet.
|
||||
En revanche, quand vous en manipulez plusieurs,
|
||||
il faut régler le problème.
|
||||
Pour ça, il y a plusieurs méthodes, en fonction de votre cas.
|
||||
|
||||
### `select_related`
|
||||
|
||||
La méthode la plus basique consiste à annoter le queryset,
|
||||
avec la méthode `select_related()`.
|
||||
En faisant ça, Django fera une jointure sur l'autre table
|
||||
et demandera des informations en plus
|
||||
à la db lors de la requête.
|
||||
|
||||
De la sorte, lorsque vous appellerez le membre relié,
|
||||
les informations seront déjà là.
|
||||
|
||||
```python
|
||||
from core.models import User
|
||||
|
||||
richest = User.objects.order_by("-customer__amount")
|
||||
for user in richest.select_related("customer")[:100]:
|
||||
print(user.customer)
|
||||
```
|
||||
|
||||
Le code ci-dessus effectue une seule requête.
|
||||
Chaque fois qu'on veut accéder à `customer`, c'est bon,
|
||||
ça a déjà été récupéré à travers le `annotate`.
|
||||
|
||||
### `prefetch_related`
|
||||
|
||||
Maintenant, un cas plus compliqué.
|
||||
Supposons que vous ne vouliez pas récupérer des informations
|
||||
reliées par une relation One-to-One,
|
||||
mais par une relation One-to-Many.
|
||||
|
||||
Par exemple, un utilisateur a un seul compte client,
|
||||
mais il peut avoir plusieurs cotisations à son actif.
|
||||
Et dans ces cas-là, `annotate` ne marche plus.
|
||||
En effet, s'il peut exister plusieurs cotisations,
|
||||
comment savoir laquelle on veut ?
|
||||
|
||||
Il faut alors utiliser un `prefetch_related`.
|
||||
C'est un mécanisme un peu différent :
|
||||
au lieu de faire une jointure et d'ajouter les informations
|
||||
voulues dans la même requête, Django va effectuer
|
||||
une deuxième requête pour récupérer les éléments de l'autre table,
|
||||
puis, à partir de ces éléments, peupler la relation
|
||||
de son côté.
|
||||
|
||||
C'est un mécanisme qui peut être un peu coûteux en mémoire
|
||||
et qui demande une deuxième requête,
|
||||
mais qui reste quand même largement préférable
|
||||
à faire N requêtes en plus.
|
||||
|
||||
```python
|
||||
from core.models import User
|
||||
|
||||
for user in User.objects.prefetch_related("subscriptions")[:100]:
|
||||
# c'est bon, la méthode prefetch a récupéré en avance les `subscriptions`
|
||||
print(user.subscriptions.all())
|
||||
```
|
||||
|
||||
!!! danger
|
||||
|
||||
La méthode `prefetch_related` ne marche que si vous
|
||||
utilisez la méthode `all()` pour accéder au membre.
|
||||
Si vous utilisez une autre méthode (comme `filter` ou `annotate`),
|
||||
alors Django effectuera une nouvelle requête,
|
||||
et vous retomberez dans le problème initial.
|
||||
|
||||
```python
|
||||
from core.models import User
|
||||
from django.db.models import Count
|
||||
|
||||
for user in User.objects.prefetch_related("subscriptions")[:100]:
|
||||
# Le prefetch_related ne marche plus !
|
||||
print(user.subscriptions.annotate(count=Count("*")))
|
||||
```
|
||||
|
||||
### Récupérer ce dont vous avez besoin
|
||||
|
||||
Des fois (souvent, même), penser explicitement
|
||||
à la jointure est le meilleur choix.
|
||||
|
||||
En effet, vous remarquerez que dans tous
|
||||
les exemples précédents, nous n'utilisions
|
||||
qu'une partie des informations
|
||||
(par exemple, nous ne récupérions que la somme
|
||||
d'argent sur les comptes, et éventuellement le numéro de compte).
|
||||
|
||||
Nous pouvons utiliser la méthode `annotate`
|
||||
pour spécifier explicitement les données que l'on veut
|
||||
joindre à notre requête.
|
||||
|
||||
Quand nous voulions récupérer les informations utilisateur,
|
||||
nous aurions tout aussi bien pu écrire :
|
||||
|
||||
```python
|
||||
from core.models import User
|
||||
from django.db.models import F
|
||||
|
||||
richest = User.objects.order_by("-customer__amount")
|
||||
for user in richest.annotate(amount=F("customer__amount"))[:100]:
|
||||
print(user.amount)
|
||||
```
|
||||
|
||||
On aurait même pu réorganiser ça :
|
||||
```python
|
||||
|
||||
from core.models import User
|
||||
from django.db.models import F
|
||||
|
||||
richest = User.objects.annotate(amount=F("customer__amount")).order_by("-amount")
|
||||
for user in richest[:100]:
|
||||
print(user.amount)
|
||||
```
|
||||
|
||||
Ça peut sembler moins bien qu'un `select_related`, comme ça.
|
||||
Des fois, c'est en effet moins bien, et des fois c'est mieux.
|
||||
La comparaison est plus évidente avec le `prefetch_related`.
|
||||
|
||||
En effet, quand nous voulions récupérer
|
||||
le nombre de cotisations des utilisateurs,
|
||||
le `prefetch_related` ne marchait plus.
|
||||
Pourtant, nous voulions récupérer une seule information.
|
||||
|
||||
Il aurait donc été suffisant d'écrire :
|
||||
```python
|
||||
from core.models import User
|
||||
from django.db.models import Count
|
||||
|
||||
for user in User.objects.annotate(nb_subscriptions=Count("subscriptions"))[:100]:
|
||||
# Et là ça marche, en une seule requête.
|
||||
print(user.nb_subscriptions)
|
||||
```
|
||||
|
||||
Faire une jointure, c'est normal en SQL.
|
||||
Et pourtant avec Django on les oublie trop facilement.
|
||||
Posez-vous toujours la question des données que vous pourriez
|
||||
avoir besoin d'annoter, et vous éviterez beaucoup d'ennuis.
|
||||
|
||||
## Les aggrégations manquées
|
||||
|
||||
Il arrive souvent que l'on veuille une information qui
|
||||
porte sur un ensemble d'objets de notre db.
|
||||
|
||||
Imaginons par exemple que nous voulons connaitre
|
||||
la somme totale des ventes faites à un comptoir.
|
||||
|
||||
Nous avons tous suivi nos cours de programmation,
|
||||
nous écrivons donc instinctivement :
|
||||
|
||||
```python
|
||||
from counter.models import Counter
|
||||
|
||||
foyer = Counter.objects.get(name="Foyer")
|
||||
total_amount = sum(
|
||||
sale.amount * sale.unit_price
|
||||
for sale in foyer.sellings.all()
|
||||
)
|
||||
```
|
||||
|
||||
On pourrait penser qu'il n'y a pas de problème.
|
||||
Après tout, on ne fait qu'une seule requête.
|
||||
Eh bien si, il y a un problème :
|
||||
on fait beaucoup de choses en trop.
|
||||
|
||||
Concrètement, on demande à la base de données
|
||||
de renvoyer toutes les informations,
|
||||
ce qui rallonge inutilement la durée
|
||||
de l'échange entre le serveur et la db,
|
||||
puis on perd du temps à convertir ces informations
|
||||
en objets Python (opération qui a un coût également),
|
||||
et enfin on reperd du temps à calculer en Python
|
||||
quelque chose que la db aurait pu calculer
|
||||
à notre plus bien plus vite.
|
||||
|
||||
Nous aurions dû aggréger la requête,
|
||||
avec la méthode `aggregate` :
|
||||
|
||||
```python
|
||||
from counter.models import Counter
|
||||
from django.db.models import Sum, F
|
||||
|
||||
foyer = Counter.objects.get(name="Foyer")
|
||||
total_amount = (
|
||||
foyer.sellings.aggregate(amount=Sum(F("amount") * F("unit_price"), default=0))
|
||||
)["amount__sum"]
|
||||
```
|
||||
|
||||
En effectuant cette requête, la base de données nous renverra exactement
|
||||
l'information dont nous avons besoin.
|
||||
Et de notre côté, nous n'aurons pas à faire de traitement en plus.
|
||||
|
||||
|
46
docs/howto/subscriptions.md
Normal file
46
docs/howto/subscriptions.md
Normal file
@ -0,0 +1,46 @@
|
||||
## Ajouter une nouvelle cotisation
|
||||
|
||||
Il peut arriver que le type de cotisation
|
||||
proposé varie en prix et en durée.
|
||||
Ces paramètres sont configurables directement dans les paramètres du projet.
|
||||
|
||||
Pour modifier les cotisations disponibles,
|
||||
tout se gère dans la configuration avec la variable `SITH_SUBSCRIPTIONS`.
|
||||
|
||||
Par exemple, si nous voulons ajouter une nouvelle cotisation d'un mois,
|
||||
voici ce que nous ajouterons :
|
||||
|
||||
```python title="settings.py"
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
SITH_SUBSCRIPTIONS = {
|
||||
# Voici un échantillon de la véritable configuration à l'heure de l'écriture.
|
||||
# Celle-ci est donnée à titre d'exemple pour mieux comprendre comment cela fonctionne.
|
||||
"un-semestre": {"name": _("One semester"), "price": 15, "duration": 1},
|
||||
"deux-semestres": {"name": _("Two semesters"), "price": 28, "duration": 2},
|
||||
"cursus-tronc-commun": {
|
||||
"name": _("Common core cursus"),
|
||||
"price": 45,
|
||||
"duration": 4,
|
||||
},
|
||||
"cursus-branche": {"name": _("Branch cursus"), "price": 45, "duration": 6},
|
||||
"cursus-alternant": {"name": _("Alternating cursus"), "price": 30, "duration": 6},
|
||||
"membre-honoraire": {"name": _("Honorary member"), "price": 0, "duration": 666},
|
||||
"un-jour": {"name": _("One day"), "price": 0, "duration": 0.00555333},
|
||||
|
||||
# On rajoute ici notre cotisation
|
||||
# Elle se nomme "Un mois"
|
||||
# Coûte 6€
|
||||
# Dure 1 mois (on raisonne en semestre, ici, c'est 1/6 de semestre)
|
||||
"un-mois": {"name": _("One month"), "price": 6, "duration": 0.166}
|
||||
}
|
||||
```
|
||||
|
||||
Une fois ceci fait, il faut créer une nouvelle migration :
|
||||
|
||||
```bash
|
||||
python ./manage.py makemigrations subscription
|
||||
python ./manage.py migrate
|
||||
```
|
||||
|
||||
N'oubliez pas non plus les traductions (cf. [ici](./translation.md))
|
84
docs/howto/terminal.md
Normal file
84
docs/howto/terminal.md
Normal file
@ -0,0 +1,84 @@
|
||||
## Quel terminal utiliser ?
|
||||
|
||||
Quel que soit votre configuration, si vous avez réussi à installer
|
||||
le projet, il y a de fortes chances que bash existe sur
|
||||
votre ordinateur.
|
||||
Certains d'entre vous utilisent peut-être un autre shell,
|
||||
comme `zsh`.
|
||||
|
||||
En effet, `bash` est bien, il fait le taff ;
|
||||
mais son ergonomie finit par montrer ses limites.
|
||||
C'est pourquoi il existe des shells plus avancés,
|
||||
qui peuvent améliorer l'ergonomie, la complétion des commandes,
|
||||
et l'apparence.
|
||||
C'est le cas de `zsh`.
|
||||
Certains vont même plus loin et refont carrément la syntaxe.
|
||||
C'est le cas de `nu`.
|
||||
|
||||
Pour choisir un terminal, demandez-vous juste quel
|
||||
est votre usage du terminal :
|
||||
|
||||
- Si c'est juste quelques commandes basiques et
|
||||
que vous ne voulez pas vous embêter à changer
|
||||
votre configuration, `bash` convient parfaitement.
|
||||
- Si vous commencez à utilisez le terminal
|
||||
de manière plus intensive, à varier les commandes
|
||||
que vous utilisez et/ou que vous voulez customiser
|
||||
un peu votre expérience, `zsh` est parfait pour vous.
|
||||
- Si vous aimez la programmation fonctionnelle,
|
||||
que vous adorez les pipes et que vous voulez faire
|
||||
des scripts complets mais qui restent lisibles,
|
||||
`nu` vous plaira à coup sûr.
|
||||
|
||||
!!! note
|
||||
|
||||
Ce ne sont que des suggestions.
|
||||
Le meilleur choix restera toujours celui
|
||||
avec lequel vous êtes le plus confortable.
|
||||
|
||||
## Commandes utiles
|
||||
|
||||
### Compter le nombre de lignes du projet
|
||||
|
||||
=== "bash/zsh"
|
||||
|
||||
```bash
|
||||
sudo apt install cloc
|
||||
cloc --exclude-dir=doc,env .
|
||||
```
|
||||
Ok, c'est de la triche, on installe un package externe.
|
||||
Mais bon, ça marche, et l'équivalent pur bash
|
||||
serait carrément plus moche.
|
||||
|
||||
=== "nu"
|
||||
|
||||
Nombre de lignes, groupé par fichier :
|
||||
```nu
|
||||
ls **/*.py | insert linecount { get name | open | lines | length }
|
||||
```
|
||||
|
||||
Nombre de lignes total :
|
||||
```nu
|
||||
ls **/*.py | insert linecount { get name | open | lines | length } | math sum
|
||||
```
|
||||
|
||||
Vous pouvez aussi exlure les lignes vides
|
||||
et les les lignes de commentaire :
|
||||
```nu
|
||||
ls **/*.py |
|
||||
insert linecount {
|
||||
get name |
|
||||
open |
|
||||
lines |
|
||||
each { str trim } |
|
||||
filter { |l| not ($l | str starts-with "#") } | # commentaires
|
||||
filter { |l| ($l | str length) > 0 } | # lignes vides
|
||||
length
|
||||
} |
|
||||
math sum
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
71
docs/howto/translation.md
Normal file
71
docs/howto/translation.md
Normal file
@ -0,0 +1,71 @@
|
||||
Le code du site est entièrement écrit en anglais,
|
||||
le texte affiché aux utilisateurs l'est également.
|
||||
La traduction en français se fait
|
||||
ultérieurement avec un fichier de traduction.
|
||||
Voici un petit guide rapide pour apprendre à s'en servir.
|
||||
|
||||
## Dans le code du logiciel
|
||||
|
||||
Imaginons que nous souhaitons afficher "Hello"
|
||||
et le traduire en français.
|
||||
Voici comment signaler que ce mot doit être traduit.
|
||||
|
||||
Si le mot est dans le code Python :
|
||||
|
||||
```python
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
help_text=_("Hello")
|
||||
```
|
||||
|
||||
Si le mot apparaît dans le template Jinja :
|
||||
|
||||
```jinja
|
||||
{% trans %}Hello{% endtrans %}
|
||||
```
|
||||
|
||||
## Générer le fichier django.po
|
||||
|
||||
La traduction se fait en trois étapes.
|
||||
Il faut d'abord générer un fichier de traductions,
|
||||
l'éditer et enfin le compiler au format binaire pour qu'il soit lu par le serveur.
|
||||
|
||||
```bash
|
||||
./manage.py makemessages --locale=fr --ignore "env/*" -e py,jinja
|
||||
```
|
||||
|
||||
## Éditer le fichier django.po
|
||||
|
||||
```locale
|
||||
# locale/fr/LC_MESSAGES/django.po
|
||||
|
||||
# ...
|
||||
msgid "Hello"
|
||||
msgstr "" # Ligne à modifier
|
||||
|
||||
# ...
|
||||
```
|
||||
|
||||
!!!note
|
||||
|
||||
Si les commentaires suivants apparaissent, pensez à les supprimer.
|
||||
Ils peuvent gêner votre traduction.
|
||||
|
||||
```
|
||||
#, fuzzy
|
||||
#| msgid "Bonjour"
|
||||
```
|
||||
|
||||
|
||||
## Générer le fichier django.mo
|
||||
|
||||
Il s'agit de la dernière étape.
|
||||
Un fichier binaire est généré à partir du fichier django.mo.
|
||||
|
||||
```bash
|
||||
./manage.py compilemessages
|
||||
```
|
||||
|
||||
!!!tip
|
||||
|
||||
Pensez à redémarrer le serveur si les traductions ne s'affichent pas
|
71
docs/howto/weekmail.md
Normal file
71
docs/howto/weekmail.md
Normal file
@ -0,0 +1,71 @@
|
||||
Le site est capable de générer des mails automatiques
|
||||
contenant l’agrégation d'articles écrits par les administrateurs de clubs.
|
||||
Le contenu est inséré dans un template standardisé
|
||||
et contrôlé directement dans le code.
|
||||
Il arrive régulièrement que l'équipe communication souhaite modifier ce template.
|
||||
Que ce soient les couleurs,
|
||||
l'agencement ou encore la bannière ou le footer,
|
||||
voici tout ce qu'il y a à savoir sur le fonctionnement
|
||||
du weekmail en commençant par la classe qui le contrôle.
|
||||
|
||||
## Modifier la bannière et le footer
|
||||
|
||||
Ces éléments sont contrôlés par les méthodes `get_banner` et `get_footer`
|
||||
de la classe `Weekmail`.
|
||||
Les modifier est donc très simple,
|
||||
il suffit de modifier le contenu de la fonction et
|
||||
de rajouter les nouvelles images dans les statics.
|
||||
|
||||
Les images sont à ajouter dans `core/static/com/img`
|
||||
et sont à nommer selon le type (banner ou footer), le semestre (Automne ou Printemps) et l'année.
|
||||
Exemple : `weekmail_bannerA18.jpg` pour la bannière de l'automne 2018.
|
||||
|
||||
```python
|
||||
from django.conf import settings
|
||||
from django.templatetags.static import static
|
||||
|
||||
# Sélectionnez le fichier de bannière pour le weekmail de l'automne 2018
|
||||
|
||||
def get_banner(self):
|
||||
return "http://" + settings.SITH_URL + static("com/img/weekmail_bannerA18.jpg")
|
||||
```
|
||||
|
||||
!!!note
|
||||
|
||||
Penser à prendre les images au format **jpg** et à les compresser un peu,
|
||||
pour qu'elles soient le plus léger possible,
|
||||
c'est bien mieux pour l'utilisateur final.
|
||||
|
||||
!!!warning
|
||||
|
||||
Pensez à laisser les anciennes images dans le dossier
|
||||
pour que les anciens weekmails ne soient pas affectés par les changements.
|
||||
|
||||
## Modifier le template
|
||||
|
||||
Il existe deux templates différents :
|
||||
|
||||
- Un en texte pur, qui sert pour le rendu dégradé des lecteurs
|
||||
de mails ne supportant pas le HTML
|
||||
- un qui fait du rendu html.
|
||||
|
||||
Ces deux templates sont respectivement accessibles aux emplacements suivants :
|
||||
|
||||
- `com/templates/com/weekmail_renderer_html.jinja`
|
||||
- `com/templates/com/weekmail_renderer_text.jinja`
|
||||
|
||||
!!!note
|
||||
|
||||
Pour le rendu HTML, pensez à utiliser le CSS et le javascript
|
||||
le plus simple possible pour que le rendu se fasse correctement
|
||||
dans les clients mails qui sont souvent capricieux.
|
||||
|
||||
!!!note
|
||||
|
||||
Le CSS est inclus statiquement pour que toute modification
|
||||
ultérieure de celui-ci n'affecte pas les versions précédemment envoyées.
|
||||
|
||||
!!!warning
|
||||
|
||||
Si vous souhaitez ajouter du contenu,
|
||||
n'oubliez pas de bien inclure ce contenu dans les deux templates.
|
43
docs/index.md
Normal file
43
docs/index.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Documentation du site de l'association des étudiants de l'UTBM
|
||||
|
||||
Bonjour, camarade.
|
||||
|
||||
Si tu es ici, c'est sans doute pour mieux connaitre
|
||||
le fonctionnement interne du site AE.
|
||||
Peut-être même as-tu envie de contribuer à son développement.
|
||||
Si tel est le cas, nous ne pouvons faire autrement
|
||||
que te souhaiter la bienvenue sur cette documentation
|
||||
et espérer que ce que nous avons écrit est assez
|
||||
clair et assez complet pour satisfaire ton esprit
|
||||
en quête de connaissances.
|
||||
|
||||
Et si tu viens pour d'autres motifs,
|
||||
ça ne change rien, soit le bienvenu (ou la bienvenue)
|
||||
quand même.
|
||||
|
||||
Pour que tu saches où chercher quelles informations,
|
||||
voici comment nous avons découpé la documentation :
|
||||
|
||||
- Dans un premier temps, les explications, où nous
|
||||
discutons tout simplement de ce à quoi nous pensions
|
||||
en faisant ceci ou cela, quels motifs ont guidé tel ou
|
||||
tel choix...
|
||||
Bref, nous discutons du projet en tant qu'ensemble
|
||||
de choix, qu'en tant qu'accumulation de détails
|
||||
techniques.
|
||||
Des détails techniques, tu en trouveras quand même,
|
||||
mais sans doute moins que dans les autres parties.
|
||||
- Deuxièmement, les tutoriels, où nous
|
||||
expliquons pas à pas comment installer le
|
||||
projet et commencer à travailler dessus.
|
||||
Vois-le comme une notice d'assemblage.
|
||||
- Troisièmement, les recettes, ou *how-to*,
|
||||
où nous présenterons des conseils, des procédures à suivre
|
||||
et des patterns qui t'aideront peut-être.
|
||||
- Enfin, la référence, où sera donnée en dur
|
||||
les détails techniques les plus intimes du projet,
|
||||
tirés directement du code,
|
||||
qui ne seront sans doute pas d'une grande utilité
|
||||
pour comprendre le projet dans sa globalité,
|
||||
mais qui seront bien plus utiles pour appréhender
|
||||
l'implémentation de telle ou telle partie du code.
|
1
docs/reference/accounting/models.md
Normal file
1
docs/reference/accounting/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: accounting.models
|
1
docs/reference/accounting/views.md
Normal file
1
docs/reference/accounting/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: accounting.views
|
1
docs/reference/club/models.md
Normal file
1
docs/reference/club/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: club.models
|
1
docs/reference/club/views.md
Normal file
1
docs/reference/club/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: club.views
|
1
docs/reference/com/models.md
Normal file
1
docs/reference/com/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: com.models
|
1
docs/reference/com/views.md
Normal file
1
docs/reference/com/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: com.views
|
1
docs/reference/core/models.md
Normal file
1
docs/reference/core/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: core.models
|
1
docs/reference/core/views.md
Normal file
1
docs/reference/core/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: core.views
|
1
docs/reference/counter/models.md
Normal file
1
docs/reference/counter/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: counter.models
|
1
docs/reference/counter/views.md
Normal file
1
docs/reference/counter/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: counter.views
|
1
docs/reference/eboutic/models.md
Normal file
1
docs/reference/eboutic/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: eboutic.models
|
1
docs/reference/eboutic/views.md
Normal file
1
docs/reference/eboutic/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: eboutic.views
|
1
docs/reference/election/models.md
Normal file
1
docs/reference/election/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: election.models
|
1
docs/reference/election/views.md
Normal file
1
docs/reference/election/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: election.views
|
1
docs/reference/forum/models.md
Normal file
1
docs/reference/forum/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: forum.models
|
1
docs/reference/forum/views.md
Normal file
1
docs/reference/forum/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: forum.views
|
1
docs/reference/galaxy/models.md
Normal file
1
docs/reference/galaxy/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: galaxy.models
|
1
docs/reference/galaxy/views.md
Normal file
1
docs/reference/galaxy/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: galaxy.views
|
1
docs/reference/launderette/models.md
Normal file
1
docs/reference/launderette/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: launderette.models
|
1
docs/reference/launderette/views.md
Normal file
1
docs/reference/launderette/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: launderette.views
|
1
docs/reference/matmat/models.md
Normal file
1
docs/reference/matmat/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: matmat.models
|
1
docs/reference/matmat/views.md
Normal file
1
docs/reference/matmat/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: matmat.views
|
1
docs/reference/pedagogy/models.md
Normal file
1
docs/reference/pedagogy/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: pedagogy.models
|
1
docs/reference/pedagogy/views.md
Normal file
1
docs/reference/pedagogy/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: pedagogy.views
|
1
docs/reference/rootplace/models.md
Normal file
1
docs/reference/rootplace/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: rootplace.models
|
1
docs/reference/rootplace/views.md
Normal file
1
docs/reference/rootplace/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: rootplace.views
|
1
docs/reference/sas/models.md
Normal file
1
docs/reference/sas/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: sas.models
|
1
docs/reference/sas/views.md
Normal file
1
docs/reference/sas/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: sas.views
|
1
docs/reference/stock/models.md
Normal file
1
docs/reference/stock/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: stock.models
|
1
docs/reference/stock/views.md
Normal file
1
docs/reference/stock/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: stock.views
|
1
docs/reference/subscription/models.md
Normal file
1
docs/reference/subscription/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: subscription.models
|
1
docs/reference/subscription/views.md
Normal file
1
docs/reference/subscription/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: subscription.views
|
1
docs/reference/trombi/models.md
Normal file
1
docs/reference/trombi/models.md
Normal file
@ -0,0 +1 @@
|
||||
::: trombi.models
|
1
docs/reference/trombi/views.md
Normal file
1
docs/reference/trombi/views.md
Normal file
@ -0,0 +1 @@
|
||||
::: trombi.views
|
3
docs/stylesheets/extra.css
Normal file
3
docs/stylesheets/extra.css
Normal file
@ -0,0 +1,3 @@
|
||||
.mermaid {
|
||||
text-align: center;
|
||||
}
|
150
docs/tutorial/devtools.md
Normal file
150
docs/tutorial/devtools.md
Normal file
@ -0,0 +1,150 @@
|
||||
Le projet n'est en aucun cas lié à un
|
||||
quelconque environnement de développement.
|
||||
Il est possible pour chacun de travailler
|
||||
avec les outils dont il a envie et d'utiliser
|
||||
l'éditeur de code avec lequel il est le plus à l'aise.
|
||||
|
||||
Pour donner une idée, Skia a écrit une énorme
|
||||
partie de projet avec l'éditeur *Vim* sur du GNU/Linux
|
||||
alors que Sli a utilisé *Sublime Text* sur MacOS
|
||||
et que Maréchal travaille avec PyCharm
|
||||
sur ~~Windows muni de WSL~~ Arch Linux btw.
|
||||
|
||||
## Configurer les pre-commit hooks
|
||||
|
||||
La procédure habituelle pour contribuer au
|
||||
projet consiste à commit des modifications,
|
||||
puis à les push sur le dépôt distant et
|
||||
à ouvrir une pull request.
|
||||
Cette PR va faire tourner les outils de vérification
|
||||
de la qualité de code.
|
||||
Si la vérification échoue, la PR est bloquée,
|
||||
et il faut réparer le problème
|
||||
(ce qui implique de push un micro-commit
|
||||
ou de push force sur la branche).
|
||||
|
||||
Dans l'idéal, on aimerait donc qu'il soit
|
||||
impossible d'oublier de faire tourner ces vérifications.
|
||||
Pour ça, il existe un mécanisme : les pre-commits hooks.
|
||||
Ce sont des actions qui tournent automatiquement
|
||||
lorsque vous effectuez un `git commit`.
|
||||
Ces dernières vont analyser et éventuellement modifier le code,
|
||||
avant que Git n'ajoute effectivement le commit sur l'arbre git.
|
||||
Voyez ça comme une micro-CI qui tourne en local.
|
||||
|
||||
Les git hooks sont une fonctionnalité par défaut de Git.
|
||||
Cependant, leur configuration peut-être un peu
|
||||
embêtante si vous le faites manuellement.
|
||||
Pour gérer ça plus simplement,
|
||||
nous utilisons le logiciel python [pre-commit](https://pre-commit.com/)
|
||||
qui permet de contrôler leur installation via un seul fichier de configuration,
|
||||
placé à la racine du projet
|
||||
(plus précisément, il s'agit du fichier `.pre-commit-config.yaml`).
|
||||
|
||||
!!!note
|
||||
|
||||
Les pre-commits sont également utilisés dans la CI.
|
||||
Si ces derniers fonctionnent localement, vous avez la garantie que la pipeline ne sera pas fachée. ;)
|
||||
|
||||
C'est une fonctionnalité de git lui-même,
|
||||
mais c'est assez embêtant à gérer manuellement.
|
||||
Pour gérer ça plus simplement,
|
||||
nous utilisons le logiciel python [pre-commit](https://pre-commit.com/)
|
||||
qui permet de contrôller leur installation via un fichier yaml.
|
||||
|
||||
Le logiciel est installé par défaut par poetry.
|
||||
Il suffit ensuite de lancer :
|
||||
|
||||
```bash
|
||||
pre-commit install
|
||||
```
|
||||
Une fois que vous avez fait cette commande, pre-commit
|
||||
tournera automatiquement chaque fois que vous ferez
|
||||
un nouveau commit.
|
||||
|
||||
Il est également possible d'appeler soi-même les pre-commits :
|
||||
|
||||
```bash
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
|
||||
## Configurer Ruff pour son éditeur
|
||||
|
||||
!!!note
|
||||
|
||||
Ruff est inclus dans les dépendances du projet.
|
||||
Si vous avez réussi à terminer l'installation, vous n'avez donc pas de configuration
|
||||
supplémentaire à effectuer.
|
||||
|
||||
Pour utiliser Ruff, placez-vous à la racine du projet et lancez la commande suivante :
|
||||
|
||||
```bash
|
||||
ruff format # pour formatter le code
|
||||
ruff check # pour linter le code
|
||||
```
|
||||
|
||||
Ruff va alors faire son travail sur l'ensemble du projet puis vous dire
|
||||
si des documents ont été reformatés (si vous avez fait `ruff format`)
|
||||
ou bien s'il y a des erreurs à réparer (si vous avez faire `ruff check`).
|
||||
|
||||
Appeler Ruff en ligne de commandes avant de pousser votre code sur Github
|
||||
est une technique qui marche très bien.
|
||||
Cependant, vous risquez de souvent l'oublier.
|
||||
Or, lorsque le code ne respecte pas les standards de qualité,
|
||||
la pipeline bloque les PR sur les branches protégées.
|
||||
|
||||
Pour éviter de vous faire régulièrement avoir, vous pouvez configurer
|
||||
votre éditeur pour que Ruff fasse son travail automatiquement à chaque édition d'un fichier.
|
||||
Nous tenterons de vous faire ici un résumé pour deux éditeurs de textes populaires
|
||||
que sont VsCode et Sublime Text.
|
||||
|
||||
### VsCode
|
||||
|
||||
Installez l'extension Ruff pour VsCode.
|
||||
Ensuite, ajoutez ceci dans votre configuration :
|
||||
|
||||
```json
|
||||
{
|
||||
"[python]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Sublime Text
|
||||
|
||||
Vous devez installer ce plugin : https://packagecontrol.io/packages/LSP-ruff.
|
||||
Suivez ensuite les instructions données dans la description du plugin.
|
||||
|
||||
Dans la configuration de votre projet, ajoutez ceci:
|
||||
|
||||
```json
|
||||
{
|
||||
"settings": {
|
||||
"lsp_format_on_save": true,
|
||||
"LSP": {
|
||||
"LSP-ruff": {
|
||||
"enabled": true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Si vous utilisez le plugin [anaconda](http://damnwidget.github.io/anaconda/),
|
||||
pensez à modifier les paramètres du linter pep8
|
||||
pour éviter de recevoir des warnings dans le formatage
|
||||
de ruff comme ceci :
|
||||
|
||||
```json
|
||||
{
|
||||
"pep8_ignore": [
|
||||
"E203",
|
||||
"E266",
|
||||
"E501",
|
||||
"W503"
|
||||
]
|
||||
}
|
||||
```
|
11
docs/tutorial/etransaction.md
Normal file
11
docs/tutorial/etransaction.md
Normal file
@ -0,0 +1,11 @@
|
||||
|
||||
La boutique en ligne nécessite une interaction
|
||||
avec la banque pour son fonctionnement.
|
||||
|
||||
Malheureusement, la manière dont cette interaction marche
|
||||
est trop complexe pour être résumée ici.
|
||||
|
||||
Nous ne pouvons donc que vous redirigez vers la doc du crédit
|
||||
agricole :
|
||||
https://www.ca-moncommerce.com/espace-client-mon-commerce/up2pay-e-transactions/ma-documentation/
|
||||
|
64
docs/tutorial/groups.md
Normal file
64
docs/tutorial/groups.md
Normal file
@ -0,0 +1,64 @@
|
||||
Il existe deux types de groupes sur le site AE :
|
||||
|
||||
- l'un se base sur des groupes enregistrés en base de données pendant le développement,
|
||||
c'est le système de groupes réels.
|
||||
- L'autre est plus dynamique et comprend tous les groupes générés
|
||||
pendant l'exécution et l'utilisation du programme.
|
||||
Cela correspond généralement aux groupes liés aux clubs.
|
||||
Ce sont les méta-groupes.
|
||||
|
||||
## La définition d'un groupe
|
||||
|
||||
Les deux types de groupes sont stockés dans la même table
|
||||
en base de données, et ne sont différenciés que par un attribut `is_meta`.
|
||||
|
||||
### Les groupes réels
|
||||
|
||||
Pour plus différencier l'utilisation de ces deux types de groupe,
|
||||
il a été créé une classe proxy
|
||||
(c'est-à-dire qu'elle ne correspond pas à une vraie table en base de donnée)
|
||||
qui encapsule leur utilisation.
|
||||
`RealGroup` peut être utilisé pour créer des groupes réels dans le code
|
||||
et pour faire une recherche sur ceux-ci
|
||||
(dans le cadre d'une vérification de permissions par exemple).
|
||||
|
||||
### Les méta-groupes
|
||||
|
||||
Les méta-groupes, comme expliqué précédemment,
|
||||
sont utilisés dans les contextes où il est nécessaire de créer un groupe dynamiquement.
|
||||
Les objets `MetaGroup`, bien que dynamiques, doivent tout de même s'enregistrer
|
||||
en base de données comme des vrais groupes afin de pouvoir être affectés
|
||||
dans les permissions d'autres objets, comme un forum ou une page de wiki par exemple.
|
||||
C'est principalement utilisé au travers des clubs,
|
||||
qui génèrent automatiquement deux groupes à leur création :
|
||||
|
||||
- club-bureau : contient tous les membres d'un club **au dessus**
|
||||
du grade défini dans `settings.SITH_MAXIMUM_FREE_ROLE`.
|
||||
- club-membres : contient tous les membres d'un club
|
||||
**en dessous** du grade défini dans `settings.SITH_MAXIMUM_FREE_ROLE`.
|
||||
|
||||
|
||||
## Les groupes réels utilisés
|
||||
|
||||
Les groupes réels que l'on utilise dans le site sont les suivants :
|
||||
|
||||
Groupes gérés automatiquement par le site :
|
||||
|
||||
- `Public` : tous les utilisateurs du site
|
||||
- `Subscribers` : tous les cotisants du site
|
||||
- `Old subscribers` : tous les anciens cotisants
|
||||
|
||||
Groupes gérés par les administrateurs (à appliquer à la main sur un utilisateur) :
|
||||
|
||||
- `Root` : administrateur global du site
|
||||
- `Accounting admin` : les administrateurs de la comptabilité
|
||||
- `Communication admin` : les administrateurs de la communication
|
||||
- `Counter admin` : les administrateurs des comptoirs (foyer et autre)
|
||||
- `SAS admin` : les administrateurs du SAS
|
||||
- `Forum admin` : les administrateurs du forum
|
||||
- `Pedagogy admin` : les administrateurs de la pédagogie (guide des UVs)
|
||||
- `Banned from buying alcohol` : les utilisateurs interdits de vente d'alcool (non mineurs)
|
||||
- `Banned from counters` : les utilisateurs interdits d'utilisation des comptoirs
|
||||
- `Banned to subscribe` : les utilisateurs interdits de cotisation
|
||||
|
||||
|
248
docs/tutorial/install.md
Normal file
248
docs/tutorial/install.md
Normal file
@ -0,0 +1,248 @@
|
||||
## Dépendances du système
|
||||
|
||||
Certaines dépendances sont nécessaires niveau système :
|
||||
|
||||
- poetry
|
||||
- libssl
|
||||
- libjpeg
|
||||
- zlib1g-dev
|
||||
- python
|
||||
- gettext
|
||||
- graphviz
|
||||
|
||||
### Installer WSL
|
||||
|
||||
Si vous utilisez Windows, je suis navré
|
||||
de vous annoncer que, certaines dépendances étant uniquement disponibles sur des sytèmes UNIX,
|
||||
il n'est pas possible de développer le site sur ce système d'exploitation.
|
||||
|
||||
Heureusement, il existe une alternative qui ne requiert pas de désinstaller votre
|
||||
OS ni de mettre un dual boot sur votre ordinateur : `WSL`.
|
||||
|
||||
- **Prérequis:** vous devez être sur Windows 10 version 2004 ou ultérieure (build 19041 & versions ultérieures) ou Windows 11.
|
||||
- **Plus d'info:** [docs.microsoft.com](https://docs.microsoft.com/fr-fr/windows/wsl/install)
|
||||
|
||||
```shell
|
||||
# dans un shell Windows
|
||||
wsl --install
|
||||
|
||||
# afficher la liste des distribution disponible avec WSL
|
||||
wsl -l -o
|
||||
|
||||
# installer WSL avec une distro (ubuntu conseillé)
|
||||
wsl --install -d <nom_distro>
|
||||
```
|
||||
|
||||
Une fois `WSL` installé, mettez à jour votre distribution et
|
||||
installez les dépendances (voir la partie installation sous Ubuntu).
|
||||
|
||||
Pour accéder au contenu d'un répertoire externe à `WSL`,
|
||||
il suffit d'utiliser la commande suivante :
|
||||
|
||||
```bash
|
||||
# oui c'est beau, simple et efficace
|
||||
cd /mnt/<la_lettre_du_disque>/vos/fichiers/comme/dhab
|
||||
```
|
||||
|
||||
!!!note
|
||||
|
||||
A ce stade, si vous avez réussi votre installation de `WSL` ou bien qu'il
|
||||
était déjà installé, vous pouvez effectuer la mise en place du projet en suivant
|
||||
les instructions pour votre distribution.
|
||||
|
||||
### Installer les dépendances
|
||||
|
||||
=== "Linux"
|
||||
|
||||
=== "Debian/Ubuntu"
|
||||
|
||||
Si ce n'est pas déjà fait, installez Python :
|
||||
```bash
|
||||
sudo apt install python3
|
||||
# on sait jamais
|
||||
sudo apt install python-is-python3
|
||||
```
|
||||
Si vous utilisez Ubuntu 22.04 ou Ubuntu 24.04,
|
||||
votre version de Python devrait être compatible
|
||||
par défaut avec celle du projet.
|
||||
Si ce n'est pas le cas, nous vous conseillons
|
||||
d'utiliser [pyenv](https://github.com/pyenv/pyenv)
|
||||
|
||||
Puis installez les autres dépendances :
|
||||
|
||||
```bash
|
||||
sudo apt install build-essentials libssl-dev libjpeg-dev zlib1g-dev python-dev \
|
||||
libffi-dev python-dev-is-python3 libgraphviz-dev pkg-config \
|
||||
gettext git pipx
|
||||
|
||||
pipx install poetry
|
||||
```
|
||||
|
||||
=== "Arch Linux"
|
||||
|
||||
```bash
|
||||
sudo pacman -Syu # on s'assure que les dépôts et le système sont à jour
|
||||
|
||||
sudo pacman -S python
|
||||
|
||||
sudo pacman -S gcc git graphviz gettext graphviz pkgconf python-poetry
|
||||
```
|
||||
|
||||
=== "macOS"
|
||||
|
||||
Pour installer les dépendances, il est fortement recommandé d'installer le gestionnaire de paquets `homebrew <https://brew.sh/index_fr>`_.
|
||||
Il est également nécessaire d'avoir installé xcode
|
||||
|
||||
```bash
|
||||
echo 'export PATH="$(brew --prefix graphviz)/bin:$PATH"' >> ~/.zshrc
|
||||
echo 'export CFLAGS="-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -I $(brew --prefix graphviz)/include"' >> ~/.zshrc
|
||||
echo 'export LDFLAGS="-L /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -L $(brew --prefix graphviz)/lib"' >> ~/.zshrc
|
||||
|
||||
brew install git python graphviz pipx
|
||||
pipx install poetry
|
||||
|
||||
# Pour bien configurer gettext
|
||||
brew link gettext # (suivez bien les instructions supplémentaires affichées)
|
||||
```
|
||||
|
||||
!!!note
|
||||
|
||||
Si vous rencontrez des erreurs lors de votre configuration, n'hésitez pas à vérifier l'état de votre installation homebrew avec :code:`brew doctor`
|
||||
|
||||
|
||||
## Finaliser l'installation
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ae-utbm/sith3.git
|
||||
cd sith3
|
||||
|
||||
# Création de l'environnement et installation des dépendances
|
||||
poetry install
|
||||
|
||||
# Activation de l'environnement virtuel
|
||||
poetry shell
|
||||
|
||||
# Installation de Xapian
|
||||
python ./manage.py install_xapian
|
||||
|
||||
# Prépare la base de données
|
||||
python ./manage.py setup
|
||||
|
||||
# Installe les traductions
|
||||
python ./manage.py compilemessages
|
||||
```
|
||||
|
||||
!!!note
|
||||
|
||||
La commande `install_xapian` est longue et affiche beaucoup
|
||||
de texte à l'écran.
|
||||
C'est normal, il ne faut pas avoir peur.
|
||||
|
||||
!!!note
|
||||
|
||||
Pour éviter d'avoir à utiliser la commande `poetry shell`
|
||||
systématiquement, il est possible de consulter [direnv](../howto/direnv.md).
|
||||
|
||||
## Configuration pour le développement
|
||||
|
||||
Lorsqu'on souhaite développer pour le site,
|
||||
il est nécessaire de passer le logiciel en mode debug
|
||||
dans les settings_custom.
|
||||
Il est aussi conseillé de définir l'URL du site sur localhost.
|
||||
Voici un script rapide pour le faire.
|
||||
|
||||
```bash
|
||||
echo "DEBUG=True" > sith/settings_custom.py
|
||||
echo 'SITH_URL = "localhost:8000"' >> sith/settings_custom.py
|
||||
```
|
||||
|
||||
## Démarrer le serveur de développement
|
||||
|
||||
Il faut toujours avoir préalablement activé
|
||||
l'environnement virtuel comme fait plus haut
|
||||
et se placer à la racine du projet.
|
||||
Il suffit ensuite d'utiliser cette commande :
|
||||
|
||||
```bash
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
!!!note
|
||||
|
||||
Le serveur est alors accessible à l'adresse
|
||||
[http://localhost:8000](http://localhost:8000) ou bien [http://127.0.0.1:8000/](http://127.0.0.1:8000/).
|
||||
|
||||
## Générer la documentation
|
||||
|
||||
La documentation est automatiquement mise en ligne à chaque envoi de code sur GitHub.
|
||||
Pour l'utiliser en local ou globalement pour la modifier,
|
||||
il existe une commande du site qui génère
|
||||
la documentation et lance un serveur la rendant
|
||||
accessible à l'adresse [http://localhost:8080](http://localhost:8000).
|
||||
Cette commande génère la documentation à
|
||||
chacune de ses modifications,
|
||||
inutile de relancer le serveur à chaque fois.
|
||||
|
||||
!!!note
|
||||
|
||||
Les dépendances pour la documentation sont optionnelles.
|
||||
Avant de commencer à travailler sur la doc, il faut donc les installer
|
||||
avec la commande `poetry install --with docs`
|
||||
|
||||
```bash
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
## Lancer les tests
|
||||
|
||||
Pour lancer les tests, il suffit d'utiliser
|
||||
la commande suivante :
|
||||
|
||||
```bash
|
||||
# Lancer tous les tests
|
||||
pytest
|
||||
|
||||
# Lancer les tests de l'application core
|
||||
pytest core
|
||||
|
||||
# Lancer les tests de la classe UserRegistrationTest de core
|
||||
pytest core.tests.UserRegistrationTest
|
||||
|
||||
# Lancer une méthode en particulier de cette même classe
|
||||
pytest core.tests.UserRegistrationTest.test_register_user_form_ok
|
||||
```
|
||||
|
||||
!!!note
|
||||
|
||||
Certains tests sont un peu longs à tourner.
|
||||
Pour ne faire tourner que les tests les plus rapides,
|
||||
vous pouvez exécutez pytest ainsi :
|
||||
|
||||
```bash
|
||||
pytest -m "not slow"
|
||||
|
||||
# vous pouvez toujours faire comme au-dessus
|
||||
pytest core -m "not slow"
|
||||
```
|
||||
|
||||
A l'inverse, vous pouvez ne faire tourner que les tests
|
||||
lents en remplaçant `-m "not slow"` par `-m slow`.
|
||||
|
||||
De cette manière, votre processus de développement
|
||||
devrait être un peu plus fluide.
|
||||
Cependant, n'oubliez pas de bien faire tourner
|
||||
tous les tests avant de push un commit.
|
||||
|
||||
|
||||
|
||||
## Vérifier les dépendances Javascript
|
||||
|
||||
Une commande a été écrite pour vérifier les éventuelles mises
|
||||
à jour à faire sur les librairies Javascript utilisées.
|
||||
N'oubliez pas de mettre à jour à la fois le fichier
|
||||
de la librairie, mais également sa version dans `sith/settings.py`.
|
||||
|
||||
```bash
|
||||
# Vérifier les mises à jour
|
||||
python manage.py check_front
|
||||
```
|
198
docs/tutorial/perms.md
Normal file
198
docs/tutorial/perms.md
Normal file
@ -0,0 +1,198 @@
|
||||
|
||||
## Les permissions
|
||||
|
||||
Le site n'utilise pas le système de permissions intégré de Django,
|
||||
mais un système de conception maison.
|
||||
|
||||
### Protéger un modèle
|
||||
|
||||
La gestion des permissions se fait directement par modèle.
|
||||
Il existe trois niveaux de permission :
|
||||
|
||||
- Éditer des propriétés de l'objet
|
||||
- Éditer certaines valeurs l'objet
|
||||
- Voir l'objet
|
||||
|
||||
Chacune de ces permissions est vérifiée par une méthode
|
||||
dédiée de la classe `User` :
|
||||
|
||||
- Editer les propriéts : `User.is_owner(obj)`
|
||||
- Editer les valeurs : `User.can_edit(obj)`
|
||||
- Voir : `User.can_view(obj)`
|
||||
|
||||
Ces méthodes vont alors résoudre les permissions
|
||||
dans cet ordre :
|
||||
|
||||
1. Si l'objet possède une méthode `can_be_viewed_by(user)`
|
||||
(ou `can_be_edited_by(user)`, ou `is_owned_by(user)`)
|
||||
et que son appel renvoie `True`, l'utilisateur a la permission requise.
|
||||
2. Sinon, si le modèle de l'objet possède un attribut `view_groups`
|
||||
(ou `edit_groups`, ou `owner_group`) et que l'utilisateur
|
||||
est dans l'un des groupes indiqués, il a la permission requise.
|
||||
3. Sinon, on regarde si l'utilisateur a la permission de niveau supérieur
|
||||
(les droits `owner` impliquent les droits d'édition, et les droits
|
||||
d'édition impliquent les droits de vue).
|
||||
4. Si aucune des étapes si dessus ne permet d'établir que l'utilisateur
|
||||
n'a la permission requise, c'est qu'il ne l'a pas.
|
||||
|
||||
Voici un exemple d'implémentation de ce système :
|
||||
|
||||
=== "Avec les méthodes"
|
||||
|
||||
```python
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.models import User, Group
|
||||
|
||||
# Utilisation de la protection par fonctions
|
||||
class Article(models.Model):
|
||||
|
||||
title = models.CharField(_("title"), max_length=100)
|
||||
content = models.TextField(_("content"))
|
||||
|
||||
# Donne ou non les droits d'édition des propriétés de l'objet
|
||||
# Un utilisateur dans le bureau AE aura tous les droits sur cet objet
|
||||
def is_owned_by(self, user):
|
||||
return user.is_board_member
|
||||
|
||||
# Donne ou non les droits d'édition de l'objet
|
||||
# L'objet ne sera modifiable que par un utilisateur cotisant
|
||||
def can_be_edited_by(self, user):
|
||||
return user.is_subscribed
|
||||
|
||||
# Donne ou non les droits de vue de l'objet
|
||||
# Ici, l'objet n'est visible que par un utilisateur connecté
|
||||
def can_be_viewed_by(self, user):
|
||||
return not user.is_anonymous
|
||||
```
|
||||
|
||||
=== "Avec les groupes de permission"
|
||||
|
||||
```python
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from core.models import User, Group
|
||||
|
||||
class Article(models.Model):
|
||||
title = models.CharField(_("title"), max_length=100)
|
||||
content = models.TextField(_("content"))
|
||||
|
||||
# relation one-to-many
|
||||
# Groupe possédant l'objet
|
||||
# Donne les droits d'édition des propriétés de l'objet
|
||||
owner_group = models.ForeignKey(
|
||||
Group, related_name="owned_articles", default=settings.SITH_GROUP_ROOT_ID
|
||||
)
|
||||
|
||||
# relation many-to-many
|
||||
# Tous les groupes qui seront ajouté dans ce champ auront les droits d'édition de l'objet
|
||||
edit_groups = models.ManyToManyField(
|
||||
Group,
|
||||
related_name="editable_articles",
|
||||
verbose_name=_("edit groups"),
|
||||
blank=True,
|
||||
)
|
||||
|
||||
# relation many-to-many
|
||||
# Tous les groupes qui seront ajouté dans ce champ auront les droits de vue de l'objet
|
||||
view_groups = models.ManyToManyField(
|
||||
Group,
|
||||
related_name="viewable_articles",
|
||||
verbose_name=_("view groups"),
|
||||
blank=True,
|
||||
)
|
||||
```
|
||||
|
||||
### Appliquer les permissions
|
||||
|
||||
#### Dans un template
|
||||
|
||||
Il existe trois fonctions de base sur lesquelles
|
||||
reposent les vérifications de permission.
|
||||
Elles sont disponibles dans le contexte par défaut du
|
||||
moteur de template et peuvent être utilisées à tout moment.
|
||||
|
||||
- `can_edit_prop(obj, user)` : équivalent de `obj.is_owned_by(user)`
|
||||
- `can_edit(obj, user)` : équivalent de `obj.can_be_edited_by(user)`
|
||||
- `can_view(obj, user)` : équivalent de `obj.can_be_viewed_by(user)`
|
||||
|
||||
Voici un exemple d'utilisation dans un template :
|
||||
|
||||
```jinja
|
||||
{# ... #}
|
||||
{% if can_edit(club, user) %}
|
||||
<a href="{{ url('club:tools', club_id=club.id) }}">{{ club }}</a>
|
||||
{% endif %}
|
||||
```
|
||||
|
||||
#### Dans une vue
|
||||
|
||||
Généralement, les vérifications de droits dans les templates
|
||||
se limitent aux urls à afficher puisqu'il
|
||||
ne faut normalement pas mettre de logique autre que d'affichage à l'intérieur
|
||||
(en réalité, c'est un principe qu'on a beaucoup violé, mais promis on le fera plus).
|
||||
C'est donc habituellement au niveau des vues que cela a lieu.
|
||||
|
||||
Notre système s'appuie sur un système de mixin
|
||||
à hériter lors de la création d'une vue basée sur une classe.
|
||||
Ces mixins ne sont compatibles qu'avec les classes récupérant
|
||||
un objet ou une liste d'objet.
|
||||
Dans le cas d'un seul objet,
|
||||
une permission refusée est levée lorsque l'utilisateur
|
||||
n'a pas le droit de visionner la page.
|
||||
Dans le cas d'une liste d'objet,
|
||||
le mixin filtre les objets non autorisés et si aucun ne l'est,
|
||||
l'utilisateur recevra une liste vide d'objet.
|
||||
|
||||
Voici un exemple d'utilisation en reprenant l'objet Article crée précédemment :
|
||||
|
||||
```python
|
||||
from django.views.generic import CreateView, ListView
|
||||
|
||||
from core.views import CanViewMixin, CanCreateMixin
|
||||
|
||||
from com.models import WeekmailArticle
|
||||
|
||||
# Il est important de mettre le mixin avant la classe héritée de Django
|
||||
# L'héritage multiple se fait de droite à gauche et les mixins ont besoin
|
||||
# d'une classe de base pour fonctionner correctement.
|
||||
class ArticlesListView(CanViewMixin, ListView):
|
||||
model = WeekmailArticle
|
||||
|
||||
|
||||
# Même chose pour une vue de création de l'objet Article
|
||||
class ArticlesCreateView(CanCreateMixin, CreateView):
|
||||
model = WeekmailArticle
|
||||
```
|
||||
|
||||
Les mixins suivants sont implémentés :
|
||||
|
||||
- `CanCreateMixin` : l'utilisateur peut-il créer l'objet ?
|
||||
- `CanEditPropMixin` : l'utilisateur peut-il éditer les propriétés de l'objet ?
|
||||
- `CanEditMixin` : L'utilisateur peut-il éditer l'objet ?
|
||||
- `CanViewMixin` : L'utilisateur peut-il voir l'objet ?
|
||||
- `UserIsRootMixin` : L'utilisateur a-t-il les droit root ?
|
||||
- `FormerSubscriberMixin` : L'utilisateur a-t-il déjà été cotisant ?
|
||||
- `UserIsLoggedMixin` : L'utilisateur est-il connecté ?
|
||||
(à éviter ; préférez `LoginRequiredMixin`, fourni par Django)
|
||||
|
||||
!!!danger "Performance"
|
||||
|
||||
Ce système maison de permissions ne rend pas trop mal, d'un point de vue esthétique.
|
||||
Mais d'un point de vue performance, c'est un désastre.
|
||||
Vérifier une seule permission peut demander plusieurs requêtes à la db.
|
||||
Et chaque vérification recommence dès le début.
|
||||
Il n'y a aucune jointure en db.
|
||||
Le mieux qu'on a trouvé comme pansement sur ça, c'est utiliser le cache,
|
||||
mais c'est seulement un pansement, qui ne rattrape pas les erreurs architecturales.
|
||||
|
||||
Sur une vue où on manipule un seul objet, passe encore.
|
||||
Mais sur les `ListView`, on peut arriver à des temps
|
||||
de réponse extrêmement élevés.
|
||||
|
||||
Faites donc doublement, triplement, quadruplement attention,
|
||||
quand vous manipulez le système de permissions.
|
||||
|
207
docs/tutorial/structure.md
Normal file
207
docs/tutorial/structure.md
Normal file
@ -0,0 +1,207 @@
|
||||
## La structure d'un projet Django
|
||||
|
||||
Un projet Django est structuré en applications.
|
||||
Une application est un package Python
|
||||
contenant un ensemble de vues, de modèles, de templates, etc.
|
||||
Sémantiquement, une application représente
|
||||
un ensemble de fonctionnalités cohérentes.
|
||||
Par exemple, dans notre cas, nous avons une application
|
||||
chargée de la gestion des comptoirs, une autre de la gestion
|
||||
des clubs, une autre de la gestion du SAS, etc.
|
||||
|
||||
On trouve généralement dans un projet Django
|
||||
une application principale qui contient les
|
||||
fichiers de configuration du projet,
|
||||
les urls et éventuellement des commandes d'administration.
|
||||
|
||||
## Arborescence du projet
|
||||
|
||||
Le code source du projet est organisé comme suit :
|
||||
|
||||
<div class="annotate">
|
||||
```
|
||||
sith3/
|
||||
├── .github/
|
||||
│ ├── actions/ (1)
|
||||
│ └── workflows/ (2)
|
||||
├── accounting/ (3)
|
||||
│ └── ...
|
||||
├── api/ (4)
|
||||
│ └── ...
|
||||
├── club/ (5)
|
||||
│ └── ...
|
||||
├── com/ (6)
|
||||
│ └── ...
|
||||
├── core/ (7)
|
||||
│ └── ...
|
||||
├── counter/ (8)
|
||||
│ └── ...
|
||||
├── docs/ (9)
|
||||
│ └── ...
|
||||
├── eboutic/ (10)
|
||||
│ └── ...
|
||||
├── election/ (11)
|
||||
│ └── ...
|
||||
├── forum/ (12)
|
||||
│ └── ...
|
||||
├── galaxy/ (13)
|
||||
│ └── ...
|
||||
├── launderette/ (14)
|
||||
│ └── ...
|
||||
├── locale/ (15)
|
||||
│ └── ...
|
||||
├── matmat/ (16)
|
||||
│ └── ...
|
||||
├── pedagogy/ (17)
|
||||
│ └── ...
|
||||
├── rootplace/ (18)
|
||||
│ └── ...
|
||||
├── sas/ (19)
|
||||
│ └── ...
|
||||
├── sith/ (20)
|
||||
│ └── ...
|
||||
├── stock/ (21)
|
||||
│ └── ...
|
||||
├── subscription/ (22)
|
||||
│ └── ...
|
||||
├── trombi/ (23)
|
||||
│ └── ...
|
||||
│
|
||||
├── .coveragerc (24)
|
||||
├── .envrc (25)
|
||||
├── .gitattributes
|
||||
├── .gitignore
|
||||
├── .mailmap
|
||||
├── .env.exemple
|
||||
├── manage.py (26)
|
||||
├── mkdocs.yml (27)
|
||||
├── poetry.lock
|
||||
├── pyproject.toml (28)
|
||||
└── README.md
|
||||
```
|
||||
</div>
|
||||
|
||||
1. Dossier contenant certaines actions réutilisables
|
||||
dans des workflows Github. Par exemple, l'action
|
||||
`setup-project` installe poetry puis appelle
|
||||
la commande `poetry install`.
|
||||
2. Dossier contenant les fichiers de configuration
|
||||
des workflows Github.
|
||||
Par exemple, le workflow `docs.yml` compile
|
||||
et publie la documentation à chaque push sur la branche `master`.
|
||||
3. Application de gestion de la comptabilité.
|
||||
4. Application contenant des routes d'API.
|
||||
5. Application de gestion des clubs et de leurs membres.
|
||||
6. Application contenant les fonctionnalités
|
||||
destinées aux responsables communication de l'AE.
|
||||
7. Application contenant la modélisation centrale du site.
|
||||
On en reparle plus loin sur cette page.
|
||||
8. Application de gestion des comptoirs, des permanences
|
||||
sur ces comptoirs et des transactions qui y sont effectuées.
|
||||
9. Dossier contenant la documentation.
|
||||
10. Application de gestion de la boutique en ligne.
|
||||
11. Application de gestion des élections.
|
||||
12. Application de gestion du forum
|
||||
13. Application de gestion de la galaxie ; la galaxie
|
||||
est un graphe des niveaux de proximité entre les différents
|
||||
étudiants.
|
||||
14. Gestion des machines à laver de l'AE
|
||||
15. Dossier contenant les fichiers de traduction.
|
||||
16. Fonctionnalités de recherche d'utilisateurs.
|
||||
17. Le guide des UEs du site, sur lequel les utilisateurs
|
||||
peuvent également laisser leurs avis.
|
||||
18. Fonctionnalités utiles aux utilisateurs root.
|
||||
19. Le SAS, où l'on trouve toutes les photos de l'AE.
|
||||
20. Application principale du projet, contenant sa configuration.
|
||||
21. Gestion des stocks des comptoirs.
|
||||
22. Gestion des cotisations des utilisateurs du site.
|
||||
23. Gestion des trombinoscopes.
|
||||
24. Fichier de configuration de coverage.
|
||||
25. Fichier de configuration de direnv.
|
||||
26. Fichier généré automatiquement par Django. C'est lui
|
||||
qui permet d'appeler des commandes de gestion du projet
|
||||
avec la syntaxe `python ./manage.py <nom de la commande>`
|
||||
27. Le fichier de configuration de la documentation,
|
||||
avec ses plugins et sa table des matières.
|
||||
28. Le fichier où sont déclarés les dépendances et la configuration
|
||||
de certaines d'entre elles.
|
||||
|
||||
|
||||
## L'application principale
|
||||
|
||||
L'application principale du projet est le package `sith`.
|
||||
Ce package contient les fichiers de configuration du projet,
|
||||
la racine des urls.
|
||||
|
||||
Il est organisé comme suit :
|
||||
|
||||
<div class="annotate">
|
||||
```
|
||||
sith/
|
||||
├── settings.py (1)
|
||||
├── settings_custom.py (2)
|
||||
├── toolbar_debug.py (3)
|
||||
├── urls.py (4)
|
||||
└── wsgi.py (5)
|
||||
```
|
||||
</div>
|
||||
|
||||
1. Fichier de configuration du projet.
|
||||
Ce fichier contient les paramètres de configuration du projet.
|
||||
Par exemple, il contient la liste des applications
|
||||
installées dans le projet.
|
||||
2. Configuration maison pour votre environnement.
|
||||
Toute variable que vous définissez dans ce fichier sera prioritaire
|
||||
sur la configuration donnée dans `settings.py`.
|
||||
3. Configuration de la barre de debug.
|
||||
C'est inutilisé en prod, mais c'est très pratique en développement.
|
||||
4. Fichier de configuration des urls du projet.
|
||||
5. Fichier de configuration pour le serveur WSGI.
|
||||
WSGI est un protocole de communication entre le serveur
|
||||
et les applications.
|
||||
Ce fichier ne vous servira sans doute pas sur un environnement
|
||||
de développement, mais il est nécessaire en production.
|
||||
|
||||
## Les applications
|
||||
|
||||
Les applications sont des packages Python.
|
||||
Dans ce projet, les applications sont généralement organisées
|
||||
comme suit :
|
||||
|
||||
<div class="annotate">
|
||||
```
|
||||
.
|
||||
├── migrations/ (1)
|
||||
│ └── ...
|
||||
├── templates/ (2)
|
||||
│ └── ...
|
||||
├── admin.py (3)
|
||||
├── models.py (4)
|
||||
├── tests.py (5)
|
||||
├── urls.py (6)
|
||||
└── views.py (7)
|
||||
```
|
||||
</div>
|
||||
|
||||
1. Dossier contenant les migrations de la base de données.
|
||||
Les migrations sont des fichiers Python qui permettent
|
||||
de mettre à jour la base de données.
|
||||
cf. [Gestion des migrations](../howto/migrations.md)
|
||||
2. Dossier contenant les templates jinja utilisés par cette application.
|
||||
3. Fichier de configuration de l'interface d'administration.
|
||||
Ce fichier permet de déclarer les modèles de l'application
|
||||
dans l'interface d'administration.
|
||||
4. Fichier contenant les modèles de l'application.
|
||||
Les modèles sont des classes Python qui représentent
|
||||
les tables de la base de données.
|
||||
5. Fichier contenant les tests de l'application.
|
||||
6. Configuration des urls de l'application.
|
||||
7. Fichier contenant les vues de l'application.
|
||||
Dans les plus grosses applications,
|
||||
ce fichier peut être remplacé par un package
|
||||
`views` dans lequel les vues sont réparties entre
|
||||
plusieurs fichiers.
|
||||
|
||||
L'organisation peut éventuellement être un peu différente
|
||||
pour certaines applications, mais le principe
|
||||
général est le même.
|
Reference in New Issue
Block a user