Project Development

This page describes the development process, and some useful tools.


USEFUL LINKS


CONVENTIONS DE CODAGE

http://planetowiki.irap.omp.eu/do/view/Main/HowtoInformatique#Conventions_de_Codage_en_Python


TODO LIST

Aller sur la page TODO

PLAN OF WORK (ROADMAP)

Le stage se déroule du 14 mars au 12 aout, soit environ 5 mois, soit exactement 22 semaines (S1 à S22), ou 44 demi-semaines.
(Ne pas oublier de rédiger le rapport de stage au fur et à mesure)

Voir le détail de la roadmap: https://projects.irap.omp.eu/projects/inventirap/roadmap

All steps below are to be understood with included refactoring, testing, and documenting (in code and on wiki),
and updating of the Dashboard module

  • Full Workflow based on Celery (starting from alert reception and until analysis) : 1 week
  • AlertManager + RequestBuilder : 2-3 weeks
  • Scheduler (to be integrated in this new architecture, and completed) : 1 week
  • RoutineManager (+ RequestBuilder) : 2-3 weeks
  • ObservationManager : 2-3 weeks
  • AnalysisManager: 2 weeks
  • Environment Monitoring : observation conditions, weather, site (PLC), and instruments status : 2 weeks
Prévu Réalisé
S01 (14/3-18/3) 1 Version 0.1 - wiki + git + easy install version 1.1
2
S02 (21/3-25/3) 1 Version 0.2 - wiki + git + easy install version 0.2 (en cours...)
2
S03 (28/3-01/4) 1
2
S04 (04/4-08/4) 1
2
S05 (11/4-15/4) 1
2
S06 (18/4-22/4) 1
2
S07 (25/4-29/4) 1
2
S08 (02/5-06/5) 1
2
S09 (09/5-13/5) 1
2
S10 (16/5-20/5) 1
2
S11 (23/5-27/5) 1
2
S12 (30/5-03/6) 1
2
S13 (06/6-10/6) 1
2
S14 (13/6-17/6) 1
2
S15 (20/6-24/6) 1
2
S16 (27/6-01/7) 1
2
S17 (04/7-08/7) 1
2
S18 (11/7-15/7) 1
2
S19 (18/7-22/7) 1
2
S20 (25/7-29/7) 1
2
S21 (01/8-05/8) 1
2
S22 (08/8-12/8) 1
2

COMMIT PROCEDURE

Voici les différentes étapes à respecter au moment de chaque commit:

1) S'assurer que tous les tests passent toujours...

2) Mettre à jour la version du ou des module(s) modifié(s)
(dans pyros/settings.py)

3) Mettre à jour le fichier README (ceci est un exemple, un template)

Date: 11/05/2016
Version: 0.2.9
Mise à jour doc install
(Attention, changement structure BDD)
Demande (terminée): https://projects.irap.omp.eu/issues/3542
Version majeure en cours (0.2): https://projects.irap.omp.eu/versions/101

Remarques:
=> Version: 0.2.9 = 9ème commit sur la version 0.2
=> préciser "(bugfix)" si c'est le cas
=> ajouter "(Attention, changement structure BDD)" s'il y a eu une modif de la BDD
=> "Demande (terminée)" ou "Demande (en cours)", ou pas de demande du tout (exceptionnellement)
=> ces infos permettront de savoir quelle version (et date) exacte du projet on a actuellement sur son disque

[4) Si c'est la fin d'une version majeure (0.1, 0.2, 0.3, ...)]

  • On doit normalement avoir écrit quelques nouveaux tests pour cette version !!!
  • Ajouter cette version en tête de la section "MAIN CHANGES (MILESTONES)" dans le fichier README
  • Mettre à jour la doc install/INSTALLATION à partir du wiki (si nécessaire)
  • Tester une installation du logiciel from scratch

5) pull

(au cas où quelqu'un d'autre aurait fait un push)

6) commit

Reprendre dans le message de commit, faire un simple copier/coller des infos du fichier README (sauf date):

Version: 0.2.9
Mise à jour doc install (bugfix)
Demande (terminée): https://projects.irap.omp.eu/issues/3542
Version majeure en cours (0.2): https://projects.irap.omp.eu/versions/101

[7) push]

(seulement si le commit est important/urgent, ou suite à un ensemble de commits sur un même thème)


DOCUMENTS


