.. -*- coding: utf-8 -*-


Migration
=========

Une des idées de base d'Erudi est la création incrémentale d'application, et
pour cela de nombreuses actions sont fournies afin de facilement faire évoluer
une application et tout particulièrement le modèle de données manipulé sans
perdre les données des instances existantes.

La version courante d'un modèle d'application est données dans le fichier
`__pkginfo__.py` sous forme d'un tuple de 3 entiers.


Gestion des scripts de migrations
---------------------------------
Les scripts des migrations doivent être placés dans le répertoire `migration` de
l'application, et nommé de la manière suivante :

::

  <n° de version X.Y.Z>[_<description>]_<mode>.py

dans lequel : 

* X.Y.Z correspond au n° de version du modèle vers lequel le script permet de
  migrer,

* le *mode* (entre le dernier "_" et l'extension ".py") indique à quelle partie
  de l'application (serveur RQL, serveur web) le script s'applique en cas
  d'installation distribuée. Il peut valoir : 

  * `common`, s'applique aussi bien sur le serveur RQL que sur le serveur web,
    et met à jour des fichiers sur le disque (migration de fichier de
    configuration par exemple).

  * `web`, s'applique uniquement sur le serveur web, et met à jour des fichiers
    sur le disque 

  * `repository`, s'applique uniquement sur le serveur RQL, et met à jour des
    fichiers sur le disque 

  * `Any`, s'applique uniquement sur le serveur RQL, et met à jour des
    données en base (migrations de schéma et de données par ex.)


Toujours dans le répertoire `migration`, le fichier spécial `depends.map` permet
d'indiquer que pour migrer vers une version spécifique du modèle, il faut tout
d'abord avoir migrer vers une version données de erudi. Ce fichier peut contenir
des commentaires (lignes commençant par un "#"), et une dépendance est notée sur
une ligne de la manière suivante : ::

  <n° de version du modèle X.Y.Z> : <n° de version erudi X.Y.Z>

Par exemple ::

  0.12.0: 2.26.0
  0.13.0: 2.27.0
  # 0.14 works with 2.27 <= erudi <= 2.28 at least
  0.15.0: 2.28.0


Contexte de base
----------------
Les identifiants suivants sont préféfinis dans les scripts de migration : 

* `config`, configuration de l'instance

* `interactive_mode`, booléen indiquant si le script est éxécuté en mode
  interactif ou non

* `appltemplversion`, version du modèle d'application de l'instance

* `applerudiversion`, version erudi de l'instance

* `templversion`, version du modéle d'application installée

* `erudiversion`, version erudi installée

* `confirm(question)`, fonction posant une question et retournant vrai si
  l'utilisateur a répondu oui, faux sinon (retourne toujours vrai en mode non
  interactif) 

* `_`, fonction équivalente à `unicode` permettant de marquer des chaines à
  internationaliser dans les scripts de migration

Dans les scripts "repository", les identifiants suivant sont également définis :

* `checkpoint`, demande confirmant et effectue un "commit" au point d'appel

* `repo_schema`, schéma persistent de l'instance (i.e. schéma de l'instance en
  cours de migration)

* `newschema`, schéma installé sur le système de fichier (i.e. schéma de la
  version à jour du modèle et de erudi)

* `sqlcursor`, un curseur SQL pour les très rares cas où il est réellement
  nécessaire ou avantageux de passer par du sql

* `repo`, l'objet repository

                        
Migration de schéma
-------------------
Les fonctions de migration de schéma suivantes sont disponibles dans les scripts
"repository" : 

* `add_attribute(etype, attrname, attrtype=None, commit=True)`, ajoute un
  nouvel attribut à un type d'entité existante. Si le type de celui-ci n'est pas
  spécifié il est extrait du schéma à jour.
        
* `drop_attribute(etype, attrname, commit=True)`, supprime un
  attribut à un type d'entité existante.

* `rename_attribute(etype, oldname, newname, commit=True)`, renomme un attribut
            
