%%
%
% 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}
\addcontentsline{toc}{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
fonctionnalités plus ou moins utiles, le site possède désormais un ensemble de fonctionnalité 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 ces rôles les plus importants.
\par Mais les vieilles technologies qu'il emploie, et le maintient plus ou moins aléatoire, 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ée 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.

\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 template, 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
\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à qu'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 ou unitaires à 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_GROUPS['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)
\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ée. \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.)r
    \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[OneToOneField] Il n'est pas présent dans ce modèle, mais 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 fonctiones 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 en \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édiaire 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 affichant
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 sont lot d'améliorations non négligeable. 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'objet, 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 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}[-7em]{0em}
    \begin{minted}{jinja}
{% extends "core/base.jinja" %}

{% block title %}
    {{ user.get_display_name() }}'s tools
{% endblock %}

{% block content %}
<h3>User Tools</h3>
<p><a href="{{ url('core:user_profile', user_id=request.user.id) }}">Back to profile</a></p>

<h4>Sith management</h4>
<ul>
{% if user.is_in_group(settings.SITH_GROUPS['root']['name']) %}
    <li><a href="{{ url('core:group_list') }}">Groups</a></li>
{% endif %}
{% if user.is_in_group(settings.SITH_GROUPS['accounting-admin']['name']) %}
    <li><a href="{{ url('accounting:bank_list') }}">Accounting</a></li>
{% endif %}
{% if user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_in_group(settings.SITH_GROUPS['root']['name']) %}
    <li><a href="{{ url('subscription:subscription') }}">Subscriptions</a></li>
    <li><a href="{{ url('counter:admin_list') }}">Counters management</a></li>
{% endif %}
</ul>
<h4>Clubs</h4>
<ul>
    {% for m in user.membership.filter(end_date=None).all() %}
    <li><a href="{{ url('club:tools', club_id=m.club.id) }}">{{ m.club }}</a></li>
    {% endfor %}
</ul>
{% endblock %}
    \end{minted}
\end{addmargin}


% TODO: bases des templates Jinja2


\chapter{Organisation du projet}
\label{cha:organisation_du_projet}

\section{Le repertoire `sith`}
\label{sec:le_repertoire_sith}

\subsection{Les options}
\label{sub:les_options}

% settings.py

\chapter{Les applications}
\label{cha:les_applications}

\section{Core}
\label{sec:core}

\section{Subscription}
\label{sec:subscription}

\section{Accounting}
\label{sec:accounting}

\section{Counter}
\label{sec:counter}

\section{Club}
\label{sec:club}


\chapter*{Conclusion}
\addcontentsline{toc}{chapter}{Conclusion}

\appendix
\addtolength{\textheight}{60mm}
\part*{Annexes}
\addtolength{\topmargin}{-50mm}
\definecolor{gray75}{gray}{0.75}
\newcommand{\hsp}{\hspace{20pt}}
\titleformat{\chapter}[block]{\Huge\bfseries}{\thechapter\hsp\textcolor{gray75}{|}\hsp}{0pt}{\Huge\bfseries}[\vskip -2em]

% \chapter{Classe python}
% \label{python_class}
% \begin{figure}[H]
% \begin{lstlisting}[language=python,morekeywords={True,False}]
% host_to_host = Table("host_to_host", Base.metadata,
%     Column("cluster_id", Integer, ForeignKey("host.host_id"), primary_key=True),
%     Column("node_id", Integer, ForeignKey("host.host_id"), primary_key=True)
% )
% class Host(Base):
%     __tablename__ = 'host'
%     host_id = Column(Integer, primary_key=True, nullable=False)
%     groups = Column(String(30), ForeignKey("env.name"))
%     name = Column(String(30), unique=True, nullable=False,
%                     default="UNKNOWN HOST")
%     address = Column(String(30), nullable=False, default="0.0.0.0")
%     alias = Column(String(30), nullable=True, default="")
%     state = Column(String(10), nullable=False, default=0)
%     num_services = Column(Integer, nullable=False, default=0)
%     num_services_crit = Column(Integer, nullable=False, default=0)
%     num_services_unknown = Column(Integer, nullable=False, default=0)
%     num_services_warn = Column(Integer, nullable=False, default=0)
%     scheduled_downtime_depth = Column(Integer, nullable=False, default=0)
%     _json_extra = Column(Text, nullable=True)
%     _last_time = Column(DateTime, server_default=func.now(),
%                             onupdate=func.current_timestamp())
%     _location = Column(String(4), nullable=True)
%     _nodes = relationship("Host",
%                           backref="_clusters",
%                           secondary=host_to_host,
%                           primaryjoin=host_id==host_to_host.c.cluster_id,
%                           secondaryjoin=host_id==host_to_host.c.node_id,
%                          )
% \end{lstlisting}
% \caption{Classe python}
% \end{figure}
% \par
% On voit bien que l'on définit les attributs de la classe à la manière des colonnes d'une table dans une base de
% donnée.
% \par
% On met aussi ici en place une relation \emph{Many To Many} entre les Hosts à l'aide de la table de jointure définie
% juste avant: \emph{host\_to\_host}.

\end{document}

%s/ \(SQLalchemy\|SQLite\)/ \\emph{\1}/