OTHER USEFUL LINKS

  • Created new user 'pyros' with password 'DjangoPyros'
  • pyrossu : pyrossu!
  • Eclipse:
    • Shift-Ctrl-f (ou Shift-Cmd-f) : reformatage du fichier selon PEP8
    • Shift-Ctrl-1 : make doc string ...

I - DATABASE SCHEMA (v0.2.1)

PYROS_PDM_v021.png


II - Get the project (from gitlab)

Gitlab management interface

https://gitlab.irap.omp.eu/epallier/pyros

https://gitlab.irap.omp.eu/epallier/pyros/team

Get the project

https://projects.irap.omp.eu/projects/pyros/wiki/Project_Installation#II-Get-the-project-from-gitlab


III - INSTALLATION

https://projects.irap.omp.eu/projects/pyros/wiki/Project_Installation#III-INSTALLATION


IV - CONFIGURATION of the Django Back Office (administration interface)

Configuration of the Django Back office (admin)


V - EVOLUTION OF THE PROJECT

Linking the User model to the django's one

  • Modifications in models.py :
    • Rename User model to PyrosUser
    • Rename user table name to pyros_user
    • Change all occurences (User -> PyrosUser, users -> pyros_users, ...)
    • from django.contrib.auth.models import User <== add at the beginning of the file
    • user = models.OneToOneField(User, on_delete=models.CASCADE) <== add this field in PyrosUser declaration
    • delete fields in PyrosUser : name, firstname, email, login, pass
  • Modifications in admin.py :
    • Change all occurences (User* -> PyrosUser*, users -> pyros_users, ...)
$ python manage.py makemigrations pyrosapp
$ python manage.py migrate

Manage static files (for admin but also for each application)

./manage.py runserver

if DEBUG=False, we have errors, missing static files :

Not Found: /static/admin/css/base.css
Not Found: /static/admin/css/login.css
Not Found: /admin/login
Not Found: /static/admin/css/base.css
Not Found: /static/admin/css/login.css
Not Found: /static/admin/css/base.css
Not Found: /static/admin/css/login.css
Not Found: /static/admin/css/base.css
Not Found: /static/admin/css/dashboard.css
Not Found: /static/admin/css/base.css
Not Found: /static/admin/css/base.css
…

=> We have to activate the static files management

(see https://docs.djangoproject.com/en/1.9/howto/static-files)

In pyros/urls.py, add this:


from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    url(r'^admin/', admin.site.urls),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

STATIC_ROOT must be defined in settings and says where is the root of all static files

Edit settings.py, add:

STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'public', 'static')

./manage.py collectstatic

=> 56 static files copied to '.../PYROS/public/static'

(in fact it is in public/static/admin/)

Cette commande copie tous les fichiers statiques de toutes les applis
dans public/static

Apache viendra lire ce dossier unique

A chq changement d’un fichier statique d’une appli, exécuter « collectstatic » pour mettre à jour le dossier final public/static (auquel on ne doit pas toucher manuellement, c’est un dossier « final »)


DJANGO SHELL (top cool)

cf page 60 for the ORM methods

First activate the pyros venv :

$ source private/venv_py3_pyros/bin/activate

Go into the pyros project folder src/ :

$ cd src/

./manage.py shell
Python 3.5.1 (default, Mar  2 2016, 03:38:02) 
(InteractiveConsole)

>>> import django

>>> from common.models import *

>>> country = Country(name='mexico', quota=1)
>>> country.save()
(ajout si pas d’id, modif si id)

>>> country = Country(name='france')
>>> country.save()
>>> country.pk
>>> 2

>>> countries = Country.objects.all()
>>> countries.count
>>> <bound method QuerySet.count of <Country: mexico>, <Country: france>>
>>> countries.count()
>>> 2
>>> print(countries)
>>> <Country: mexico>, <Country: france>
>>> print(countries.query)
>>> SELECT country.id, country.name, country.desc, country.quota FROM country

>>> cs = countries.filter(name__icontains='fran')
>>> print(cs)
>>> <Country: france>

>>> cs = countries.filter(name__startswith='me')
>>> print(cs)
>>> <Country: mexico>


HOWTO (divers)

Models

1) Relationships between models (1-1, 1-N, N-M) (p55)

models.ManyToManyField : représente une relation de type N-N
(peu importe de quel côté on le déclare)

models.OneToOneField : attention, le sens est important, on n’accèdera d’une table à l’autre que dans un sens