* `add_entity_type(etype, auto=True, commit=True)`, ajoute un nouveau type
  d'entité. Si `auto` est vrai, toutes les relations utilisant ce type d'entité
  et ayant un type d'entité connu à l'autre extrémité vont également être
  ajoutées.

* `drop_entity_type(etype, commit=True)`, supprime un type d'entité et toutes
  les relations l'utilisant.

* `rename_entity_type(oldname, newname, commit=True)`, renomme un type d'entité
            
* `add_relation_type(rtype, addrdef=True, commit=True)`, ajoute un nouveau type
  de relation. Si `addrdef` est vrai, toutes les définitions de relation de ce
  type seront également ajoutées.

* `drop_relation_type(rtype, commit=True)`, supprime un type de relation et
  toutes les définitions de ce type.

* `rename_relation(oldname, newname, commit=True)`, renomme une relation.

* `add_relation_definition(subjtype, rtype, objtype, commit=True)`, ajoute une
  définition de relation.

* `drop_relation_definition(subjtype, rtype, objtype, commit=True)`, supprime
  une définition de relation.

* `synchronize_permissions(ertype, commit=True)`, synchronise les permissions
  d'un type d'entité ou de relation
        
* `synchronize_rschema(rtype, commit=True)`, synchronise les propriétés et
  permissions d'un type de relation.
                
* `synchronize_eschema(etype, commit=True)`, synchronise les propriétés et
  permissions d'un type d'entité.
    
* `synchronize_schema(commit=True)`, synchronise le schéma persistent avec le
  schéma à jour (mais sans ajouter ni supprimer de nouveaux types d'entités ou
  de relations ni de définitions de relation).
        
* `change_relation_props(subjtype, rtype, objtype, commit=True, **kwargs)`, change
  les propriétés d'une definition de relation en utilisant les arguments nommés
  pour les propriétés à changer.

* `set_widget(etype, rtype, widget, commit=True)`, change le widget à utiliser
  pour la relation <rtype> du type d'entité <etype>

* `set_size_constraint(etype, rtype, size, commit=True)`, change la contrainte
  de taille pour la relation <rtype> du type d'entité <etype>


Migration de données
--------------------
Les fonctions de migration de données suivantes sont disponibles dans les scripts
"repository" : 

* `rql(rql, kwargs=None, cachekey=None, ask_confirm=True)`, éxécute une
  requête rql arbitraire, d'interrogation ou de modification. Un objet result
  set est retourné.

* `add_entity(etype, *args, **kwargs)`, ajoute une nouvelle entité du type
  données. La valeur des attributs et relations est spécifiée en utilisant les
  arguments nommés et positionnels.

  
Création de workflow
--------------------
Les fonctions de création de workflow suivantes sont disponibles dans les scripts
"repository" : 

* `add_state(name, stateof, initial=False, commit=False, **kwargs)`, ajoute un
  nouvel état de workflow
    
* `add_transition(name, transitionof, fromstates, tostate, requiredgroups=(), commit=False, **kwargs)`, 
  ajoute une nouvelle transtion de workflow

Migration de configuration
--------------------------
Les fonctions de migration de configuration suivantes sont disponibles dans tout
les scripts : 

* `option_renamed(oldname, newname)`, indique qu'une option a été renommée

* `option_group_change(option, oldgroup, newgroup)`, indique qu'une option a
  changé de groupe

* `option_added(oldname, newname)`, indique qu'une option a été ajoutée

* `option_removed(oldname, newname)`, indique qu'une option a été supprimée


Autres fonctions de migration
-----------------------------
Ces fonctions ne sont utilisés que pour des opérations de bas niveau
irréalisables autrement ou pour réparer des bases cassées lors de session
interactive. Elles sont disponibles dans les scripts "repository".

* `sqlexec(sql, args=None, ask_confirm=True)`, éxécute une requête sql
  arbitraire, à n'utiliser 

* `add_entity_type_table(etype, commit=True)`
* `add_relation_type_table(rtype, commit=True)`
* `uninline_relation(rtype, commit=True)`