task <=> user
task.user
user.task_set

2) Quelques options pour les champs

Chaque type de champ possède ses propres propriétés. Cependant, certaines sont communes et souvent utilisées comme :

verbose_name: label du champ
null : valeur NULL autorisée ou non en base de données
blank : valeur vide autorisée lors de la validation du champ dans un formulaire
default : valeur par défaut pour une nouvelle instance
editable : le champ doit-il apparaître automatiquement dans les formulaires
...

Pour relier une appli python pure à un projet django :

=> Ajouter le src/ dans le pythonpath

Séparer le code métier :

=> installer le code métier comme un module via pip
Couches :
  • présentation
  • métier
  • modèle

ORM

(cf page 60)


Exemple: Book
===========================

b = Book(name='Two scoops of django',
             release=date(2013, 08, 31))
b.save()
=> ajout car pas d’id, 

b.name ='Two scoops of django - Best practices'
b.save()
=> modif car il y a un id

b.delete()

Toutes les instances:
Book.objects.all()

Liste filtrée:
books = Book.objects.filter(
        release__gte=date(2013, 01, 01)
      ).exclude(
        borrowed=True
      )

Attention, la requete est exécutée le plus tard possible, seulement quand django a vraiment besoin de l’exécuter

Liste ordonnée:
books = Book.objects.exclude(borrowed=True).order_by('title')
print (books.author)

Une instance précise:
Book.objects.get(pk=12)

Liens vers objets associés:
===========================

Retrouver les livres disponibles d'un auteur :
>> author = Author.objects.get(pk=25)
>> author.books.filter(borrowed=False)
(books est le related name déclaré dans la relation manytomany)

Ajouter un livre à une catégorie :
>> category = Category.objects.get(pk=5)
>> book = Book.objects.get(pk=12)
>> category.books.add(book)

Supprimer l'association de livres à une catégorie :
>> category = Category.objects.get(pk=5)
>> category.books.clear()


Serveur Web


Passer sur le port 80 :

venv_py35_pyros ==> lib ==> python3.5 ==> site-packages ==> django ==> core ==> management ==> commands
Edit runserver.py and change "default_port" 

Apache : gère tous les fichiers statiques (images, html…), et délègue les fichiers python au serveur django

Moteur web django sera soit du wsgi soit du unicorn
Par défaut, 1 seul worker, mais on peut en configurer plusieurs,
l’idéal étant de faire nb coeurs + 1 (le worker maître qui fait le dispatching aux autres)

Frontend : Apache ou Ngininx
Backend : gunicorn (gère facilement des workers) ou uwsgi

==> gunicorn library.wsgi
(à la place de manage runserver ==> A EVITER EN PROD)

pip install gunicorn

gunicorn library.wsgi
ou
gunicorn - - workers 5 library.wsgi

(gunicorn est un équivalent de tomcat, c'est un serveur d’appli Python)

1) URL arrive à Apache, qui dispatche (il gère seul les fichiers statiques, et les fichiers python il les transmet à Django)
2) le fichier library/urls.py prend le relai pour tout ce qui est django

*On peut ajouter un fichier urls.py pour gérer les urls de l’appli
(on garde le urls.py général du projet pour gérer les grandes urls):*

Déclaration d'une URL

# books/urls.py
from django.conf.urls import patterns, include, url
urlpatterns = [
    url(r'^book_list$', 'books.views.book_list', name='book_list'),
]

Inclusion des URLs de l'application au projet

# library/urls.py
...
urlpatterns = [
    ...
    url(r'^books/', include('books.urls', namespace='books')),
]

MTV design pattern

MTV == MVC :

Model = Model
Template = View
View = Controller

CREATION DE LA VUE (en fait, le controleur) : slide 23
views.py = CONTROLEUR

Template = LA VUE (slide 24)
(on peut user Jinja à la place du moteur django par défaut)

Dans chq appli, on crée un dossier templates/ et un sous-dossier du nom de l’appli/
(==> todo/templates/todo/)

Même principe pour les fichiers statiques :
src/static/appli1, appli2, appli3…

NOMENCLATURE template :
nommodèle_action.html
(ex: book_list.html)

On pourrait tout mettre dans le urls.py du projet, MAIS c’est mieux
de créer un fichier urls.py PAR APPLI, puis de les inclure dans le fichier urls.py principal

Class-based views ==> classes controleurs
Les méthodes get() et post() sont déjà définies, et on peut les surcharger…

Avec un template (gabarit), on peut générer autre chose que du html,
on peut par ex générer du texte, du pdf, un email, un xml…

Vue liste (et détail) :
https://docs.djangoproject.com/fr/1.8/topics/class-based-views/generic-display/
1) todo/views.py
from django.views.generic import ListView
from books.models import Publisher

class PublisherList(ListView):
model = Publisher

2) todolist/urls.py
from django.conf.urls import url
from books.views import PublisherList

urlpatterns = [
url(r'^publishers/$', PublisherList.as_view()),
]

3) todo/templates/todo/

- Vue Liste : par défaut le template reçoit un objet « objects_list »
- Vue détail : object (on peut faire object.pk, …)


USEFUL TOOLS FOR DEV


PlantUML

Pour créer des diagrammes UML à la volée, à partir d'un fichier texte, au fur et à mesure de la frappe

1) Prérequis : il faut installer graphviz :

$ sudo apt-get install graphviz (ubuntu, debian)

$ sudo yum install graphviz (fedora)

2) Installation du plugin dans les IDE Eclipse ou PyCharm

Pour PyCharm

-télécharger plugin : https://plugins.jetbrains.com/plugin/7017-plantuml-integration
-extraire les deux .jar dans le dossier /bin de pycharm : ex= /opt/pycharm-community-2017.2.3/bin/ si pycharm est dans /opt
Pour ouvrir une fenêtre plantUML : View -> Tool Windows -> PlantUML

Pour Eclipse

(1) Installer le plugin :

(cf http://plantuml.com/eclipse)

Menu Help/Install New Software...

Entrer l'adresse suivante, et sélectionner/installer PlantUML

http://hallvard.github.io/plantuml/

(2) Créer et visionner un graphique UML

- Créer un fichier texte (voir exemple ci-dessous)

- Window/Show View/Other/Plantuml

=> le diagramme doit alors s'afficher

3) Utilisation

Diagrammes de classe : http://plantuml.com/class-diagram


Pyreverse (included in pylint) : useful for generating uml class diagrams

https://www.logilab.org/blogentry/6883

$ cd PYROS/

$ pyreverse -o png -p pyrosapp -a 1 -A src/pyrosapp/

$ pyreverse -o png -p pyrosapp -s 1 -S src/pyrosapp/

$ pyreverse -ASmy -k -o png -p pyrosappadmin src/pyrosapp/admin.py

classes_pyrosapp.png classes_pyrosappadmin.png classes_dashboard.png

django-extensions and graphviz : useful for generating an image of all the models and their relationships

models.png

django-extensions: http://django-extensions.readthedocs.org/en/latest/installation_instructions.html

GraphViz (development) installation :

MacOS with macport:
$ sudo port install graphviz-devel

Ubuntu:
$ sudo apt-get install graphviz-dev

Windows:
Download it

Python pygraphviz package installation:

$ pip install pygraphviz
=> errors on MacOS (using macport) : "fatal error: 'graphviz/cgraph.h' file not found" 
=> $ pip install --install-option="--include-path=/opt/local/include"  --install-option="--library-path=/opt/local/lib" pygraphviz

Python django-extensions package installation:

$ pip install django-bootstrap3
$ pip install django-extensions
($ pip install --upgrade django-extensions)

Generate the image:

$ cd src/
$ python manage.py graph_models -a -g -o models.png


INSTALLATION FROM THE BEGINNING (for dev only, history of the initial project creation)

Pyros installation from the beginning

PYROS_PDM_v021.png (306 KB) Etienne Pallier, 03/24/2016 12:53 pm

models.py Magnifier (18.7 KB) Paul Carensac, 03/24/2016 03:30 pm

admin.py Magnifier (3.87 KB) Paul Carensac, 03/24/2016 03:30 pm

models.png (603 KB) Etienne Pallier, 04/01/2016 11:42 am

classes_dashboard.png (19.1 KB) Etienne Pallier, 04/01/2016 12:52 pm

classes_pyrosapp.png (2.09 MB) Etienne Pallier, 04/01/2016 12:52 pm

classes_pyrosappadmin.png (433 KB) Etienne Pallier, 04/01/2016 12:52 pm

IRAP-OFT-CC-SP-0007.pdf (630 KB) Etienne Pallier, 04/01/2016 04:25 pm