En entrée, il prend un MCD (modèle conceptuel de données) décrit dans un langage dédié minimaliste.
En sortie, il produit un diagramme entité-association et, à la demande, un MLD (schéma relationnel, sous forme graphique ou textuelle), un DDL (script SQL de création de la base), un diagramme de classes UML, etc.
En bonus, il est capable de réarranger automatiquement votre MCD de façon esthétique, et de lui appliquer des opérations de réécriture qui vont du mondain (typographie) à l'académique (décomposition d'associations), en passant par le merveilleux (inférence de types, génération d'exercices et d'exemples).
Vous pouvez utiliser Mocodo :
à distance, sans rien installer, avec Mocodo online ;
en local, comme n'importe quel programme Python ;
dans un document Jupyter Notebook (à l'instar de cette documentation).
Ci-dessous, un exemple d'utilisation sous Jupyter Notebook. L'appel du programme est en première ligne ; le texte-source proprement dit, lignes suivantes. En sortie, le diagramme conceptuel, égayé au passage par l'option --colors :
L'appel précédent a automatiquement enregistré le texte-source sous le nom de sandbox.mcd. Renommons-le ccp.mcd pour y avoir accès tout au long de ce document.
In [4]:
Path("sandbox.mcd").rename("ccp.mcd");
On peut le récupérer avec --input pour lui appliquer diverses opérations. Ainsi, l'appel suivant génère et affiche son MLD, son diagramme relationnel et son DDL :
Dans la suite, pour épargner la frappe, les options --input et --transform seront respectivement abrégées en -i et -t.
Les opérations de conversion ne se limitent pas forcément au schéma relationnel. En voici une qui extrait un dictionnaire des données, par défaut sous la forme d'un tableau Markdown à trois colonnes :
In [6]:
%mocodo -i ccp -t data_dict
ccp_data_dict_3.md
Entité ou association
Libellé de l'attribut
Type
Client
Adresse
VARCHAR(255)
"
Nom
VARCHAR(255)
"
Prénom
VARCHAR(255)
"
Réf. client
VARCHAR(8)
Commande
Date
DATE
"
Montant
DECIMAL(10,2)
"
Num. commande
VARCHAR(8)
Inclure
Quantité
INTEGER
Produit
Libellé
VARCHAR(50)
"
Prix unitaire
DECIMAL(10,2)
"
Réf. produit
VARCHAR(8)
Une autre qui transcrit le MCD dans la notation crow's foot (crow) pour Mermaid (mmd) :
Le rendu des diagrammes décrits dans des langages-tiers (comme Mermaid, PlantUML, etc.) n'est pas directement pris en charge, mais peut être délégué (--defer) de façon transparente au service web approprié. Dans ce cas, c'est la sortie graphique qui est affichée :
Une réécriture transforme un MCD Mocodo en un autre MCD Mocodo (au contraire d'une conversion, qui produit un animal d'une espèce différente).
Heureusement, l'utilisateur n'a pas à réfléchir si la transformation qu'il souhaite appliquer est une réécriture ou une conversion : dans les deux cas, il invoque -t (c'est-à-dire --transform), et Mocodo se débrouille.
En guise de premier exemple de réécriture, mettons les noms des entités et associations (boxes) en majuscules, et les libellés (labels) en ASCII et snake case :
Remarquez que l'exécution d'une réécriture affiche, au-dessous du diagramme, le code-source résultant. Celui-ci est précédé de la commande magique originale, privée de l'option -i et de toute opération de réécriture. Ces dispositions permettent de continuer à travailler directement dessus si on le copie-colle dans une autre cellule.
Plusieurs opérations de réécriture de nature sémantique sont également offertes. Par exemple, on peut décomposer un MCD quelconque en un MCD équivalent, mais n'employant que des dépendances fonctionnelles et des entités faibles :
%%mocodo --seed=3 --colors brewer+3
Client: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)]
Passer, 0N Client, 11 Commande
Commande: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)]
DF, _11 Inclure, 1N Commande
Inclure: _Quantité [INTEGER]
DF, _11 Inclure, 0N Produit
Produit: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)]
Notez l'argument arrange:wide. Il a procédé à une réorganisation aléatoire des boîtes, ce que l'insertion de deux nouvelles associations de dépendance fonctionnelles avait rendu nécessaire. Quant à l'option --seed=3, elle garantit que le résultat sera le même à chaque exécution.
Après cet aperçu de quelques-unes des fonctionnalités de Mocodo, entrons dans les détails.
Comme nous l'avons dit, vous pouvez utiliser Mocodo sans rien installer. Il vous suffit d'aller sur mocodo.net et de commencer à taper votre MCD. Appuyez à tout moment sur le ventilateur pour rafraîchir les diagrammes conceptuel et relationnel. Quand le résultat vous convient, appuyez sur le bouton de téléchargement pour récupérer une archive ZIP contenant tous les fichiers d'entrée et de sortie spécifiés.
Mocodo online est conçu pour une utilisation occasionnelle et/ou interactive, et son interface vise avant tout à la simplicité. Vous n'avez donc accès qu'aux options essentielles du programme. Si vous en voulez davantage, tant en termes de paramétrage que de calcul ou de fonctionnalités, nous vous conseillons d'installer Mocodo sur votre machine.
Mocodo est un programme écrit en Python 3. Si vous êtes sous macOS ou Linux, il est déjà installé. Dans le cas contraire, vous devrez peut-être le faire.
Une fois Python présent sur votre machine, tapez sous un terminal:
python -m pip install mocodo
Puis testez-le :
mocodo
Invoqué sous cette forme, Mocodo récupère le texte d'entrée du MCD dans le répertoire courant sous le nom de sandbox.mcd. Si ce fichier n'existe pas, il y sera automatiquement créé avec un MCD d'exemple. Par la suite, vous n'aurez qu'à le garder ouvert sous un éditeur de texte, afin de le modifier à votre fantaisie avant de relancer la commande.
Si votre système se plaint que cette commande n'existe pas, localisez le fichier mocodo et ajoutez à votre PATH le chemin du répertoire contenant:
Installez la distribution Anaconda, qui contient Python 3, Jupyter Notebook et bien plus encore.
La ligne donnée précédemment installe en fait, en plus de Mocodo, sa « commande magique » pour Jupyter Notebook. Nous l'avons déjà invoquée plusieurs fois dans ce document :
python -m pip install mocodo
Mocodo fonctionne parfaitement sans aucune dépendance. Cependant, si vous souhaitez :
utiliser l'option --svg_to pour générer des figures en PDF ou en PNG ;
copier directement dans le presse-papier le résultat d'une réécriture ;
... vous pouvez installer les dépendances optionnelles aux bibliothèques CairoSVG et Pyperclip ainsi :
python -m pip install 'mocodo[svg,clipboard]'
Pour mettre la « commande magique » mocodo à disposition d'un notebook donné, évaluez dans celui-ci la cellule suivante:
%reload_ext mocodo
Techniquement, %load_ext mocodo suffit, mais cette forme vous épargnera un message d'erreur si vous réévaluez ultérieurement la cellule.
Pour tester la commande :
In [11]:
%%mocodo
MISSION: accomplie
Pour charger automatiquement mocodo à chaque ouverture d'un notebook (ce qui dispense d'évaluer %load_ext mocodo) :
exécutez sous un terminal :
ipython profile create
éditez le fichier créé (p. ex.: ~/.ipython/profile_default/ipython_config.py) en remplaçant la ligne suivante :
Le préfixe "%mocodo" ou "%%mocodo" est ignoré, mais l'autoriser permet de copier toute une ligne ou toute une cellule (vraiment) magique, et de la coller directement comme argument de la fonction.
Nouveauté de la version 4.2. Avec cette technique, Mocodo peut être utilisé sous Basthon v. 0.62.12 ou ultérieure. Basthon est une plateforme permettant de travailler sous Jupyter Notebook sans avoir à rien installer. Son auteur, Romain Casati, a ajouté un exemple d'utilisation de Mocodo dans la galerie du logiciel, à tester ici. Sous Mocodo online, cliquez sur le symbole de lecture de Basthon (dans le pied de page) pour ouvrir un notebook avec le MCD en cours d'édition.
Passons en revue les principes de la syntaxe. Ils ne devraient pas poser trop de problèmes.
La première ligne ne fait pas partie de la définition du MCD. Sous Jupyter Notebook, elle dénote l'appel à une « commande magique », qui lance Mocodo sur le reste de la cellule. En dehors d'un notebook, vous n'en avez pas besoin, et toute ligne commençant par un pourcentage (%) sera considérée comme un commentaire.
Une entité est définie par :
$$
\overbrace{\texttt{COMMANDE}}^{\text{son nom}}
\quad
\overbrace{\texttt{:}}^{\text{deux-points}}
\quad
\overbrace{\texttt{Num. commande, Date, Montant}}^{\text{ses attributs séparés par des virgules}}
$$
Notez que le premier attribut d'une entité est considéré par défaut comme son identifiant, et donc souligné.
Une association est définie par :
$$
\overbrace{\texttt{INCLURE}}^{\text{son nom}}
\quad
\overbrace{\texttt{,}}^{\text{une virgule}}
\quad
\overbrace{
\underbrace{\texttt{1N}}_{\text{cardinalités}}
\underbrace{\texttt{COMMANDE}}_{\text{entité}}
\quad
,
\quad
\underbrace{\texttt{0N}}_{\text{cardinalités}}
\underbrace{\texttt{PRODUIT}}_{\text{entité}}
}^{\text{ses pattes séparées par des virgules}}
\quad
\overbrace{\texttt{:}}^{\text{deux-points}}
\quad
\overbrace{\texttt{Quantité}}^{\text{ses attributs séparés par des virgules}}
$$
Notez que pour les associations sans attributs (comme PASSER), le deux-points est facultatif.
Astuce. Si vous recopiez un MCD ou que vous l'avez bien en tête, commencez par les associations et, à tout moment, faites apparaître les entités manquantes (double clic sur le lapin magique sous Mocodo online). Elles auront comme identifiant le nom de l'entité en minuscules, par défaut précédé de « id. » (sauf pour les entités DATE et PÉRIODE). Vous n'aurez plus qu'à remplir les autres attributs :
Lorsque l'une des cardinalités maximales d'une association binaire est 1 (ou quelquefois 0), on désigne cette association sous le nom de dépendance fonctionnelle. On la figure souvent par un cercle portant le symbole DF : cela évite de se creuser la tête pour trouver un nom à une association qui (spoiler) disparaîtra corps et biens au moment du passage au relationnel.
Quelquefois appelées circulaires, unaires ou récursives, elles associent une entité à elle-même. Voici par exemple une représentation des filiations patrilinéaires (le 01 permet d'« arrêter » la remontée des ancêtres) :
Suffixez d'un chevron (< ou >) les cardinalités de la patte concernée. La direction indiquée se lit en partant de l'association et en allant vers l'entité.
In [24]:
%%mocodo
Peut recevoir, 1N> Groupe sanguin, 1N< Groupe sanguin
Groupe sanguin: type de sang
Appartient, 11> Personne, 0N Groupe sanguin
Personne: Num. SS, Nom, Prénom, Sexe
Engendre, 0N< Personne, 22> Personne
Comme on l'a vu dans l'introduction, chaque attribut peut être assorti d'un type entre crochets, qui servira lors de la conversion en SQL. À partir de la version 4.0, Mocodo est capable de deviner le type des attributs usuels à partir de leur nom :
Nouveauté de la version 4.3. Avec l'option -t prompt:types, Mocodo compose un prompt à copier-coller sous ChatGPT (ou autre) pour compléter le MCD avec les types manquants. Ce prompt est trop long pour être reproduit ici : il consiste en une présentation de la syntaxe de Mocodo, des instructions sur le travail demandé, plusieurs exemples corrigés, et enfin le MCD à compléter. Voici par exemple les types générés par Claude 3.5 Haiku pour le MCD de la page d'accueil de Mocodo online :
In [26]:
%%mocodo -t data_dict
AYANT-DROIT: nom ayant-droit [VARCHAR(100)], lien [VARCHAR(50)]
DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET
REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise [INTEGER]
PIÈCE: réf. pièce [VARCHAR(20)], libellé pièce [VARCHAR(255)]
COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité [INTEGER]
DF, _11 AYANT-DROIT, 0N EMPLOYÉ
EMPLOYÉ: matricule [VARCHAR(20)], nom employé [VARCHAR(100)]
PROJET: num. projet [VARCHAR(20)], nom projet [VARCHAR(255)]
FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie [INTEGER]
DÉPARTEMENT: num. département [VARCHAR(20)], nom département [VARCHAR(100)]
EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT
TRAVAILLER, 0N EMPLOYÉ, 1N PROJET
SOCIÉTÉ: num. société [VARCHAR(20)], raison sociale [VARCHAR(255)]
CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ
sandbox_data_dict_3.md
Entité ou association
Libellé de l'attribut
Type
AYANT-DROIT
lien
VARCHAR(50)
"
nom ayant-droit
VARCHAR(100)
COMPOSER
quantité
INTEGER
DÉPARTEMENT
nom département
VARCHAR(100)
"
num. département
VARCHAR(20)
EMPLOYÉ
matricule
VARCHAR(20)
"
nom employé
VARCHAR(100)
FOURNIR
qté fournie
INTEGER
PIÈCE
libellé pièce
VARCHAR(255)
"
réf. pièce
VARCHAR(20)
PROJET
nom projet
VARCHAR(255)
"
num. projet
VARCHAR(20)
REQUÉRIR
qté requise
INTEGER
SOCIÉTÉ
num. société
VARCHAR(20)
"
raison sociale
VARCHAR(255)
Ce problème d'inférence n'est pas très difficile pour une IA. Les résultats devraient être très bons, surtout en comparaison de ceux de la version purement algorithmique, qui sont généralement incomplets :
In [27]:
%%mocodo -t guess:types data_dict --select cv
AYANT-DROIT: nom ayant-droit, lien
DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET
REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise
PIÈCE: réf. pièce, libellé pièce
COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité
DF, _11 AYANT-DROIT, 0N EMPLOYÉ
EMPLOYÉ: matricule, nom employé
PROJET: num. projet, nom projet
FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie
DÉPARTEMENT: num. département, nom département
EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT
TRAVAILLER, 0N EMPLOYÉ, 1N PROJET
SOCIÉTÉ: num. société, raison sociale
CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ
Mocodo combine les avantages de l'approche diagram as code (comme PlantUML, Mermaid ou Graphviz), avec la liberté de positionnement offerte par les logiciels WYSIWYG (what you see is what you get).
Sa grande originalité est de se baser sur l'ordre et la séparation des lignes de la description pour définir une mise en page qui se révèle suffisante dans la majorité des cas.
L'option --detect_overlaps (active par défaut sous Mocodo online) signale le problème et donne la solution :
In [29]:
%mocodo -i sandbox --detect_overlaps
Mocodo Err.29 - Mauvaise disposition des boîtes :
- La patte « INCLURE — COMMANDE » chevauche « PRODUIT ».
- La patte « INCLURE — PRODUIT » chevauche « PASSER ».
- La patte « PASSER — CLIENT » chevauche « COMMANDE ».
- Les pattes « PASSER — COMMANDE » et « INCLURE — COMMANDE » se chevauchent.
Pour corriger cela, réordonnez et/ou sautez des lignes dans le texte-source, soit à la
main, soit à l'aide de l'option -t arrange (tablette de chocolat sous Mocodo online).
Limitation. Seuls les chevauchements mettant en jeu des pattes horizontales ou verticales sont détectés.
Les centres des boîtes sont placés aux intersections d'une grille invisible. Ils sont donc alignés aussi bien horizontalement que verticalement. De plus, dans un but esthétique, le dessin fait l'objet d'une compression dans les deux dimensions. Par exemple, ci-dessus, un espace horizontal négatif a été créé entre le bord droit de l'entité de gauche et le bord gauche de l'entité de droite.
Mocodo centre les rangées qui contiennent moins de boîtes que les autres. Ce comportement n'est pas toujours idéal :
In [32]:
%%mocodo
Ultrices, 01 Aliquet, 0N Aliquet
Aliquet: hendrerit, metus, lacus, quis
Risus, 1N Aliquet, 0N Massa
Massa: metus, posuere
Euismod, 0N Massa, 0N Massa
Convallis, 0N Aliquet, 0N Ante: vestibulum
Tincidunt, 11 Ante, 0N Massa
Gravida: ornare
Rutrum, 0N Gravida, 0N Ante, 0N Aliquet: faucibus, curae
Ante: vitae, tempor
L'utilisateur peut alors spécifier les espacements qu'il désire en complétant les rangées par des boîtes invisibles dont le seul rôle est de « pousser » les autres à l'emplacement voulu. Cela se fait en insérant manuellement des lignes réduites au caractère deux-points :
Cependant, en général, ces manipulations sont inutiles : Mocodo est capable de calculer tout seul des plongements à la fois compacts et esthétiques. Voici une compilation des réarrangements automatiques du MCD précédent produits par :
Préfixez d'un tiret bas (_) une cardinalité 11 pour indiquer que l'entité distinguée est faible.
In [35]:
%%mocodo
ŒUVRE: Cote œuvre, Titre, Date parution
DF, 1N ŒUVRE, _11 EXEMPLAIRE
EXEMPLAIRE: Num. exemplaire, État du livre, Date d'achat
Dans le diagramme, les identifiants (ou plutôt, discriminateurs) d'une telle entité seront soulignés en pointillés, tandis que le 11 sera par défaut souligné d'un trait plein.
Notez qu'un discriminateur n'est pas toujours obligatoire :
Son absence implique cependant que l'occurrence de l'entité forte qui « renforce » une occurrence de l'entité faible ne peut en renforcer une autre : d'où la cardinalité maximale 1 sur la patte distinguant EMPLOYÉ.
Mocodo rejettera donc la version « polygame » du MCD précédent (avec 0N à la place de 01) :
L'héritage permet de regrouper dans une entité, dite « mère », les attributs communs à plusieurs autres entités, dites « filles », qui se distinguent les unes des autres par des attributs spécifiques.
Pour définir une spécialisation, insérez une ligne spécifiant :
$$
\overbrace{\texttt{/}\,\texttt{XT}\,\texttt{\\}}^{\text{sa nature entre barres obliques}}
\qquad
\overbrace{\texttt{<}\,\texttt{-}}^{\text{son type de flèche}}
\qquad
\overbrace{\texttt{mère}}^{\text{une entité}}
\qquad
\overbrace{\texttt{,}}^{\text{une virgule}}
\qquad
\overbrace{\texttt{fille 1, fille 2, fille 3}}^{\text{des entités séparées par des virgules}}
\qquad
\overbrace{\texttt{: discriminateur}}^{\text{optionnellement, un attribut}}
$$
In [38]:
%%mocodo
Personne: num SS, nom, prénom
/XT\ Personne <- Homme, Femme: sexe
Homme:
:
Femme: nom de jeune fille
Inscrivez dans le triangle les symboles de votre choix pour prendre une part active aux guerres culturelles de notre temps :
Totalité
Exclusion
Symboles
Exemple de population
Toute personne est :
oui
oui
/XT\
♂♂♂♂♂♂♂ ♀♀♀♀♀♀♀♀♀♀
- soit un homme - soit une femme
non
oui
/X\
♂♂♂♂♂♂♂♂ ♀♀♀♀♀♀ ○○○○
- soit un homme - soit une femme - soit aucun des deux
oui
non
/T\
♂♂♂♂♂♂ ♀♀♀♀♀♀♀♀ ⚥⚥⚥⚥
- soit un homme - soit une femme - soit les deux à la fois
non
non
/\
♂♂♂♂♂ ♀♀♀♀♀♀ ○○○○ ⚥⚥⚥⚥
- soit un homme - soit une femme - soit aucun des deux - soit les deux à la fois
Pour définir une telle contrainte, ajoutez n'importe où une ligne spécifiant :
$$
\overbrace{\texttt{(}\,\texttt{FOO}\,\texttt{)}}^{\text{son nom entre parenthèses}}
\quad
\overbrace{
\texttt{-}\,\texttt{>}
\texttt{boîte 1,}
\quad
\texttt{.}\,\texttt{.}
\texttt{boîte 2,}
\quad
\texttt{-}\,\texttt{-}
\texttt{boîte 3,}
\quad
\texttt{boîte 4}
}^{\text{les types de liens et les boîtes qu'elle met en jeu, séparés par des virgules}}
\quad
\overbrace{\texttt{:}\,\,\texttt{horizontale, verticale}}^{\text{optionnellement, ses coordonnées}}
$$
Précisions.
Comme leur nom ne l'indique pas, ces contraintes peuvent aussi bien porter sur des entités que des associations.
Le nom d'une contrainte peut être formé de zéro à trois symboles quelconques.
Un lien est soit vide (trait invisible), soit formé d'un ou plusieurs symboles - (trait plein) ou . (trait pointillé), optionnellement précédés d'un symbole < (flèche vers la contrainte) et/ou suivis d'un symbole > (flèche vers la boîte).
Au contraire des boîtes (entités et associations), les contraintes ne sont pas... contraintes par la grille sous-jacente : la mise en page du MCD n'en tient absolument pas compte. Celui de l'exemple ci-dessous a donc dû être « aéré » avec des : pour leur faire de la place.
Les clauses de définition des contraintes peuvent apparaître n'importe où. Notez cependant que tout réarrangement automatique les enverra systématiquement à la fin du texte-source.
Chaque coordonnée du centre de la contrainte peut être exprimée sous la forme :
d'une référence à une boîte sur laquelle l'aligner horizontalement ou verticalement (respectivement) ;
d'un pourcentage de la largeur ou de la hauteur (respectivement) du MCD.
En l'absence de coordonnées, la contrainte est placée au barycentre des boîtes qu'elle relie.
Voici un exemple concret, adapté de la Fig. 7.37 de Merise, deuxième génération (Dominique Nanci et Bernard Espinasse, 4e éd., 2001) :
Dans le MCD ci-dessous, le petit rond et l'enveloppe pointillée indiquent qu'une réservation d'une chambre donnée à une date donnée ne peut être faite que par un seul client :
In [40]:
%%mocodo --colors ocean # changement de palette pour faire apparaître un fond semi-transparent
Date: Date
Réserver, /1N Client, 1N Chambre, 0N Date: Durée
Chambre: Num. chambre, Prix
Client: Id. client
La syntaxe est minimale : une simple barre oblique / avant la cardinalité de l'entité-cible.
S'il y a au maximum deux entités à agréger, et que l'angle formé par l'association et les entités est plat ou rectangle, une enveloppe de points matérialise la pseudo-entité.
Sinon, pour des raisons de simplicité du code et de clarté du diagramme, l'enveloppe n'est pas affichée. Cependant, le petit rond subsiste et le reste des traitements est bien sûr inchangé.
Remarques.
La représentation traditionnelle demanderait à insérer une association DF entre CLIENT et RÉSERVER. Mais permettre l'association d'une entité et d'une association demanderait à apporter au code de Mocodo des changements conséquents, pour un bénéfice qui ne nous saute pas forcément aux yeux.
Les agrégats sur association binaire sont soit inutiles (sur une DF), soit impossibles (sur une non-DF, ils reviennent à permettre à une même occurrence d'entité de renforcer plusieurs occurrences d'une entité faible sans discriminateur). Mocodo les interdit dans les deux cas.
La notion d'agrégation n'a pas très bonne presse dans les milieux autorisés. Ceux-ci lui préfèrent généralement celle (équivalente) de contrainte d'intégrité fonctionnelle à unicité complète. Pour rentrer dans leurs bonnes grâces (et désactiver du même coup la visualisation de l'enveloppe et du petit rond), il suffit d'expliciter la contrainte correspondante dans le MCD précédent. Mocodo peut le faire pour vous :
%%mocodo --seed=12 --colors ocean
Date: Date
Réserver, /1N Client, 1N Chambre, 0N Date: Durée
Chambre: Num. chambre, Prix
-INVISIBLE_1, XX Client, XX Client
Client: Id. client
:
(CIF) ..Réserver, ->Client, --Chambre, --Date: INVISIBLE_1, INVISIBLE_1
Remarquez que la visibilité de la CIF est assurée par le positionnement de celle-ci sur une boîte invisible introduite à cet effet : si vous rectifiez ce positionnement, vous pouvez bien sûr supprimer cette boîte.
Vous avez également le droit d'alléger la visualisation des CIFs complètes en transformant les traits pleins "--" des entités émettrices en traits invisibles "". Là encore, Mocodo peut le faire pour vous :
%%mocodo --seed=12 --colors ocean
Date: Date
Réserver, /1N Client, 1N Chambre, 0N Date: Durée
Chambre: Num. chambre, Prix
-INVISIBLE_1, XX Client, XX Client
Client: Id. client
:
(CIF) ..Réserver, ->Client, Chambre, Date: INVISIBLE_1, INVISIBLE_1
Limitation. Au niveau fonctionnel, c'est toujours la barre oblique qui conditionne le traitement des CIF. Par conséquent, les CIF à unicité incomplète ne sont prises en charge qu'au niveau visuel.
Mocodo 4.1.1 relaxe par défaut (sauf dans la version en ligne) la contrainte de Merise restreignant aux entités la présence d'identifiants. Si vous l'osez, vous pouvez donc obtenir le même schéma relationnel avec le MCD « allégé » suivant :
la création d'une représentation interne complète du MLD ;
la traduction de celle-ci en une représentation externe dans le ou les formats de sortie souhaités.
Pour construire la représentation interne, l'algorithme de base réalise la séquence d'opérations suivante :
Pour chaque entité, une relation (table) de même nom et de mêmes attributs est créée. Le ou les identifiants de l'entité constituent la clé primaire de la relation.
Toute relation issue d'une entité faible est renforcée, c'est-à-dire que la clé primaire de l'entité qu'elle détermine fonctionnellement vient s'adjoindre à sa clé primaire, au besoin de façon récursive.
Les associations sont traitées ainsi :
si toutes les pattes de l'association portent la cardinalité maximale N, une relation de même nom et de mêmes attributs est créée. Sa clé primaire est constituée de l'ensemble des clés primaires des relations issues des entités mises en jeu ;
dans le cas contraire, c'est-à-dire si l'une des pattes de l'association porte la cardinalité (1,1), ou à défaut (0,1), l'entité distinguée se voit adjoindre :
1. en tant que clés étrangères, l'ensemble des clés primaires des autres entités mises en jeu;
2. en tant que simples attributs, l'ensemble des attributs de l'association.
Remarque. Un couple de cardinalités non standard, c'est-à-dire distinct de (0,1), (1,1), (0,N) et (1,N), est traité comme (0,1) si son deuxième symbole est un 1, et comme (0,N) sinon. Cela couvre en particulier les cas (*, 1), (*,N), (?,?) et (X,X).
Pour construire la représentation externe, Mocodo formate la représentation interne à l'aide d'un gabarit. Près d'une centaine de gabarits sont fournis, qui permettent de produire quatre grandes catégories de résultats :
le schéma relationnel dans toutes les combinaisons de formats (HTML, Markdown, texte brut, $\LaTeX$) et d'options (avec ou sans explications, avec ou sans boilerplate, avec ou sans visualisation des contraintes d'unicité et d'optionalité) ;
le diagramme relationnel au format Mocodo, avec ou sans visualisation des contraintes d'unicité et d'optionalité, ou dans des formats externes, comme DBML ou D2.
le DDL en SQL ANSI et dans les principaux dialectes (Microsoft SQL Server, MySQL, Oracle DB, PostgreSQL, SQLite), avec ou sans boilerplate de création de la base ;
La transformation -t mld opère la conversion d'un MCD (modèle conceptuel de données) en un MLD (modèle logique de données), autrement appelé schéma relationnel :
Sous Jupyter Notebook, comme pour toutes les opérations de conversion, le tracé du MCD est omis par défaut. Dans le cas très fréquent où l'on a besoin de visualiser simultanément le MCD et le MLD, on peut invoquer l'option -t sans arguments (ou encore --mld) :
La plupart des SGBD offrent une représentation hybride (graphique / texte) de la base sous la forme de tables liées par des flèches. Nous l'appelons diagramme relationnel. Sous Mocodo :
In [46]:
%mocodo -i ccp --colors mondrian -t diagram
ccp_mld.svg
Le nom du fichier généré avant rendu graphique se termine par _mld.mcd. L'extension .mcd signifie que Mocodo peut le prendre comme texte-source (bien que ce ne soit pas un schéma conceptuel). Examinons son contenu :
La syntaxe d'un MLD est effectivement la même que celle d'un MCD, à ceci près que les associations sont remplacées par des liens allant de l'attribut a1 de l'entité E1 à l'attribut a2 de l'entité E2, et notés : E1: ... a1 > E2 > a2.
Les relations sont placées dans le même ordre que les boîtes du MCD d'origine. Vous devrez quelquefois les réarranger (automatiquement ou manuellement) pour obtenir un résultat plus esthétique. Notez que des boîtes invisibles ont été automatiquement insérées une colonne sur deux afin de laisser de la place aux flèches.
Limitation. Les clés étrangères composites sont actuellement représentées comme si elles étaient séparées (autant de flèches que de parties).
Nouveautés de la version 4.0.
Ce qui se notait E1: ... a1->E2->a2 se note maintenant E1: ... a1 > E2 > a2.
Sous Jupyter Notebook, un seule étape suffit pour visualiser le diagramme relationnel.
Le nom du fichier intermédiaire se termine par _mld.mcd au lieu de .mld. L'ancienne extension est abandonnée.
La principale raison de vivre d'un MCD est de se métamorphoser in fine en une chatoyante base de données relationnelles. Cela se fait au moyen d'un sous-ensemble du langage SQL appelé DDL (Data Definition Language). Sous Mocodo :
Notez le NOT NULL des clés primaires. Il s'agit d'une redondance, mais aussi d'une bonne pratique. Non seulement parce qu'explicit is better than implicit : elle peut se révéler utile, par exemple pour désactiver les contraintes de clé primaire pendant une maintenance tout en continuant à interdire le remplissage avec des NULL. Si cela vous gêne, nous montrons en annexe comment l'éliminer.
Principales nouveautés de la version 4.0.
Génération automatique des contraintes UNIQUE, NOT NULL et NULL appropriées.
Les libellés sont convertis en ASCII, et toute suite de caractères qui ne sont ni des lettres, ni des chiffres, ni des tirets bas sont remplacés par un unique tiret bas. Cela simplifie la référence aux tables et aux champs, et évite de noyer le DDL sous un essaim de délimiteurs (qui plus est spécifiques à chaque dialecte). Ainsi, « Réf. client » se trouvera transformé en Ref_client.
Vos choix de casse (minuscules, MAJUSCULES, snake_case, camelCase, PascalCase, etc.) sont respectés.
Avec l'option de réécriture -t create:types Mocodo essaiera d'inférer des libellés des attributs tout type manquant. La langue définie avec l'option --language est prioritaire, mais l'anglais prend la relève en cas d'échec.
In [49]:
%%mocodo -t create:types --select rw
Customer: Customer ID, Last Name, First Name, Address
Make Order, 0N Customer, 11 Order
Order: Order Number, Date, Amount
INCLUDE, 1N Order, 0N Product: Quantity
Product: Product ID, Description, Unit Price
sandbox.mcd
%%mocodo --select rw
Customer: Customer ID [VARCHAR(8)], Last Name [VARCHAR(255)], First Name [VARCHAR(255)], Address [VARCHAR(30)]
Make Order, 0N Customer, 11 Order
Order: Order Number [VARCHAR(8)], Date [DATE], Amount [DECIMAL(10,2)]
INCLUDE, 1N Order, 0N Product: Quantity [INTEGER]
Product: Product ID [VARCHAR(8)], Description [TEXT], Unit Price [DECIMAL(10,2)]
Il va de soi que les types inférés devront être systématiquement contrôlés, et parfois corrigés ou complétés. Une discussion sur StackOverflow, Common MySQL fields and their appropriate data types, a servi de point de départ. La liste des types utilisée est celle de Wikipedia. Consultez les correspondances spécifiées dans mocodo/resources/default_datatypes_fr.tsv, etc. pour plus de détails, et n'hésitez pas à ouvrir le débat si vous avez des corrections ou des suggestions.
Si vous préférez tout typer à la main, vous pouvez au moins créer les « cases » à remplir :
In [50]:
%%mocodo -t create:types=[] --select rw
Customer: Customer ID, Last Name, First Name, Address
Make Order, 0N Customer, 11 Order
Order: Order Number, Date, Amount
INCLUDE, 1N Order, 0N Product: Quantity
Product: Product ID, Description, Unit Price
sandbox.mcd
%%mocodo --select rw
Customer: Customer ID [], Last Name [], First Name [], Address []
Make Order, 0N Customer, 11 Order
Order: Order Number [], Date [], Amount []
INCLUDE, 1N Order, 0N Product: Quantity []
Product: Product ID [], Description [], Unit Price []
L'argument sql peut être remplacé par un nom de dialecte parmi mysql, oracle, postgresql, sqlite et (nouveauté de la version 4) mssql. Quelques transformations seront alors appliquées au code-source généré. Du fait de l'uniformisation de la syntaxe apportée par la version 4, elles sont assez minimes.
Notez les points suivants :
Mocodo ne touche pas aux types spécifiés par l'utilisateur. Une traduction inter-dialectes automatique est envisageable dans le futur, mais c'est un problème qui n'a en réalité aucune solution satisfaisante.
Mocodo protège les noms de tables ou de colonnes qui se trouveraient faire partie des mots réservés par le dialecte-cible. Par exemple, ci-dessous, les noms USER et MEMBER sont réservés en MySQL. Les listes viennent du site modern-sql.com de Markus Winand, et plus précisément de la page https://modern-sql.com/reserved-words-empirical-list.
Avec la sous-sous-option b, un boilerplate de création de la base est ajouté en préambule.
Génération de contraintes d'optionalité et d'unicité¶
Avec la sous-sous-option c, Mocodo peut faire apparaître dès le niveau logique certaines contraintes du niveau physique. Les notations sont les suivantes :
Contrainte
niveau logique
niveau physique
non-optionalité
attribut!
NOT NULL
optionalité
attribut?
NULL
unicité
attribut $^{u1\ u2...}$
UNIQUE
Par exemple, ci-dessous, la clé étrangère #Réf_client est maintenant marquée comme non optionnelle :
La clé étrangère #id employé est marquée comme unique :
In [54]:
%%mocodo --select all -t mld:c
EMPLOYÉ: id. employé, nom employé
DIRIGER, 11 DÉPARTEMENT, 01 EMPLOYÉ
DÉPARTEMENT: id. département, nom département
sandbox_mld.md
DÉPARTEMENT (id. département, nom département, #id. employé!u1)
EMPLOYÉ (id. employé, nom employé)
Ajout manuel de contraintes d'optionalité et d'unicité¶
Les contraintes NOT NULL ou NULL spécifiées dans les types peuvent également faire l'objet d'une visualisation au niveau relationnel :
In [55]:
%%mocodo -t mld:c
Personne: id. personne [VARCHAR(8)], nom [VARCHAR(255) NOT NULL], prénom [VARCHAR(255)], nom de jeune fille [VARCHAR(255) NULL]
sandbox_mld.md
Personne (id. personne, nom!, prénom, nom de jeune fille?)
Pour les contraintes d'unicité, qui peuvent s'appliquer à plusieurs groupes d'attributs potentiellement non disjoints, la notation est un peu plus complexe. Voici un exemple proposé par Fabien Duchateau :
In [56]:
%%mocodo --select all -t mld:c --shapes trebuchet # changement de police de caractères pour mieux distinguer les 1 des I
CLIENT: Réf. client, 1_Nom, 1_Prénom, Adresse, 2_Mail
sandbox_mld.md
CLIENT (Réf. client, Nom u1, Prénom u1, Adresse, Mail u2)
C'est l'occasion d'introduire quelques définitions :
On appelle identifiant candidat d'une entité tout sous-ensemble minimal d'attributs dont chaque occurrence est unique.
Parmi ces sous-ensembles, l'un est élu identifiant (tout court), souligné, et appelé à devenir clé primaire lors du passage au relationnel.
Les candidats malheureux sont appelés identifiants alternatifs.
Ainsi, dans l'exemple précédent, l'ensemble des identifiants candidats de CLIENT est constitué de :
l'identifiant proprement dit Réf. client ;
l'identifiant alternatif (Nom, Prénom) (dont on supposera pour les besoins de la cause qu'il assure l'unicité) ;
l'identifiant alternatif Mail.
Par défaut, dès la première déclaration d'un identifiant alternatif, Mocodo fait apparaître une gouttière à gauche des attributs de toutes les entités. Y sont portés :
un symbole « ID » (resp. « id ») pour l'identifiant fort (resp. faible).
des chiffres de 1 à 9 correspondant aux numéros des identifiants alternatifs.
La notation de Mocodo permet de faire de n'importe quels sous-ensembles d'attributs des identifiants alternatifs. Ci-dessous, le premier identifiant alternatif est le triplet (bar, biz, quux), le second (biz, buz, quux) et le troisième (qux, quux) :
Examinons le cas exceptionnel où l'identifiant à souligner a un attribut commun avec un identifiant alternatif. Ce dernier devant être distinct et minimal, cela implique que l'identifiant est composite.
Si l'attribut commun est en tête de liste (ici, foo, entité 1), rien ne change, on écrit 1_foo.
S'il n'est pas en tête de liste (ici, bar, entité 2), l'écriture 1_bar dénote déjà l'appartenance à un identifiant alternatif. Elle ne peut dénoter simultanément l'appartenance à l'identifiant à souligner.
C'est pourquoi on doit donc expliciter l'appartenance à l'identifiant à souligner qui, comme on l'a vu, est numéroté 0 (entité 3).
Un sous-cas demande encore réflexion : celui où le premier attribut ne fait pas partie de l'identifiant à souligner (entité 4). Si l'on veut alors que ce premier attribut appartienne à une clé alternative, il faut expliciter le 0 (entité 5).
En résumé, on explicite le 0, soit pour empêcher le soulignement du premier attribut, soit pour forcer le soulignement d'un attribut suivant.
Cela devrait vous rappeler quelque chose… Remplacez 0 par _ dans la phrase précédente et vous retrouverez la règle que vous avez appris à connaître et à aimer : on explicite le _, soit pour empêcher le soulignement du premier attribut, soit pour forcer le soulignement d'un attribut suivant.
Gestion des entités faibles (identification relative)¶
Dans ce joli exemple dû à Idris NEUMANN, Initiation à la conception de bases de données relationnelles avec MERISE, les renforcements successifs aboutissent à faire entrer l'identifiant de RUE dans celui de APPARTEMENT, alors même que ces entités sont séparées par non moins de trois associations :
In [60]:
%%mocodo -t
Appartement: num appart., nb pièces
Composer, 0N Étage, _11 Appartement
Étage: num étage, nb appartements
Appartenir, 1N Immeuble, _11 Étage
Immeuble: num immeuble, nb étages
Se situer, 0N Rue, _11 Immeuble
Rue: code rue, nom rue
Appartement (#code rue, #num immeuble, #num étage, num appart., nb pièces)
Étage (#code rue, #num immeuble, num étage, nb appartements)
Immeuble (#code rue, num immeuble, nb étages)
Rue (code rue, nom rue)
Le résultat apparaît plus clairement sur le diagramme relationnel :
In [61]:
%mocodo -i sandbox -t diagram --colors mondrian
sandbox_mld.svg
Une phase préliminaire de l'algorithme de passage au relationnel consiste à « renforcer » les entités faibles de façon à traiter uniquement des identifiants forts dans la suite. Cela permet la gestion des renforcements en cascade comme ci-dessus, ainsi que la détection des problèmes de renforcement cyclique :
In [62]:
%%mocodo
Pick, 0N Land, _11 Peer
Land: true, hold
Peer: foot, city
Zone, 1N Peer, _11 Land
In [63]:
%mocodo -i sandbox -t
Mocodo Err.17 - Cycle d'entités faibles dans "Land", "Peer".
La bonne gestion d'un héritage est primordiale, tant du point de vue de la préservation du patrimoine, que de l'optimisation des ressources financières, de la minimisation des conflits familiaux et du respect des dernières volontés du défunt. Je blague.
Adaptons ici un exemple d'un cours de Stéphane Crozat. Nous reprenons sa terminologie et donnons en parallèle celle introduite par Martin Fowler dans Patterns of Enterprise Application Architecture, Addison-Wesley (2003).
Ci-dessous, l'héritage est considéré comme total (tout document est soit un ouvrage, soit une thèse, soit les deux). Crozat passe en revue trois mécanismes possibles pour le passage au relationnel.
L'héritage par référence (en anglais, Class Table Inheritance ou Table Per Type Inheritance) se note en Mocodo par une flèche simple allant vers les filles. Une référence à la table-mère sera ajoutée dans chaque table-fille comme clé étrangère. L'intérêt de cette solution est en raison directe du nombre d'attributs non identifiants de l'entité-mère.
L'héritage par absorption dans les tables-filles (en anglais, Concrete Table Inheritance ou Table Per Concrete Inheritance) se note en doublant la flèche :
les attributs de la table-mère sont reproduits dans chacune des filles. Pour chaque document qui est à la fois un ouvrage et une thèse, il y aura donc duplication des triplets (cote, titre, auteur).
la table-mère disparaît. Pour éviter de perdre les informations sur les documents qui ne seraient ni des ouvrages, ni des thèses, Mocodo lève une erreur si l'héritage n'est pas total (T ou XT).
L'héritage par absorption dans la table-mère (en anglais, Single Table Inheritance ou Table Per Hierarchy Inheritance) se note par une flèche simple allant vers la mère. Les tables-filles disparaissent, et leurs attributs migrent dans la mère avec une contrainte d'optionalité (point d'interrogation en relationnel, NULL en SQL).
Ci-dessus, nous avons donné comme « attribut » au triangle d'héritage un « discriminateur » de type ENUM. Cela ajoute à la table un champ optionnel ou non (selon que l'héritage est partiel ou total) permettant de déterminer à quel type concret d'une occurrence on a affaire.
C'est par défaut un UNSIGNED INT, qui pourra prendre les valeurs :
1 = OUVRAGE, 2 = THÈSE, pour l'héritage exclusif ;
1 ($2^0$) = OUVRAGE, 2 ($2^1$) = THÈSE, 3 ($2^0 | 2^1$) = OUVRAGE + THÈSE, pour l'héritage non exclusif.
Alternativement à l'emploi d'un discriminateur, Mocodo peut ajouter un drapeau booléen par table-fille. Cela se note en doublant la flèche :
Un agrégat simple autour d'une association dont toutes les cardinalités maximales sont N se traduit en relationnel comme la même association non agrégée, mais avec une clé primaire réduite :
In [68]:
%%mocodo --select all -t mld:c
DATE: date
RÉSERVER, /1N CLIENT, 1N CHAMBRE, 0N DATE: durée
CHAMBRE: num. chambre, prix
CLIENT: id. client, nom client
Un agrégat simple autour d'une association dont la cardinalité maximale « de sortie » est 1 se traduit en relationnel comme la même association non agrégée, mais avec une contrainte d'unicité sur la clé étrangère. Ci-dessous, au lieu d'avoir simplement num résa $\implies$ (num voilier, num semaine), on a donc en plus (num voilier, num semaine) $\implies$ num résa :
In [69]:
%%mocodo --select all -t mld:c
Voilier: num voilier, longueur
Offrir, /11 Réservation, 0N Voilier, 0N Semaine: tarif
Semaine: num semaine, date début
Réservation: num résa, arrhes, date résa
Le passage au relationnel supprime automatiquement les tables réduites à une clé primaire, pourvu qu'aucun composant de celle-ci ne soit clé étrangère. Si l'on souhaite maintenir certaines de ces tables, on préfixe d'un + l'entité concernée. Ainsi, ci-dessous, Date est supprimée, mais pas Thème.
Le champ thème fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Thème.
Le champ id. formation fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Formation.
Animateur (num. animateur, nom animateur)
Le champ num. animateur constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Animateur.
Le champ nom animateur était déjà un simple attribut de l'entité Animateur.
Formation (id. formation, durée)
Le champ id. formation constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Formation.
Le champ durée était déjà un simple attribut de l'entité Formation.
Intervenir (#num. animateur, #id. formation, date, nb heures)
Le champ num. animateur fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Animateur.
Le champ id. formation fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Formation.
Le champ date fait partie de la clé primaire de la table. Sa table d'origine (Date) ayant été supprimée, il n'est pas considéré comme clé étrangère.
Le champ nb heures était déjà un simple attribut de l'association Intervenir.
Thème (thème)
Le champ thème constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Thème.
NB. La table Date a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.
Notez l'explication de la suppression (en NB) et celle de la perte du caractère étranger de l'attribut date (dans la table Intervenir). Pour une discussion sur cette problématique, cf. issue #66.
Dans le cas où les particuliers sont beaucoup plus nombreux que les employés d'entreprise, la clé étrangère #id. entreprise est presque toujours à NULL. C'est une perte d'espace de stockage. On peut entourer l'association de crochets droits préfixer d'un + (à partir de la version 4) l'association pour forcer sa conversion en table. Mocodo produit alors une visualisation intermédiaire entre entité et association :
Cela permet du même coup d'éviter un champ optionnel, dont la gestion peut être délicate (notamment sous Microsoft SQL Server qui, au mépris du standard SQL, ne semble pas convaincu qu'une colonne UNIQUE peut contenir plus d'un NULL !).
Étudiant (num. étudiant, nom, coordonnées, date 1, note stage, date 2, #num. enseignant)
Force est de constater que la table Étudiant laisse quelque peu à désirer du point de vue sémantique :
on ne sait pas à quoi correspondent les attributs date 1 et date 2 : ils ne peuvent en tout cas certainement pas être laissés en l'état ;
ensuite, on pourrait avoir envie d'expliciter la raison pour laquelle un enseignant apparaît parmi les attributs d'un étudiant.
Ces précisions peuvent être apportées en insérant, entre la cardinalité et l'entité des pattes appropriées, une note entre crochets, appelée rôle. Ces rôles seront utilisés pour compléter le nom des clés étrangères correspondantes. Cela permet de réintroduire la sémantique perdue lors de la disparition des associations de dépendance fonctionnelle par lesquelles elle ont migré :
Étudiant (num. étudiant, nom, coordonnées, date soutenance, note stage, date visite resp, #num. ens. resp.)
La composition du rôle est entièrement paramétrable :
date soutenance : par défaut, le rôle est concaténé à la clé, avec un séparateur dépendant du gabarit (espace par défaut, tiret bas pour SQL).
date visite resp. : + en préfixe supprime le séparateur.
num. ens. resp. : - en préfixe supprime le nom de la clé et le remplace par le rôle.
Pour ne pas surcharger le dessin, le rôle n'est pas affiché à côté du lien, mais il apparaît au survol de la cardinalité.
Correction de la version 4.0. Dans une association réflexive hiérarchique, c'est désormais le rôle porté par la patte *N qui sert à rétablir la sémantique. Auparavant c'était l'inverse, en contradiction avec le traitement des associations binaires et n-aires : si vous avez utilisé des rôles dans une association réflexive avant la version 4.0.0, vous devez donc les permutez pour que Mocodo les traite correctement. Reprenons la filiation patrilinéaire :
Lorsque toutes les cardinalités d'une dépendance fonctionnelle sont 11 (ou toutes 01), le sens de migration est spécifié à coût zéro en référençant l'entité réceptrice en tête de la liste des entités énumérées dans la clause de définition de l'association :
In [76]:
%%mocodo -t mld diagram sql --colors mondrian
USER: user id [VARCHAR(8)], name [VARCHAR(100)], pseudo [VARCHAR(100)]
DF, 11 USER, 11 AUTHENTICATION
AUTHENTICATION: email [VARCHAR(255)], password hash [BINARY(64)], salt [BINARY(16)]
Les traitements alternatifs (fusion en une seule table ou migration dans les deux sens) ne sont pas pris en charge par Mocodo. La dernière pratique ressortit à la phase d'optimisation et de dénormalisation de la base, qui est en dehors de sa juridiction. La seule chose que vous pouvez faire, à des fins d'illustration, est de reprendre le MLD généré pour y ajouter à la main la migration inverse. Notez cependant que les schémas logique et physique générés seront alors dépourvus de toute clé étrangère (et nécessiteront donc également d'être retouchés à la main).
In [78]:
%%mocodo -t mld sql --colors mondrian --select all
:
USER: user id [VARCHAR(8)], name [VARCHAR(100)], pseudo [VARCHAR(100)], #email > AUTHENTICATION > email
:
AUTHENTICATION: email [VARCHAR(255)], password hash [BINARY(64)], salt [BINARY(16)], #user id > USER > user id
:
sandbox_mld.md
AUTHENTICATION (email, password hash, salt, user id)
La table A dépend de la table B lorsque A possède une clé étrangère qui est clé primaire de B. Cette notion est utile dans le cas où l'on doit importer une base de données à partir d'un ensemble de fichiers CSV (ou autres). Si l'on veut éviter de perdre le bénéfice du contrôles de clés étrangères (en faisant, p. ex. sous MySQL, SET FOREIGN_KEY_CHECKS = 0), il conviendra de lire ces fichiers dans un ordre topologique quelconque. Mocodo peut générer un graphe des dépendances qui met cet ordre en évidence :
In [79]:
%mocodo -i ccp -t dependencies --defer
ccp_dependencies.svg
Même si un tel graphe n'est pas forcément sans circuits, remplir les tables dans le sens de lecture des langues latines minimisera le recours à la désactivation des contraintes de clés étrangères.
DBML (Database Markup Language) est un DSL (langage dédié) open-source conçu pour définir et documenter les schémas et structures de base de données. Il vise la simplicité, la cohérence et la lisibilité.
À notre connaissance, il n'existe pas actuellement d'API publique de rendu des diagrammes DBML. Pour les visualiser, copiez-collez la sortie sur Dbdiagram.io, ou installez un plugin VS-Code :
D2 est un langage de description de diagrammes de différents types. La prise en charge des diagrammes relationnels est minimale, mais le projet est jeune (actuellement en version 0.6.1, open source depuis novembre 2022). Son point fort est TALA, son algorithme de plongement propriétaire, que vous pouvez tester ici. Celui-ci requérant une licence payante, si vous déférez le rendu, c'est le plongement par défaut (Dagre, celui de Mermaid) qui sera utilisé.
À tout seigneur tout honneur, Mocodo peut traduire votre MCD en UML. Ce formalisme graphique n'a, curieusement, pas de DSL textuel officiel. Nous nous rabattons donc sur le standard de fait, PlantUML :
In [84]:
%mocodo -i ccp --title CCP -t uml:plantuml=- # '-' supprime un préambule par défaut qui ne nous intéresse pas ici
Nouveauté de la version 4.0. Force est de reconnaître que de nos jours, les MCD à la sauce Merise ne sont plus goûtés que par une poignée d'irréductibles Gaulois (et contractuellement leurs étudiants). Dans le cadre de son projet secret de domination planétaire, Mocodo commence à faire du pied à des notations mieux comprises du reste de l'univers.
Dans le même ordre d'idées, Mocodo peut générer des ERD dans l'astucieuse notation introduite en 1976 par Gordon Everest. Par défaut, le format du fichier intermédiaire est là encore Graphviz :
In [88]:
%mocodo -i ccp -t crow --defer --colors brewer+3
ccp_erd_crow.svg
Il est également possible de demander une sortie au format Mermaid. Vous en avez un exemple dans l'introduction, nous ne le répétons pas ici. Le DSL de Mermaid est de plus haut niveau, non encombré d'informations de style. Cependant, le format Graphviz peut être préféré pour plusieurs raisons :
on peut lui appliquer une palette de couleurs de Mocodo (cf. ci-dessus) ;
il gère les accents ;
il admet la virgule dans les types (Mermaid demande actuellement à transformer DECIMAL(10,2) en DECIMAL(10-2)) ;
il peut produire de meilleurs plongements (notamment en jouant sur la valeur de l'option --seed) ;
ceux-ci peuvent-être rectifiés à la main (quoique péniblement).
Vous pouvez extraire sous forme de table diverses informations sur les attributs de votre MCD.
Un format possible est TSV. Dans ce cas, sous Jupyter Notebook, si la bibliothèque pandas est installée, elle sera rendue comme un dataframe :
In [89]:
%mocodo -i ccp -t data_dict:tsv
ccp_data_dict_3.tsv
Entité ou association
Libellé de l'attribut
Type
0
Client
Adresse
VARCHAR(255)
1
Client
Nom
VARCHAR(255)
2
Client
Prénom
VARCHAR(255)
3
Client
Réf. client
VARCHAR(8)
4
Commande
Date
DATE
5
Commande
Montant
DECIMAL(10,2)
6
Commande
Num. commande
VARCHAR(8)
7
Inclure
Quantité
INTEGER
8
Produit
Libellé
VARCHAR(50)
9
Produit
Prix unitaire
DECIMAL(10,2)
10
Produit
Réf. produit
VARCHAR(8)
Le format par défaut est Markdown. Vous pouvez préciser en sous-sous-option tout ou partie des colonnes suivantes dans l'ordre où vous les souhaitez :
label: le libellé de l'attribut ;
type : son type ou un descriptif (auquel cas il conviendra de changer le nom de la colonne) ;
box : le nom de l'entité ou association où il se trouve.
Entourez ces noms de colonnes de balises Markdown pour les mettre en forme (pas d'incidence en TSV). Faites-les suivre de ="Nom de colonne personnalisé" pour éviter la valeur par défaut (dépendante de l'option language). Dans l'exemple ci-dessous, les sous-sous-options se décodent ainsi :
**box**="Entité ou association" : boîtes en colonne 1, en-tête personnalisé, cellules en gras ;
label : libellé des attributs en colonne 2 ;
`type`=`Type` : types en colonne 3, en-tête personnalisé et cellules dans une police non proportionnelle.
In [90]:
%mocodo -i ccp -t data_dict:**box**="Entité ou<br>association",label,`type`=`"Type de données"`
ccp_data_dict_3.md
Entité ou association
Libellé de l'attribut
Type de données
Client
Adresse
VARCHAR(255)
"
Nom
VARCHAR(255)
"
Prénom
VARCHAR(255)
"
Réf. client
VARCHAR(8)
Commande
Date
DATE
"
Montant
DECIMAL(10,2)
"
Num. commande
VARCHAR(8)
Inclure
Quantité
INTEGER
Produit
Libellé
VARCHAR(50)
"
Prix unitaire
DECIMAL(10,2)
"
Réf. produit
VARCHAR(8)
Aménités.
Au format Markdown, les répétitions de cellules de la première colonne sont remplacées par un guillemet itératif.
Le tableau est automatiquement trié selon sa première, puis éventuellement deuxième et troisième colonnes.
Si une seule colonne est demandée, la ligne d'en-tête n'est pas générée et, en Markdown, c'est une liste qui est produite.
In [91]:
%mocodo -i ccp -t data_dict:label
ccp_data_dict_1.md
Adresse
Date
Libellé
Montant
Nom
Num. commande
Prix unitaire
Prénom
Quantité
Réf. client
Réf. produit
Nouveautés de la version 4.0.
Sélection, ordre et nommage des colonnes, mise en forme Markdown, tri, guillemets itératifs.
Remplacement par un algorithme dédié du gabarit relationnel utilisé (abusivement) dans les versions antérieures.
Un chiffre ou tiret bas à la fin du nom d'une boîte (entité ou association) est utilisé en interne pour distinguer cette boîte des autres, mais n'est pas affiché dans le diagramme conceptuel. Cela peut servir à produire une vue en extension d'un MCD.
Voici par exemple le MCD que j'utilise en cours pour introduire la notion d'entité faible (à gauche, vue en compréhension, à droite vue en extension):
In [93]:
%%mocodo
ŒUVRE1: cote, titre, date de publication
:::
ŒUVRE2: 612.NAT.34, J'apprends à lire à mes souris blanches, mai 1975
:
DF, 1N ŒUVRE1, _11 EXEMPLAIRE1
::
DF, XX ŒUVRE2, XX EXEMPLAIRE2
DF, XX ŒUVRE2, XX EXEMPLAIRE3
DF, XX ŒUVRE2, XX EXEMPLAIRE4
EXEMPLAIRE1: numéro d'exemplaire, état, date d'achat
::
EXEMPLAIRE2: 1, bon état, 12/6/1975
EXEMPLAIRE3: 2, bon état, 1/8/1977
EXEMPLAIRE4: 3, reliure rongée, 3/4/2005
Mocodo n'interdit pas la conversion en relationnel d'un tel MCD, mais celle-ci n'a aucun sens.
Si l'on veut garder les cardinalités sans les afficher, on peut les préfixer d'un -. Le résultat de la conversion en relationnel peut alors être interprété comme l'ensemble des lignes des différentes tables.
Les débutants ont souvent des doutes sur la sémantique de telle ou telle cardinalité. Cette information peut être incluse dans le texte-source en annotant les pattes correspondantes. Survolez les cardinalités du MCD ci-dessous pour faire apparaître leur description.
In [95]:
%%mocodo
Produit: réf. produit, libellé, prix unitaire
Inclure, 1N [Une commande inclut au moins un produit.] Commande, 0N [Un produit peut être commandé un nombre quelconque de fois.] Produit: quantité
Commande: num. commande, date, montant
DF, 0N [Un client peut passer zéro (prospect) ou plusieurs commandes.] Client, 11 [Une commande est passée par un et un seul client.] Commande
Client: réf. client, nom, adresse
Parrainer, 01 [Un client peut avoir été parrainé ou non.] Client, 0N [Un client peut parrainer d'autres clients.] Client : date parrainage
Nouveauté de la version 4.3. Avec l'option -t prompt:cards, Mocodo compose un prompt à copier-coller sous ChatGPT (ou autre) pour compléter le MCD avec de telles descriptions. Ce prompt est trop long pour être reproduit ici : il consiste en une présentation de la syntaxe de Mocodo, des instructions sur le travail demandé, plusieurs exemples corrigés, et enfin le MCD à compléter. Voici par exemple les explications générées par DeepSeek pour le MCD de la page d'accueil de Mocodo online :
In [96]:
%%mocodo
AYANT-DROIT: nom ayant-droit, lien
DIRIGER, 0N [Un employé peut diriger zéro ou un projet.] EMPLOYÉ, 01 [Un projet est dirigé par au plus un employé.] PROJET
REQUÉRIR, 1N [Un projet requiert au moins une pièce.] PROJET, 0N [Une pièce peut être requise par plusieurs projets.] PIÈCE: qté requise
PIÈCE: réf. pièce, libellé pièce
COMPOSER, 0N [Une pièce peut être composée de plusieurs autres pièces.] PIÈCE, 0N [Une pièce peut entrer dans la composition de plusieurs autres pièces.] PIÈCE: quantité
DF, _11 [Un ayant-droit est associé à un et un seul employé.] AYANT-DROIT, 0N [Un employé peut avoir plusieurs ayants-droit.] EMPLOYÉ
EMPLOYÉ: matricule, nom employé
PROJET: num. projet, nom projet
FOURNIR, 1N [Un projet est fourni par au moins une société.] PROJET, 1N [Une pièce est fournie par au moins une société.] PIÈCE, 1N [Une société fournit au moins une pièce pour un projet.] SOCIÉTÉ: qté fournie
DÉPARTEMENT: num. département, nom département
EMPLOYER, 11 [Un employé appartient à un et un seul département.] EMPLOYÉ, 1N [Un département emploie au moins un employé.] DÉPARTEMENT
TRAVAILLER, 0N [Un employé peut travailler sur plusieurs projets.] EMPLOYÉ, 1N [Un projet implique au moins un employé.] PROJET
SOCIÉTÉ: num. société, raison sociale
CONTRÔLER, 0N< [Une société peut en contrôler une autre.] SOCIÉTÉ, 01 [Une société est contrôlée par au plus une autre société.] SOCIÉTÉ
Le résultat est en général plus qu'honorable, mais demande à être relu attentivement pour vérifier l'adhérence au MCD original, corriger d'éventuelles hallucinations ou apporter des précisions. Ci-dessus, en l'occurrence, une société peut en contrôler plusieurs autres (et non une autre).
Différence syntaxique entre rôles et explications. Remarquez que la syntaxe est la même que pour les rôles, qui sont en plus utilisés lors du passage au relationnel. Comment Mocodo fait-il la différence ? En appliquant les règles suivantes :
si la note de patte commence par + ou -, ou qu'elle ne contient aucun espace, c'est un rôle.
sinon, c'est une description de cardinalité.
Explication de contraintes. Le même principe s'applique aux contraintes (survolez le Ⓘ) :
In [97]:
%%mocodo
Projet: num. projet, nom projet
:
Fournir, 1N Projet, 1N Pièce, 1N Société: quantité
Société: num. société, raison sociale
Requérir, 1N Projet, 0N Pièce: quantité
:
Pièce: réf. pièce, libellé pièce
(I) [Toute pièce fournie doit avoir été requise.] ..Pièce, ->Requérir, --Fournir, Projet
Limitations.
Non pris en charge par les éditeurs de SVG comme Inkscape.
Ne semble pas fonctionner dans une page HTML statique (comme la version HTML de ce document sous GitHub).
Nécessite de faire confiance à un notebook (Trust notebook) pour s'afficher à la réouverture.
Nouveauté de la version 3. Il est possible de faire apparaître progressivement les différentes « boîtes » constituant un MCD. Pour cela, il suffit d'indenter (décaler vers la droite à l'aide d'espaces ou de tabulations) au moins une ligne. Les éléments correspondants seront alors répartis sur autant de « calques » qu'il y a de niveaux d'indentations.
Voici par exemple un exercice consistant en la description du « réel perçu » d'une entreprise de VPC :
Un produit est connu par une référence, un libellé et un prix unitaire.
Toute commande inclut un produit ou plusieurs, chacun en une certaine quantité.
Un client peut passer zéro (client potentiel) ou plusieurs commandes.
Un client peut entrer dans la base par parrainage d'un autre client.
L'enseignant peut le présenter pas à pas en suivant les étapes de l'énoncé :
Pas de règle sur la taille de l'indentation. Pour Mocodo, autant de niveaux d'indentation distincts, autant de calques.
Les différents calques sont codés directement dans le SVG. L'interaction ne nécessite donc aucun logiciel spécifique.
Sous Mocodo online, le MCD est toujours présenté entièrement dévoilé. Cela permet de voir directement le résultat d'une modification du texte-source.
Pour ajouter facilement de l'interactivité à un MCD existant :
commencez par indenter au maximum toutes les lignes ;
effacez l'indentation des lignes du premier calque ;
décalez votre curseur de $n$ caractères vers la droite ;
placez-vous tour à tour sur les lignes à intégrer au deuxième calque et effacez les espaces surnuméraires ;
recommencez à l'étape 3 jusqu'au dernier calque.
Depuis la version 4.0, l'éditeur de Mocodo online vous permet de créer des curseurs multiples, ce qui simplifie encore ces opérations.
Limitations.
Pas de granularité plus fine que la ligne (entité ou association avec toutes ses pattes et cardinalités).
Pas de prise en charge des touches directionnelles. Cela serait sans doute possible, mais difficilement compatible avec la présence de plusieurs MCD interactifs sur une même page (comme dans cette documentation).
Éviter qu'une interaction sur un SVG ne s'applique à un autre.
Dans le cas très rare où plusieurs SVG interactifs générés à partir du même texte-source coexistent sur une même page web, une interaction opérée sur l'un s'applique également à tous les autres. Par exemple, cliquer sur l'un des ronds gris de l'une des figures ci-dessous agira sur les deux figures :
In [99]:
%%mocodo
FOO: foo
BAR: bar
In [100]:
%%mocodo
FOO: foo
BAR: bar
Ce problème trahit une « collision » : différents éléments du DOM se sont vus attribuer la même empreinte (obtenue par hachage du texte-source). La solution est de passer un entier discriminant qui, par concaténation, fera de ces empreintes de véritables identifiants.
Les gabarits de conversion en MLD (à savoir html, markdown, latex et text) admettent une sous-sous-option e qui accompagne le résultat par des explications détaillées du mécanisme de passage :
In [103]:
%mocodo -i ccp -t markdown:e
ccp_mld.md
Client (Réf. client, Nom, Prénom, Adresse)
Le champ Réf. client constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Client.
Les champs Nom, Prénom et Adresse étaient déjà de simples attributs de l'entité Client.
Le champ Num. commande constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Commande.
Les champs Date et Montant étaient déjà de simples attributs de l'entité Commande.
Le champ Réf. client est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle Passer à partir de l'entité Client en perdant son caractère identifiant.
Inclure (#Num. commande, #Réf. produit, Quantité)
Le champ Num. commande fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Commande.
Le champ Réf. produit fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité Produit.
Le champ Quantité était déjà un simple attribut de l'association Inclure.
Produit (Réf. produit, Libellé, Prix unitaire)
Le champ Réf. produit constitue la clé primaire de la table. C'était déjà un identifiant de l'entité Produit.
Les champs Libellé et Prix unitaire étaient déjà de simples attributs de l'entité Produit.
Nous avons essayé d'être aussi précis que possible, tout en « factorisant » avec soin les lignes consécutives suseptibles de l'être. Vous pouvez adapter ces explications à votre enseignement en modifiant une copie du gabarit html-ce.yaml, dont les autres sont dérivés.
Pour éviter le marquage automatique du premier attribut d'une entité comme identifiant, il suffit de le préfixer par un tiret bas (_) : ce caractère est donc un commutateur, qui souligne un attribut non souligné par défaut, et désouligne un attribut souligné par défaut.
Vous pouvez mettre deux virgules consécutives pour réserver la place d'un attribut manquant. Les espaces insécables sont préservés, ce qui permet de réserver plus d'espace horizontal, cf. ci-dessous premier attribut vide de INCLURE.
In [106]:
%%mocodo
CLIENT: Réf. client,,,
PASSER, XX CLIENT, XX COMMANDE
COMMANDE: , Date, Montant
INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité,,,,
PRODUIT: Réf. produit, Libellé, Prix unitaire
Régression de la version 4.0. Les espaces insécables ne sont plus préservés. Il n'y a donc plus d'autre moyen de réserver davantage d'espace horizontal que d'employer le style blank (paragraphe suivant).
Ne faire apparaître que le squelette du schéma conceptuel¶
Vous pouvez transformer en exercice à trous n'importe quel MCD en rendant complètement transparentes les couleurs des attributs, associations et cardinalités. Le style blank a été prédéfini à cet effet:
In [107]:
%mocodo -i ccp --colors=blank
Attention, n'utilisez cette méthode que pour la projection : l'information textuelle est toujours présente, susceptible d'être sélectionnée et collée ailleurs. Vous pouvez bien sûr empêcher cette possibilité en convertissant le SVG en PNG, mais le plus simple est d'appliquer une réécriture empty :
In [108]:
%mocodo -i ccp -t empty # équivalent de "delete:types,notes,attrs,cards"
ccp.mcd
%%mocodo
Client: , , ,
Passer, XX Client, XX Commande
Commande: , ,
Inclure, XX Commande, XX Produit:
Produit: , ,
Obfusquer un MCD consiste à vider celui-ci de sa sémantique de surface en substituant des chaînes aléatoires à tous les libellés. Cela permet de créer des exemples d'illustration de telle ou telle notion « pure » sans risquer de voir son public se focaliser sur des détails-métier (c'est l'équivalent de la variable méta-syntaxiquefoobar dans le contexte de la pédagogie de la programmation).
En argument, vous pouvez ajouter le chemin d'un fichier texte quelconque où puiser les mots de substitution. Par exemple, le texte du README de ce projet :
Mocodo essaie d'abord de trouver ce fichier à l'endroit indiqué. En cas d'échec, il le cherche (avec extension .txt facultative) parmi ceux distribués avec le logiciel, à savoir:
"lorem.txt" (6464 mots) : le faux-texte le plus courant, augmenté d'une sélection des 10000 mots latins les plus courants compilés par Kyle P. Johnson, le tout privé de ses doublons et des mots de moins de 3 lettres ("lorem_ipsum.txt" avant la version 4.0).
"fr.txt" (3396 mots) : une liste des 4000 mots français les plus courants, privée de ceux comportant une apostrophe ou moins de 4 lettres. Source : http://wortschatz.uni-leipzig.de/index.htmlviaWikitionary. Nouveauté de la version 4.0.
"fr5.txt" (464 mots): la liste de "fr.txt", restreinte aux mots de 5 lettres. Nouveauté de la version 4.0.
"en4.txt" (640 mots): une sélection (SFW) de mots anglais de quatre lettres ("four_letter_words.txt" avant la version 4.0).
"disparition.txt" (7489 mots) : le lexique du célèbre roman lipogrammatique de Georges Perec, privé des mots de moins de 4 lettres.
En cas de nouvel échec, il se rabat sur "lorem.txt".
NB. L'algorithme s'assure que la distance de Damerau-Levenshtein entre deux libellés de substitution quelconques est d'au moins 3. En clair, cela signifie que, si vous donnez en examen un exercice de conversion en relationnel basé sur un tel MCD, les erreurs de transcription d'un étudiant stressé, inattentif, illettré, dyslexique, roublard, ou tout cela à la fois, ne devraient pas vous empêcher de lui octroyer les points qui lui reviennent.
Des sous-options pré-définies (données ci-dessous avec leur valeur par défaut après le =) permettent de spécifier finement le nombre désiré :
d'associations réflexives (arity_1=2) ;
d'associations ternaires (arity_3=2) ;
d'associations quaternaires (arity_4=0) ;
d'associations doubles, i.e., associant le même couple d'entités (doubles=1) ;
d'identifiants composites (composites=1) ;
d'attributs maximum par entité (ent_attrs=4) ;
d'attributs maximum par association (assoc_attrs=2).
On ne peut pas préciser directement le nombre d'associations binaires : si le nombre total des autres associations spécifiées n'arrivent pas à n, elles viennent en complément.
Des sous-options de forme plus ou moins libre décrivent les cardinalités. Par exemple, _11-*N=2 créera deux entités faibles et /*N-*N un agrégat. Les associations de complément sont *N-*N.
Avec la sous-option from_scratch, le MCD de départ est vide. À titre d'exemple, voici la transformation complexe invoquée par Mocodo online pour créer un MCD d'entraînement à la conversion au relationnel, accompagné de cette dernière. Notez la création de rôles par défaut : ils permettent de simuler le rétablissement de la sémantique des associations disparues.
Il n'est pas impossible que le MCD résultant soit incorrect (p. ex., apparition d'une identification relative circulaire), mais les contrôles effectués a priori et a posteriori devraient dans la majorité des cas produire quelque chose de raisonnable.
Impressionnez votre public en accompagnant l'option -t share de --defer :
In [112]:
%mocodo -i ccp -t share --defer
ccp_url.svg
Limitation. Comme il faut un mobile pour scanner ce QR code, et que Mocodo online n'est pas vraiment adapté aux mobiles, l'intérêt réel de cette fonctionnalité est pour l'instant discutable.
Vous avez créé avec Mocodo toute une bibliothèque de MCD, certains pour illustrer des points de votre cours, d'autres comme solutions d'exercices. À partir de la version 4.1, vous pouvez donner accès à tel ou tel de ces MCD en communiquant simplement son nom à vos étudiants.
Pour comprendre le fonctionnement, accédons à un MCD de la bibliothèque par défaut : copiez-collez la chaîne def_weak-974e comme titre du MCD dans l'onglet Entrée de Mocodo online. Le texte-source est mis à jour avec le contenu d'un fichier def_weak-974e.mcd stocké en ligne :
Remarques.
Vous avez sans doute déjà utilisé cette fonctionnalité sans le savoir en parcourant le tutoriel, dont les MCD, appelés tuto-0000, tuto-0001, etc., sont récupérés de la même manière.
Tous les noms de fichiers de la bibliothèque par défaut se terminent par un code de quatre caractères alphanumériques : cette disposition évite qu'un utilisateur ne tombe dessus par hasard, et protège certains MCD (typiquement, les solutions d'exercices) de la curiosité naturelle des étudiants. Au moment de la correction, je leur donne simplement le nom complet, et ils récupèrent directement la solution, ce qui dégage du temps pour des activités plus éducatives que la recopie.
Bien sûr, la plupart des MCD de la bibliothèque par défaut n'intéressent que mon enseignement, et n'ont pas vocation à être diffusés au-delà du cercle très select des happy few qui en bénéficient.
Pour mettre votre propre bibliothèque à la disposition de vos propres étudiants :
placez-la dans un répertoire dédié d'un serveur sur lequel vous avez les droits ;
copiez-collez l'URL de ce répertoire dans le champ Bibliothèque de MCD (onglet Options). Désormais, c'est à cette adresse que Mocodo essaiera de trouver les MCD quand vous mettrez à jour leur titre ;
vos étudiants devront eux-mêmes avoir renseigné le champ en question, ce qui peut poser des problèmes à certains. Facilitez-leur la tâche en leur donnant le lien suivant (tout ce qui suit lib= doit bien sûr être remplacé par l'adresse de votre répertoire distant) :
L'option --input (ou -i) est surchargée pour aller chercher un MCD sur internet s'il ne se trouve pas dans le répertoire local. Pour commencer la démonstration, assurons-nous que le fichier en question n'existe pas en local :
Récupérons-le de façon transparente dans la bibliothèque par défaut (avec ou sans extension .mcd) :
In [114]:
%mocodo -i def_weak-974e.mcd
Notez qu'au passage, pour le rendre disponible hors ligne, le fichier a été sauvegardé sur votre machine :
In [115]:
path.is_file()
Out[115]:
True
NB. Si le paramètre de -i est un chemin de la forme path/to/file.mcd (avec ou sans extension .mcd), c'est la dernière partie du chemin (à savoir file.mcd) qui sera récupérée sur le serveur. Par contre, c'est le chemin complet qui déterminera l'emplacement de la sauvegarde.
Pour utiliser votre propre répertoire distant, passez son URL à l'option --lib :
Plusieurs styles prédéfinis sont distribués avec l'application. Un style se définit comme la combinaison d'une palette de couleurs (répertoire colors) avec un dictionnaire de polices et de dimensions (répertoire shapes).
Vous pouvez bien sûr créer vos propres styles en vous inspirant des fichiers fournis. Si vous êtes particulièrement content d'un style, soumettez-le pour inclusion dans une prochaine distribution.
Réglages. Une approximation de la largeur des caractères des différentes polices a été pré-calculée sous macOS. Il est possible qu'elle soit inexacte, en particulier sous Windows ou Linux. Dans ce cas, en particulier, les traits de soulignement n'atteindront pas ou excéderont la largeur des libellés soulignés. Vous pouvez contourner le problème en appliquant un facteur multiplicatif avec l'argument --adjust_width).
Il est possible de demander à Mocodo de chercher tout seul une « bonne » permutation des définitions des boîtes, ce qui à la main deviendrait vite difficile.
Le critère que nous avons retenu pour évaluer la qualité du tracé est double :
les liens ne doivent pas se couper ;
leur longueur cumulée doit être minimale.
Actuellement, deux algorithmes d'arrangement sont fournis :
un algorithme exact (bb, pour Branch & Bound), qui ne trouve que des solutions satisfaisant au premier critère ;
un algorithme approché (ga, pour Genetic Algorithm), réservé aux cas où il est impossible d'y satisfaire. L'algorithme va alors chercher des solutions où les liens se coupent seulement le moins possible.
Remarque. Mocodo met en œuvre une technique de réarrangement originale : en contraignant la position des boîtes aux intersections d'une grille invisible, il transforme un classique problème de plongement en un problème d'affectation, ce qui permet de satisfaire de façon efficace à un certain nombre de contraintes esthétiques pertinentes (planarité, compacité, etc.).
Nous illustrerons les algorithmes et leurs paramètres sur le MCD d'accueil de Mocodo online.
Le réarrangement automatique peut se faire sans contraintes, ou dans les limites d'une grille spécifiée par l'utilisateur.
Le réarrangement dit organique consiste à choisir une première boîte au hasard, puis à essayer d'agréger les autres sans se préoccuper de contenir le tout dans une grille prédéterminée :
Cela donne un plongement sur une grille $5\times4$, ce qui est loin d'être optimal. Cependant, l'arrangement organique fournit souvent un bon point de départ pour chercher soi-même une permutation plus esthétique ou mettant en évidence certaines propriétés du MCD.
Le réarrangement automatique des boîtes peut s'opérer dans les limites de la grille courante ; c'est-à-dire que le MCD résultant aura (au plus) le même nombre de colonnes et de rangées que le texte de départ (ici, $4\times5$) :
Dans un document paginé, on cherche en général à utiliser en priorité l'espace horizontal. La version 4.0 introduit à cet effet une option de réarrangement « en largeur d'abord ». Le MCD d'accueil s'y prête particulièrement bien :
Ici, l'utilisateur a demandé $8$ boîtes en largeur (c'est aussi la valeur par défaut). L'algorithme commence par calculer le nombre de lignes minimal correspondant, sachant qu'il a $14$ boîtes à placer : c'est $\lceil14/8\rceil=2$. Il cherche donc un plongement sur une grille $8\times2$.
On peut chercher à faire tenir le MCD dans la plus petite grille possible, tout en maintenant entre hauteur et largeur un rapport « équilibré » (ou proche du nombre d'or). Par exemple, un MCD de 13 boîtes (entités ou associations) peut tenir dans les grilles:
$13\times1$;
$7\times2$, ce qui laisse 1 case vide;
$5\times3$, ce qui laisse 2 cases vides;
$4\times4$, ce qui laisse 3 cases vides;
etc.
Les deux premières grilles étant non équilibrées, on retiendra la plus petite des suivantes, de dimensions $5\times3$.
La table ci-dessous énumère les dimensions des grilles minimales d'équilibre supérieur à 0,5 pour tous les MCD comportant moins de 100 boîtes. On peut y vérifier par exemple que le MCD de taille 13 (en gras) se trouve effectivement aux coordonnées (5, 3).
1
2
3
4
5
6
7
8
9
10
11
12
13
1
1
2
3
2
4
5, 6
3
7, 8, 9
10, 11, 12
13, 14, 15
4
16
17, 18, 19, 20
21, 22, 23, 24
26, 27, 28
5
25
29, 30
31, 32, 33, 34, 35
37, 38, 39, 40
43, 44, 45
6
36
41, 42
46, 47, 48
50, 51, 52, 53, 54
57, 58, 59, 60
65, 66
7
49
55, 56
61, 62, 63
67, 68, 69, 70
73, 74, 75, 76, 77
82, 83, 84
91
8
64
71, 72
78, 79, 80
85, 86, 87, 88
92, 93, 94, 95, 96
9
81
89, 90
97, 98, 99
10
100
Le MCD d'accueil ayant déjà été arrangé avec l'option -t arrange:balanced, nous ne le reproduisons pas ici. Sachez cependant qu'au cas où, pour une raison ou une autre, la plus petite grille équilibrée ne convient pas, il est possible de passer à la $i^\text{e}$ suivante en mettant $i$ en sous-sous-argument. Ici, pour $14$ boîtes, balanced=1 permet donc de passer de la grille $5\times3$ (prévue pour $13$, $14$ et $15$ boîtes) à la grille $4\times4$ (prévue pour $16$ boîtes).
Le réarrangement exact ne fonctionnera jamais sur les MCD non planaires ou n'admettant aucun plongement planaire dans les limites de la grille spécifiée (par les sous-options current, wide ou balanced).
Rappelons qu'un graphe est dit planaire lorsqu'il en existe au moins un arrangement sans croisement. Le graphe non planaire comportant le plus petit nombre de liens est connu sous le doux nom de $K_{3,3}$ :
In [120]:
%%mocodo
DIGNISSIM: nec sem, nunc, vulputate
IMPERDIET: a praesent, nibh, semper
TINCIDUNT: faucibus, orci, cursus
RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
Son réarrangement par Branch & bound échouera donc nécessairement :
In [121]:
%mocodo -i sandbox --seed=1 -t arrange:balanced
Mocodo Err.9 - Impossible de calculer un plongement planaire satisfaisant la contrainte
donnée.
Comme on voit, Mocodo ne cherche pas à savoir si la non-planarité est intrinsèque au graphe, ou résulte des dimensions de la grille imposée pour le plongement. Pour en avoir le cœur net, tentez un arrangement non contraint :
In [122]:
%mocodo -i sandbox --seed=1 -t arrange
Mocodo Err.41 - Impossible de calculer un plongement planaire.
NB. Plusieurs paramètres permettent de régler le fonctionnement de l'algorithme exact (Branch and Bound) :
call_limit : nombre maximal d'appels pour une boîte de départ donnée (défaut: 10000).
min_objective : meilleur objectif esthétique pour la mise en page (défaut: 0).
max_objective : pire objectif esthétique pour la mise en page (défaut: 15).
Réservés aux spécialistes, ils ne sont pas décrits dans cette documentation.
Dans tous les cas où la méthode exacte ne produit pas de résultat satisfaisant, on pourra se rabattre sur une heuristique qui, au lieu d'interdire les croisements, cherchera simplement à en minimiser le nombre.
In [123]:
%mocodo -i sandbox --seed=1 -t arrange:algo=ga
sandbox.mcd
%%mocodo --seed=1
DIGNISSIM: nec sem, nunc, vulputate
RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
IMPERDIET: a praesent, nibh, semper
SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
TINCIDUNT: faucibus, orci, cursus
QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
L'amélioration ne saute pas forcément aux yeux, mais il n'y a plus que trois croisements au lieu de neuf. Ce plongement constitue en tout cas un bon point de départ pour un réarrangement manuel. Il ne reste en effet plus qu'à insérer quelques boîtes invisibles:
In [124]:
%%mocodo
RHONCUS, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
DIGNISSIM: nec sem, nunc, vulputate
:::
IMPERDIET: a praesent, nibh, semper
SODALES, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
:::
QUIS ENIM, 1N DIGNISSIM, 1N IMPERDIET, 1N TINCIDUNT
TINCIDUNT: faucibus, orci, cursus
NB. Plusieurs paramètres permettent de régler le fonctionnement de l'algorithme génétique :
population_size : nombre d'individus à faire évoluer (défaut: 1000).
crossover_rate : taux de croisement, entre 0 et 1 (défaut: 0.9).
mutation_rate : taux de mutation, entre 0 et 1 (défaut: 0.06).
sample_size : taille de l'échantillon pour les tournois (défaut: 7).
max_generations : nombre maximal de générations (défaut: 300).
plateau : nombre maximal de générations consécutives sans amélioration (défaut: 30).
Réservés aux spécialistes, ils ne sont pas décrits dans cette documentation.
Une entité liée à de nombreuses associations peut limiter ou même empêcher les possibilités de réarrangement sans croisements.
Par duplication d'entités réduites à leur identifiant¶
Lorsque cette entité est indépendante et réduite à son identifiant, elle est vouée à disparaître lors du passage au relationnel. Il n'y a donc aucun inconvénient à en créer plusieurs, et cela peut avoir l'avantage de faciliter (ou même de rendre possible) l'obtention d'une bonne mise en page.
Le graphe sous-jacent étant non planaire, des croisements sont inévitables (ici entre certaines pattes des associations SHR, CT et THR). Cependant, cette non-planarité résulte elle-même de la mise en jeu d'une même entité Période dans quatre associations triples. La monnayer en quatre entités Période numérotées augmente l'ordre du graphe, mais le rend planaire, le tout sans impact sur le schéma relationnel :
Ce genre de problème (et sa solution) se présente typiquement lorsqu'une entité DATE réduite à un identifiant date est associée à un grand nombre d'entités n'ayant rien à voir entre elles.
Inversement, toujours étant donné que ce type d'entité est destiné à disparaître lors du passage au relationnel, on peut décider de les supprimer par anticipation. Pour cela, il faut accepter que les associations puissent être porteuses d'identifiants (ce qui n'est pas prévu par Merise). Cela permet d'obtenir un schéma conceptuel beaucoup plus lisible, toujours sans impact sur le schéma relationnel :
Notez qu'avec cette technique, les cardinalités minimales des pattes distinguées par les entités supprimées sont perdues dès le niveau conceptuel (sachant qu'elles l'auraient été de toute façon lors du passage au relationnel).
Comme la notion d'identifiant explicite d'association enfreint le standard Merise, et risque de semer la discorde entre professeurs et étudiants, elle est par défaut désactivée sous Mocodo online. En ligne de commande, elle est interdite à la demande :
In [127]:
%mocodo -i sandbox --no_assoc_ids
Mocodo Err.52 - L'association « CSG » ne peut pas avoir d'identifiant.
La mise en forme des clés étrangères affichées dans le diagramme relationnel peut être paramétrée, par exemple pour enlever le préfixe par défaut (#) :
Comme le cercle est alors un peu plus grand, on peut vouloir régler (a priori une fois pour toutes) le ratio défini dans l'objet shapes du fichier appelé par défaut sandbox_geo.json):
"df_text_height_ratio" : 1.00,
Basculement des cardinalités et inflexion des pattes rectilignes¶
Mocodo est capable de détecter certaines configurations de pattes dont les cardinalités présentent un risque élevé de collision. Il procède alors à deux types d'ajustements:
Issue 25. Les cardinalités d'une patte verticale ou horizontale sont envoyées de l'autre côté de la patte. Par exemple, dans le plongement du MCD précédent, la présence de la patte oblique BLANDIT-VIVAMUS envoie les cardinalités de VELIT-BLANDIT à l'opposé de leur position par défaut (à droite d'une patte verticale ou en bas d'une patte horizontale).
Issue 27. Les pattes obliques sont infléchies de façon à ménager plus d'espace pour afficher deux couples de cardinalités. Ici, l'inflexion de la patte DF-CONGUE permet à ses cardinalités de coexister sans problème avec celles de l'association réflexive.
Ces ajustements automatiques résolvent les problèmes les plus courants. Toutefois, étant antérieurs au tracé proprement dit, ils peuvent seulement réduire les risques de collision, et non les prévenir totalement. Ils peuvent même en produire d'autres. Ainsi, autour des entités monstrueusement pattues, des collisions qui ne se seraient pas produites par défaut seront parfois observées. L'utilisateur a alors deux possibilités:
diminuer la valeur du paramètre --flex (par défaut, 0.75) pour réduire la courbure de l'inflexion automatique, en allant jusqu'à 0 pour la désactiver totalement ;
modifier à la main les positions des cardinalités en conflit, comme expliqué dans la section suivante.
La possibilité de rendre un trait invisible peut être exploitée pour « attirer » une contrainte vers telle ou telle boîte existante, soit en la répétant dans la définition de la contrainte, soit en l'y intégrant (fictivement) si elle n'en fait pas partie.
Par exemple, ci-dessous, répéter l'association Stocker et intégrer l'entité Commande permet de réduire la largeur du MCD sans compromettre sa lisibilité :
Ces réglages restent relativement fragiles. Une façon d'assurer qu'ils survivront à un réarrangement est de faire coïncider le centre d'une contrainte avec celui d'une boîte supplémentaire, que nous appelons ici « Z » :
In [132]:
%%mocodo
:::
Dépôt: num dépôt, surface
:
Louer, 11 Commande, 0N Dépôt
Z, XX Commande, XX Article, XX Dépôt
Stocker, 1N Dépôt, 1N Article: quantité
Commande: num. commande, date
Composer, 1N Commande, 0N Article
:
Article: réf. article, prix
(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer: Z, Z
Il ne reste plus qu'à rendre invisibles l'association Z et ses pattes en préfixant Z d'un signe moins - :
In [133]:
%%mocodo
:::
Dépôt: num dépôt, surface
:
Louer, 11 Commande, 0N Dépôt
-Z, XX Commande, XX Article, XX Dépôt
Stocker, 1N Dépôt, 1N Article: quantité
Commande: num. commande, date
Composer, 1N Commande, 0N Article
:
Article: réf. article, prix
(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer: Z, Z
Cette technique résiste très bien aux réarrangements automatiques.
... ou au contraire de la forcer quand elle est superfétatoire :
In [135]:
%%mocodo --gutters ids:visibility=on
CLIENT: Réf. client, Nom, Prénom, Adresse, Mail
Vous pouvez aussi préciser quelles chaînes dénoteront un identifiant fort (par défaut, strong=ID), un identifiant faible (par défaut, weak=id) ainsi que les numéros des groupes d'identifiants alternatifs (par défaut, alts=123456789, mais vous pouvez lister moins de 9 symboles) :
Le type de flèche spécifiant le mécanisme de passage au modèle relationnel (<=, <-, -> ou =>) fait par défaut l'objet d'une visualisation dès le modèle conceptuel : la ou les pattes vers les entités de destination des attributs migrants sont orientées vers celles-ci ; et la ou les autres pattes sont doublées (pour <= et =>) ou non (pour <- et ->).
In [137]:
%%mocodo -t
Personne: num SS, nom, prénom
/XT\ Personne => Homme, Femme: sexe
Homme:
:
Femme: nom de jeune fille
Femme (num SS, nom, prénom, nom de jeune fille)
Homme (num SS, nom, prénom)
Cette visualisation n'est pas conventionnelle : on se borne normalement à ajouter une flèche dirigée vers l'entité-mère.
Pour désactiver l'embellissement opéré par Mocodo, sans pour autant changer le mécanisme de passage au relationnel, prolongez simplement d'un caractère la flèche : <==, <--, --> ou ==> :
In [138]:
%%mocodo -t
Personne: num SS, nom, prénom
/XT\ Personne ==> Homme, Femme: sexe
Homme:
:
Femme: nom de jeune fille
Femme (num SS, nom, prénom, nom de jeune fille)
Homme (num SS, nom, prénom)
Changement de la version 4.0. Précédemment, --> et ==> s'écrivaient respectivement ->> et =>>.
Lors du plongement, Mocodo génère systématiquement un fichier (intitulé par défaut sandbox_geo.json) répertoriant les positions les plus importantes du dessin. Les autres coordonnées sont calculées relativement à celles-ci.
un couple de dimensions width et height définit la taille du MCD ;
deux listes de couples cx et cy, les abscisses et ordonnées des centres des boîtes ;
un dictionnaire shift, les positions relatives des cardinalités par rapport à leur position par défaut ;
une liste ratio, les positions des flèches éventuelles : leur valeur peuvent varier de 0.0 (flèche cachée sous la boîte d'origine) à 1.0 (par défaut, pointe de la flèche au contact du bord de la boîte de destination, compte non tenu de l'arrondi s'il s'agit d'une association).
Copions-collons ce texte, apportons-lui quelques modifications et sauvegardons-le :
Il suffit maintenant d'ajouter l'option --reuse_geo pour appliquer ces modifications :
In [142]:
%mocodo -i sandbox --colors brewer+1 --reuse_geo
Bien entendu, ce genre de manipulations doit rester exceptionnel. Il est en tout cas à réserver à la toute dernière étape de production d'un MCD, puisque la plupart des évolutions subséquentes de son texte-source frapperaient d'obsolescence votre fichier de géométrie.
NB. Étant donné que Mocodo produit des SVG, rien ne vous empêche de retoucher ceux-ci avec un logiciel de dessin vectoriel dédié, comme Inkscape (libre) ou Adobe Illustrator, Freehand, CorelDRAW, etc. Attention, le SVG généré n'est pas spécialement pensé pour cet usage. Certains éléments sont groupés pour permettre leur déplacement en bloc, mais les liens ne suivent pas ces déplacements, ce qui peut vite devenir fastidieux.
Attention, seules les polices de caractères les plus courantes apparaîtront correctement sur toutes les plateformes.
Le second est un format bitmap, donc avec une certaine perte de qualité :
In [145]:
display.Image("ccp.png")
Out[145]:
Mentionnons une possibilité amusante, actuellement non intégrée à Mocodo : Svg2Rough.js pourra donner à votre travail une touche finale plus informelle, dans un style « tracé à main levée ».
Aussi surprenant que cela puisse paraître, tout MCD peut être transformé en un MCD équivalent dont les seules associations sont des dépendances fonctionnelles binaires non porteuses d'attributs, telles que celle ci-dessous :
In [147]:
%%mocodo
ENTITÉ 1_: at 1 1, at 1 2, at 1 3
DF, ?1 ENTITÉ 1_, XX ENTITÉ 2_
ENTITÉ 2_: at 2 1, at 2 2, at 2 3
La transformation peut demander jusqu'à trois opérations que Mocodo appelle drain, split et explode, et que nous allons découvrir dans la suite.
Stage (num. stage, sujet, #nom entreprise, date proposition)
Le MCD ci-dessus comporte trois associations de dépendance fonctionnelle porteuses d'attributs. Certains auteurs considèrent cela comme une anomalie. En tout état de cause, Mocodo peut les déplacer dans les entités distinguées par les cardinalités 11. Nous appelons cette opération un « drainage des dépendances fonctionnelles » (option -t drain). Notez bien que ces deux MCD sont strictement équivalents, et produisent le même MLD.
Stage (num. stage, sujet, date proposition, #nom entreprise)
La première version peut être préférée pour des raisons de localité sémantique (date proposition dans PROPOSER), ou honnie parce qu'elle complique la définition d'identifiant d'association. Mocodo n'a pas d'opinion sur la question. Par contre, il se refuse à procéder au drainage via les cardinalités 01. Dans le MCD ci-dessus, le placement de note stage dans SOUTENIR indique qu'un étudiant peut ne pas se voir attribuer de note de stage ; si elle était mise dans ÉTUDIANT, la connaissance du fait que ce champ autorise la valeur NULL serait perdue.
Toute association non DF d'arité $n$ peut être décomposée en un ensemble de $n$ associations DF : elle-même se voit alors remplacée par une entité liée aux $n$ entités originales. Cette nouvelle entité est appelée « entité-intersection », « entité-associative » ou « Gerund », cf. Song, Il-Yeol & Evans, Mary & Park, Eui Kyun. A Comparative Analysis of Entity-Relationship Diagrams. Journal of Computer and Software Engineering. 3 (1995). Dans les cours de Laurent Audibert ou de Patrick Bergougnoux, l'opération est illustrée à l'aide du MCD suivant :
Considérons le MCD suivant, qui comporte deux associations binaires non DF, l'une porteuse d'attribut, l'autre non :
In [155]:
%%mocodo -t
COMMANDE: num. commande, date, montant
LIGNE COM., 1N COMMANDE, 0N PRODUIT: quantité
PRODUIT: réf. produit, libellé, prix unitaire
DISPONIBILITÉ, 1N PRODUIT, 0N PAYS
PAYS: code pays, nom pays
COMMANDE (num. commande, date, montant)
DISPONIBILITÉ (#réf. produit, #code pays)
LIGNE COM. (#num. commande, #réf. produit, quantité)
PAYS (code pays, nom pays)
PRODUIT (réf. produit, libellé, prix unitaire)
Par souci de concision, quand on parle d'associations $n$-aires dans ce document, c'est par opposition aux associations binaires ; mais bien sûr, la règle de décomposition énoncée à la section précédente est également valable pour $n=2$, même si par défaut elle ne s'applique qu'aux associations d'arité minimale 3. En réduisant la valeur de arity à 2, toutes les associations binaires non DF seront également décomposées :
LIGNE COM. (id. ligne com., quantité, #num. commande, #réf. produit)
PAYS (code pays, nom pays)
PRODUIT (réf. produit, libellé, prix unitaire)
Cette décomposition devient un peu extrême ! Vous pouvez conserver certaines associations binaires non DF, à savoir celles qui ne portent aucun attribut, en mettant arity à 2.5 (à lire comme « intermédiaire entre 2 et 3 ») :
Ci-dessus, le processus de modification a créé un identifiant id. ligne com. qui peut sembler superfétatoire. Si l'on a introduit la notion d'entité faible, on aura ici avantage à utiliser l'option weak :
... se verra décomposé de la même façon que sans agrégat, à ceci près que la patte distinguée par le "/" produira une cardinalité 11 au lieu d'une cardinalité _11 :
Cette décomposition peut quelquefois se simplifier. Ici, on remarque que l'entité DATE est indépendante et réduite à son identifiant. Si l'on anticipe sur le passage au relationnel, on peut donc la supprimer, après avoir pris soin, bien sûr, de déplacer dans RÉSERVATION son unique attribut. Celui-ci renforçant une entité faible, il doit garder son caractère identifiant.
Comme l'indique le nom que nous leur avons malicieusement attribué, certaines de ces décompositions peuvent littéralement faire exploser la taille du MCD. Cependant :
elles sont intéressantes d'un point de vue terroriste théorique ;
elles peuvent permettre la détection d'erreurs de conception ;
elles peuvent conduire à des améliorations fines du MCD (c'est ce qu'on a fait sur celui des réservations de chambre) ;
elles décrivent le MCD dans un langage volontairement appauvri, ce qui le rendra plus aisément portable dans une autre notation, comme UML ou crow's foot.
La convention Look Across s'oppose à la convention Look Here de Merise. Elle est plus répandue internationalement.
Nous allons l'étudier par le biais du formalisme graphique de Chen, auquel cette section pourra du même coup servir d'introduction pour les personnes familières avec Merise.
Considérons un MCD Merise avec une association de dépendance fonctionnelle :
In [162]:
%%mocodo
Employé: employé
Travailler, 11 Département, 1N Employé
Département: département
Dans le formalisme graphique de Chen, cela donnera :
In [163]:
%mocodo -i sandbox -t chen:layout=circo --defer
sandbox_erd_chen.svg
Le 1 et le N correspondent aux cardinalités maximales du MCD Merise. Comme on voit, elles sont permutées. Merise suit la convention Look Here (LH) ; la notation de Chen, Look Across (LA). Comme cette dernière est la plus répandue dans le monde anglo-saxon, nous parlerons par commodité d'« ERD » (Entity-relationship diagram) pour les modèles conceptuels selon Chen, et toujours de « MCD » pour ceux de Merise.
Considérons maintenant les deux versions d'une association avec des cardinalités minimales distinctes :
In [164]:
%%mocodo
Mer: mer
Recevoir, 01 Rivière, 1N Mer
Rivière: rivière
In [165]:
%mocodo -i sandbox -t chen:layout=circo --defer
sandbox_erd_chen.svg
Les cardinalités maximales sont toujours permutées ; par contre, les cardinalités minimales ne le sont pas. Dans l'ERD, elles sont figurées par un trait simple (0) ou double (1). On les appelle aussi des « participations » : une rivière peut ne participer (directement) à l'alimentation d'aucune mer (ce n'est pas un fleuve) ; toute mer participe à la réception d'une rivière (un fleuve) minimum. Les conventions sont donc les suivantes :
Cardinalités
MCD Merise
ERD Chen
minimale
LH
LH
maximale
LH
LA
La notation Merise a l'avantage de la cohérence ; celle de Chen en a d'autres, que nous verrons un peu plus loin.
Avant cela, voyons un dernier point de détail : lorsque deux cardinalités N se trouvent de part et d'autre d'une association binaire, l'une est notée M :
L'entité faible, à l'instar de la DF « renforçante », sont entourées d'un double trait.
Le vocabulaire de Chen comporte encore un élément graphique, que Merise ne distingue pas spécialement. Nous avons vu que toute association non DF pouvait être décomposée de façon équivalente par l'insertion d'entités faibles :
In [ ]:
%%mocodo
Produit: produit
DF, _11 Ligne de commande, 0N Produit
Ligne de commande: _quantité
DF, _11 Ligne de commande, 1N Commande
Commande: commande
LIGNE DE COMMANDE est quelquefois qualifiée d'« entité associative », et Chen la figure logiquement par un losange (association) inscrit dans un rectangle (entité). Graphviz ne prend actuellement pas en charge cette représentation. Nous l'approximons donc ainsi :
Passons maintenant aux choses sérieuses. Jusqu'ici, la différence entre les deux notations était purement cosmétique. Qu'en est-il des associations n-aires, et que signifie Look Across quand across peut désigner plusieurs endroits ?
Quels sont les autres cas ? Il ne s'agit pas de ceux où l'association n-aire est entourée d'une ou plusieurs cardinalités maximale 1, puisqu'on pourrait alors la décomposer en DF binaires (cf. argument split). En réalité, on touche ici aux limites de la notation Look Here, plus intuitive, mais moins puissante que Look Across, et que Merise n'a pu rattraper que dans sa version 2.
Pour répondre progressivement à la question, procédons désormais dans l'ordre inverse, en présentant d'abord l'ERD, et ensuite seulement le MCD équivalent.
Les exemples qui suivent (ainsi d'ailleurs que le précédent), sont adaptés des pages 28 sqq. et 96 sqq. de l'ouvrage de Toby J. Teorey, Sam S. Lightstone, Tom Nadeau, H.V. Jagadish, Database Modeling and Design - Logical Design, 5th Edition - February 10, 2011 (Elsevier).
Le premier ERD apportant véritablement quelque chose de nouveau a cette structure :
In [173]:
%%mocodo -t chen:layout=circo,mindist=1,scale=0.45 --defer
Ingénieur: ingénieur
Gérer, /1N Responsable, 1N Ingénieur, 1N Projet
Projet: projet
Responsable: responsable
sandbox_erd_chen.svg
Les auteurs explicitent ainsi ses règles de gestion :
Chaque ingénieur travaillant sur un projet particulier a exactement un responsable, mais chaque responsable d'un projet peut gérer plusieurs ingénieurs, et chaque responsable d'un ingénieur peut gérer cet ingénieur sur plusieurs projets.
Reprenons ces trois assertions, et faisons-les correspondre au trois cardinalités dans le sens anti-horaire à partir du 1 :
1 : pour un couple (ingénieur, projet) donné, un seul responsable possible ;
N : pour un couple (responsable, projet) donné, plusieurs ingénieurs possibles ;
N : pour un couple (responsable, ingénieur) donné, plusieurs projets possibles.
Avec Merise, les assertions seraient plutôt du type : « pour un x donné, tant de couples (y, z) possibles ». Il y a donc bien permutation, mais permutation des références au singleton et au $n-1$-uplet.
C'est la réponse à notre question initiale, et nous verrons avec plaisir qu'elle reste valable quel que soit le triplet de cardinalités de l'association ternaire.
Pour l'instant, résumons les règles de gestion par cette unique dépendance fonctionnelle :
(Ingénieur, Projet) $\implies$ Responsable.
Pour exprimer cela en (ou en dépit de) Look Here, on doit recourir à un agrégat :
In [174]:
%mocodo -i sandbox --mld --colors pond
Gérer (ingénieur, projet, responsable)
… ou à une CIF à unicité complète (ici en notation allégée) :
Les schémas relationnels produits permettent de se convaincre que ces différentes variantes respectent bien la dépendance fonctionnelle désirée : (Ingénieur, Projet) $\implies$ Responsable.
%%mocodo -t chen:layout=circo,mindist=1,scale=0.45 --defer
Projet: projet
Affecter, /1N Site, /1N Projet, 0N Employé
Site: site
Employé: employé
sandbox_erd_chen.svg
Règles de gestion :
Chaque employé affecté à un projet travaille sur un seul site pour ce projet, mais peut se trouver sur différents sites pour différents projets. Sur un site donné, un salarié ne travaille que sur un seul projet. Sur un site donné, il peut y avoir plusieurs employés affectés à un projet donné.
Autrement dit (en partant du bas et dans le sens anti-horaire) :
1 : pour un couple (projet, employé) donné, un seul site possible ;
1 : pour un couple (employé, site) donné, un seul projet possible ;
N : pour un couple (projet, site) donné, plusieurs employés possibles.
En résumé, deux dépendances fonctionnelles :
(Projet, Employé) $\implies$ Site.
(Employé, Site) $\implies$ Projet.
Voici le MCD correspondant, successivement avec des agrégats et des CIFS (plus de variante possible avec entité faible) :
In [178]:
%mocodo -i sandbox --select all -t mld:c --colors pond
On voit apparaître dans le schéma relationnel un « u1 » en exposant qui signifie : contrainte d'unicité n°1. Il y a en effet deux clés candidates : (projet, employé) et (employé, site). Seule l'une des deux est devenue clé primaire, mais cela ne veut pas dire qu'on a perdu la contrainte de dépendance fonctionnelle induite par l'autre. En SQL :
%%mocodo -t chen:layout=circo,mindist=1,scale=0.45 --defer
Technicien: technicien
Utiliser, /1N Technicien, /1N Carnet, /1N Projet
Projet: projet
Carnet: carnet
sandbox_erd_chen.svg
Règles de gestion :
Un technicien utilise exactement un carnet pour chaque projet. Chaque carnet appartient à un technicien pour chaque projet. Notez qu'un technicien peut toujours travailler sur plusieurs projets et gérer différents carnets pour différents projets.
Autrement dit (en partant du haut et dans le sens horaire) :
1 : pour un couple (technicien, projet) donné, un seul carnet possible ;
1 : pour un couple (carnet, projet) donné, un seul technicien possible ;
1 : pour un couple (carnet, technicien) donné, un seul projet possible.
En résumé, trois dépendances fonctionnelles :
(Technicien, Projet) $\implies$ Carnet.
(Carnet, Technicien) $\implies$ Projet.
(Carnet, Projet) $\implies$ Technicien.
In [182]:
%mocodo -i sandbox --select all -t mld:c --colors pond
Notre syntaxe /1N pour les agrégats permet d'exprimer simplement toutes les combinaisons possibles de cardinalités d'une association n-aire dans la notation de Chen : il suffit de mettre un /1N partout où Chen met un 1. Chaque /1N créera une nouvelle dépendance fonctionnelle, autrement dit, une nouvelle clé candidate.
Plus rigoureusement : supposons une association $n$-aire $A$ exclusivement entourée de 1N (ou 0N). Soient $E_1$, $E_2$, ..., $E_n$ les entités mises en jeu, et $k_1, k_2$, ..., $k_n$ leurs identifiants respectifs. Alors, noter /1N (ou /0N) la cardinalité de la patte de $E_i$ dénote l'existence de la dépendance fonctionnelle suivante : $(k_1, ..., k_{i-1}, k_{i+1}, ..., k_n) \implies k_i$, autrement dit : que $(k_1, ..., k_{i-1}, k_{i+1}, ..., k_n)$ est une clé candidate de la table $A$.
Notez pour finir que Mocodo permet de spécifier à coût zéro quelle clé candidate sera élue clé primaire. Il suffit pour cela de placer en tête de la liste des entités mises en jeu par $A$ celle dont l'identifiant ne doit pas entrer dans la clé primaire. Si c'est $E_1$, la clé primaire sera automatiquement $(k_2, ..., k_n)$.
usage: mocodo [--language CODE] [--params_path PATH] [--input PATH] [--help]
[--version] [--restore] [--lib [URL]] [--output_dir PATH]
[--encodings [STR ...]] [--svg_to {png,pdf} [{png,pdf} ...]]
[--print_params] [--reuse_geo] [--uid_suffix INT]
[--select [{all,mcd,rw,source,text,code,mocodo,cv,mld,ddl,sql} ...]]
[--defer [STR ...]] [--mld] [--transform [STR ...]]
[--seed [FLOAT]] [--title STR] [--df STR] [--card_format [STR]]
[--fk_format [STR]] [--strengthen_card [STR]] [--flex FLOAT]
[--colors STEM_OR_PATH] [--shapes STEM_OR_PATH] [--scale RATE]
[--adjust_width RATE] [--detect_overlaps] [--no_assoc_ids]
[--gutters STR [STR ...]]
NOM :
Mocodo - Un générateur de diagrammes entité-association.
DESCRIPTION :
Mocodo est un outil libre destiné à l'enseignement des bases de données relationnelles.
Il prend en entrée une description textuelle des entités et associations d'un diagramme
entité-association (MCD). Il produit en sortie un dessin vectoriel en SVG et un schéma
relationnel dans divers formats (SQL, LaTeX, Markdown, etc.).
NOTE :
Chacune des valeurs suivantes est :
- spécifiée explicitement par l'utilisateur comme option de ligne de commande ;
- sinon, récupérée depuis un fichier de chemin --params_path ;
- sinon, récupérée depuis un fichier nommé « params.json » dans le répertoire d'entrée ;
- sinon, calculée à partir d'une valeur par défaut, éventuellement dépendante de votre système.
OPTIONS SUR MOCODO LUI-MÊME :
--language CODE force la localisation des messages avec le code de
langue donné (par exemple, « fr », « en », ...)
(default: fr)
--help affiche ce message d'aide, puis termine
--version affiche le numéro de version, puis termine
--restore recrée une version vierge des fichiers « sandbox.mcd »
et « params.json » dans le répertoire d'entrée, puis
termine (default: False)
ENTRÉE/SORTIE :
--params_path PATH le chemin du fichier de paramètres. S'il est omis,
utilise « params.json » dans le répertoire d'entrée.
S'il n'existe pas, utilise les paramètres par défaut.
(default: params.json)
--input PATH, -i PATH
le chemin du fichier d'entrée. Par défaut, les
fichiers de sortie seront générés dans le même
répertoire (default: /Users/aristide/Dropbox/Sites/moc
odo/doc/mocodo_notebook/sandbox.mcd)
--lib [URL] remote directory to use as fallback when the input
file is not found locally (default:
https://mocodo.net/web/lib)
--output_dir PATH le répertoire des fichiers de sortie (default: .)
--encodings [STR ...]
un ou plusieurs encodages à essayer successivement
lors de la lecture du fichier d'entrée (default:
['utf8', 'macroman'])
--svg_to {png,pdf} [{png,pdf} ...]
génère une version PNG ou PDF de la sortie SVG
(requiert CairoSVG) (default: [])
--print_params affiche le contenu du fichier de paramètres, puis
termine (default: False)
--reuse_geo réutilise le fichier de géométrie de l'exécution
précédente (default: False)
--uid_suffix INT ajoute un discriminateur à un SVG interactif (default:
0)
--select [{all,mcd,rw,source,text,code,mocodo,cv,mld,ddl,sql} ...]
sous Jupyter Notebook, spécifie explicitement les
catégories de résultats à afficher
--defer [STR ...] utilise un service web externe pour convertir les
résultats de la conversion dans les formats graphiques
donnés
--mld alias de compatibilité ascendante pour « -t » (sans
arguments). Équivalent à « -t markdown » mais, sous
Jupyter Notebook, n'empêche pas le rendu du diagramme
conceptuel dans la sortie de la cellule (default:
False)
--transform [STR ...], -t [STR ...]
crée une nouvelle version du MCD en appliquant
séquentiellement les opérations de réécriture données,
et/ou le convertit dans les formats ou langages
donnés. Sous Jupyter Notebook, « -T » remplace
respectivement la cellule courante par le résultat
textuel, ou le copie dans le presse-papier (pip3
install pyperclip)
--seed [FLOAT] valeur initiale pour le générateur de nombres
aléatoires (default: None)
--title STR nom du modèle, utilisé à divers endroits (système de
fichiers, base de données, etc.) (default: MCD)
ASPECT DE LA SORTIE GRAPHIQUE :
--df STR l'acronyme à entourer dans une dépendance
fonctionnelle (default: DF)
--card_format [STR] chaîne de formatage pour les cardinalités minimales et
maximales (default: {min_card},{max_card})
--fk_format [STR] chaîne de formatage pour les clés étrangères dans le
diagramme relationnel (default: #{label})
--strengthen_card [STR]
chaîne pour les cardinalités relatives (default:
_1,1_)
--flex FLOAT incurve les pattes rectilignes dont les cardinalités
peuvent se chevaucher (default: 0.75)
--colors STEM_OR_PATH
la palette de couleurs à utiliser lors de la
génération du dessin. Nom (sans extension) d'un
fichier situé dans le répertoire « colors », ou chemin
vers un fichier personnel (default: bw)
--shapes STEM_OR_PATH
spécification des polices, dimensions, etc. Nom (sans
extension) d'un fichier situé dans le répertoire
« shapes », ou chemin vers un fichier personnel
(default: copperplate)
--scale RATE applique au diagramme le facteur de mise à l'échelle
donné (default: 1)
--adjust_width RATE applique à tous les textes calculés le facteur de mise
à l'échelle donné (default: 1)
--detect_overlaps lève une erreur quand des pattes horizontales ou
verticales se chevauchent (default: False)
--no_assoc_ids interdit l'utilisation d'identifiants dans les
associations (conformément au standard Merise)
(default: False)
--gutters STR [STR ...]
définit la visibilité et le contenu des gouttières
latérales
VOIR AUSSI :
Version en ligne https://mocodo.net
Code source https://github.com/laowantong/mocodo
Documentation https://laowantong.github.io/mocodo/doc/fr_refman.html
Aide-mémoire pour -t https://github.com/laowantong/mocodo/blob/master/doc/fr_cheat_sheet.md
LICENCE : MIT
CONTACT :
Auteur Aristide Grange
Adresse Université de Lorraine
Laboratoire LCOMS - UFR MIM
3 rue Augustin Fresnel
57070 METZ Technopôle
France
Courriel <prénom.nom>@univ-lorraine.fr
Pour éviter d'avoir à invoquer Mocodo répétitivement avec une longue kyrielle d'options, vous pouvez mettre celles-ci dans un fichier params.json situé dans le répertoire de lancement de Mocodo. La commande:
mocodo --restore
... le fait pour vous avec un fichier de paramètres vide, i.e., un fichier-texte réduit aux deux caractères {} (attention, elle rétablit aussi le fichier sandbox.mcd à son contenu par défaut).
Pour que le style de vos MCD soit maintenu à moindre frais à travers tous vos documents, vous êtes encouragés à modifier ce fichier de paramètres selon vos goûts et vos besoins. Mocodo peut même vous aider à le faire en exécutant la cellule suivante:
%mocodo --print_params
Son évaluation remplace son propre contenu par des lignes de code similaires à :
In [186]:
# You may edit and run the following linesimportjson,pathlibparams="""\{ "adjust_width": 1, "card_format": "{min_card},{max_card}", "colors": "bw", "detect_overlaps": false, "df": "DF", "encodings": [ "utf8", "macroman" ], "fk_format": "#{label}", "flex": 0.75, "language": "fr", "lib": "https://mocodo.net/web/lib", "mld": false, "no_assoc_ids": false, "output_dir": ".", "restore": false, "scale": 1, "seed": null, "shapes": "copperplate", "strengthen_card": "_1,1_", "svg_to": [], "title": "MCD", "uid_suffix": 0}"""try:json.loads(params)except:raiseRuntimeError("Invalid JSON. Check your syntax on https://jsonlint.com.")pathlib.Path("./params.json").write_text(params,encoding="utf8");
Ensuite :
Modifiez la variable params à votre gré en respectant la syntaxe JSON.
Exécutez la cellule pour créer un fichier de nom et emplacement adéquats.
En cas de besoin, vous pourrez toujours ponctuellement passer outre ces réglages en en précisant d'autres en ligne de commande. Plus précisément, chaque paramètre est déterminé:
par sa valuation en ligne de commande;
à défaut, par sa valuation dans le fichier de paramètres indiqué par --params_path;
à défaut d'une telle indication, par sa valuation dans le fichier params.json du répertoire courant;
à défaut, par une valeur défaut éventuellement dépendante de votre système.
L'API de Mocodo a été revue de façon radicale pour la version 4.0. Le nombre d'options de premier niveau a été réduit ; en contrepartie, certaines sont devenues de véritables mini-commandes, avec leur propre liste d'arguments, et une syntaxe et un comportement uniformes. Voici leur définition en ABNF :
Introduisons, sur un exemple fictif, un vocabulaire que nous avons essayé d'employer le plus rigoureusement possible tout au long de cette documentation (et dans le code lui-même) :
mocodo --select all --transform drain arrange:wide explode:weak,arity=2.5
La commandemocodo est invoquée avec les options--select et --transform.
On passe à la seconde trois arguments de complexité croissante, drain, arrange:wide et explode:weak,arity=2.5. Notez que le séparateur des arguments est l'espace. Par conséquent, un argument ne peut contenir d'espaces qu'entre guillemets.
Chacun des arguments consiste en un nom de sous-option (drain, arrange ou explode), suivi éventuellement d'un deux-points (:), suivi d'une liste de sous-arguments séparés par des virgules (,).
Chacun des sous-arguments consiste en un nom de sous-sous-option, suivi éventuellement d'un signe égal (=), suivi d'un sous-sous-argument (éventuellement entre guillemets).
On n'ira pas plus loin et, heureusement, la plupart des commandes réelles n'atteignent pas ce degré d'imbrication. La décomposition suivante fixera peut-être les idées, ou pourra du moins servir de référence en cas de doute :
L'intérêt de cette structuration en arborescence est double. Elle permet :
de créer des pipelines d'opérations : le résultat de l'application du premier argument est fourni en entrée au second, etc ;
de regrouper les opérations interdépendantes. Par exemple, avant la version 4.0, l'argument explode:weak,arity=2.5 aurait dû être mis à plat : --explode --weak_explosion --explosion_arity=2.5. Cela donne le même statut à toutes, masquant le fait que certaines n'ont de sens qu'en présence d'une autre.
L'option à tout faire est --transform, que vous pouvez abréger -t. Avant de traiter ses arguments, Mocodo commence par les répartir en deux catégories :
les réécritures, qui transforment le MCD original en un nouveau MCD avec, par exemple, les libellés mis en majuscules, ou les associations n-aires décomposées en DF, ou les boîtes réarrangées ;
les conversions, qui produisent quelque chose qui n'est pas un MCD : ce peut être un dictionnaire des données, un diagramme de classes UML, un ERD dans la notation crow's foot, etc.
L'exécution se déroule alors en deux étapes :
les réécritures sont opérées en pipeline, c'est-à-dire que le MCD produit par la première réécriture est passé en entrée de la seconde, etc. Leur ordre a donc de l'importance ;
les conversions sont alors appliquées au résultat de la dernière réécriture. Leur ordre n'a pas d'importance.
traitement de chaînes de caractères : truncate, slice, prefix, suffix, replace ;
homogénéisation de la mise en forme : ascii, camel, snake, pascal, lower, upper, etc.
L'aide-mémoire suivant devrait vous permettre de prendre en main les sous-options de --transform qui vous intéressent. Survolez une sous-option pour afficher ses alias éventuels.
Dans l'aide-mémoire, de nombreuses opérations de réécriture sont décrites comme s'appliquant à des « éléments donnés ». Il s'agit de certains ensembles de tokens produits par l'analyse syntaxique. Plus précisément :
arrows : flèches de pattes (suffixes < ou > des cardinalités).
attrs : libellés des attributs d'entités ou d'associations.
boxes : libellés des entités et associations.
cards : cardinalités (11, 1N, etc., sans préfixe ni suffixe).
card_prefixes : préfixes de cardinalités (_ pour les entités faibles, / pour les agrégats).
constraint_notes : messages affichés au survol d'une contrainte.
labels : libellés des entités, des associations et des attributs.
leg_notes : notes de pattes (rôles ou messages affichés au survol).
Il peut être utile de comprendre ce qui se passe en coulisses lorsque l'on invoque la commande magique sur une cellule d'un notebook Jupyter :
dans le répertoire courant est créé au besoin un sous-répertoire mocodo_notebook ;
le texte d'entrée y est sauvegardé sous le nom de sandbox.mcd ;
la commande est lancée sur ce fichier avec tous les paramètres donnés par l'utilisateur. Cela signifie que tous les fichiers générés le seront au même endroit sous les noms de sandbox.svg, sandbox.html, etc. (relisez-les avec la commande magique %load) ;
certains des fichiers générés sont affichés dans la sortie de la cellule, d'autres pas, selon les règles suivantes (rw et cv dénotent respectivement une opération de réécriture et de conversion) :
commande
tracé du MCD
résultat des réécritures
résultats des conversions
mocodo
initial
-
-
mocodo -t ou mocodo --mld
initial
-
MLD en Markdown
mocodo -t rw1 rw2 ...
final
final
-
mocodo -t cv1 cv2 ...
-
-
tous
mocodo -t rw1 rw2 ... cv1 cv2 ...
final
-
tous
Ces règles d'affichage par défaut devraient répondre aux besoins les plus courants, mais l'option --select peut prendre le pas :
sans arguments, rien n'est affiché ;
l'argument mcd affiche le diagramme conceptuel ;
l'argument rw (ou un alias de commodité parmi source, text, code, mocodo) affiche le résultat de la dernière réécriture ;
l'argument cv (ou un alias de commodité parmi mld, ddl, sql) affiche les résultats des conversions ;
ces arguments peuvent être librement combinés et ordonnés ;
l'argument all affiche tout ce qui est disponible, dans l'ordre mcd, rw et cv ;
Les conversions génèrent par défaut un fichier-texte. Un dessin s'affichera à la place :
pour l'argument diagram ;
avec l'option --defer, pour autant qu'un web-service de rendu soit défini pour le fichier en question.
Il est possible de mettre -t en majuscule. Dans ce cas :
s'il y a une ou plusieurs opérations de réécriture, le résultat de la dernière réécriture remplace le contenu de la cellule et la sortie est effacée ;
sinon, s'il y a une ou plusieurs opérations de conversion et que Pyperclip est installé, le résultat de la dernière conversion est copié dans le presse-papier.
L'algorithme convertissant la représentation interne d'un schéma relationnel en une représentation externe dans tel ou tel format est totalement découplé dudit format. Cela signifie que vous pouvez modifier ou créer un format sans écrire une seule ligne de code, mais en remplissant une espèce de formulaire, ou gabarit (template en anglais).
Si vous n'avez aucune velléité de le faire ou que vous n'êtes pas ami avec les expressions régulières, vous pouvez sauter cette section. Sinon, accrochez-vous, ça va devenir un peu technique.
Les gabarits de Mocodo étaient à l'origine écrits en JSON. Par conséquent, un grand nombre des caractères utilisés dans les expressions régulières devaient être échappés, ce qui rendait celles-ci (encore) plus difficiles à écrire, à relire et à tester. La version 4 passe à YAML, qui n'a pas cet inconvénient. Voici par exemple un extrait de gabarit dans l'ancienne syntaxe :
{"order":400,"comment":"Move all foreign keys constraints at the end of the document","search":"(?sm)(CREATE TABLE ([^\n]+) \\(\n(?:[^\n]+\n)*) (FOREIGN KEY[^\n]+),\n(.*)","replace":"\\1\\4\nALTER TABLE \\2 ADD \\3;","iterated":true},
Si vous avez écrit vos propres gabarits JSON, vous pouvez les convertir automatiquement en YAML à l'aide du script json_to_yaml_templates.py fourni dans le répertoire dev.
Remarque. Si les gabarits de Mocodo constituent des fichiers YAML valides, l'inverse n'est pas forcément vrai. Mocodo utilise un sous-ensemble sain de YAML adapté à ses besoins. Il a son propre parser avec les particularités suivantes :
Un gabarit est un dictionnaire dont les valeurs sont :
soit des types scalaires (chaînes, nombres, booléens, null) ;
soit des listes de dictionnaires à valeurs scalaires.
Toute valeur chaîne doit être délimitée par des simples quotes ('). Les quotes internes sont échappées en les doublant ('').
Les expressions régulières de recherche et de remplacement suivent les mêmes règles d'échappement que les r-strings de Python.
Les autres chaînes (notamment les chaînes de composition) ne demandent pas à échapper les anti-slashes, mais à doubler les accolades (règles des fr-strings). Exception : pour les distinguer du retour-chariot et de la tabulation, il faudra échapper les \n et les \t si on veut les laisser littéralement. Vous en avez un exemple pour \newcommand et \title dans le boilerplate du gabarit pour $\LaTeX$.
Depuis la version 3.1, il est possible de définir un gabarit dérivé d'un autre (qui lui-même peut dériver d'un autre, etc.). Dans le but de réduire les redondances et de faciliter les tests et la maintenance, la version 4.0 fait un usage intensif de cette fonctionnalité :
Les teintes les plus foncées correspondent approximativement aux gabarits les plus complexes. Les autres ne sont souvent que des alias, par exemple, tex.yaml renvoie directement à latex.yaml :
La clé "parent" donne le chemin ou le nom du gabarit dont on souhaite créer une version dérivée :
une chaîne terminée par .yaml est interprétée comme un chemin ;
sinon, comme le nom d'un gabarit de la distribution.
Tous les gabarits textuels (text.yaml, latex.yaml, markdown.yaml, html.yaml et leurs alias) dérivent des gabarits html-c.yaml (version avec affichage des contraintes) et html-ce.yaml (version avec affichage des contraintes et des explications).
Par exemple, le gabarit latex se borne à ajouter un message d'aide pour la ligne de commande et à empêcher l'ajout des contraintes d'unicité et d'optionalité :
... on voit que les opérations consistent simplement à modifier le séparateur de colonne, à composer les tables et le schéma relationnel complet avec un boilerplate, et enfin à remplacer les balises HTML par leur équivalent en $\LaTeX$. Toute la logique complexe est traitée dans le gabarit html-c.
Supposons que cette redondance nous défrise, et voyons comment l'éliminer. Pour cela, examinons le contenu du gabarit sql.yaml :
La contrainte d'optionalité {optionality} apparaît à côté de PRIMARY KEY en lignes 28 et 31. Nous pouvons donc simplement remplacer les valeurs associées à compose_primary_key et compose_primary_foreign_key par des copies supprimant cette mention :
Nous aurions pu procéder autrement. Reprenons le gabarit original :
Le dictionnaire transform_relation spécifie une séquence de réécritures à appliquer à chacune des clauses CREATE TABLE (composées ligne 37). La première accumule toutes les lignes contenant PRIMARY KEY en fin de table (où elles seront concaténées avant d'être ramenées au début). Nous devons donc intervenir avant que NOT NULL ne soit séparé de PRIMARY KEY. Pour cela, définissons un gabarit qui se borne à insérer avant cette transformation (de numéro d'ordre 100) une suppression des NOT NULL incriminés :
In [195]:
%%file new_sql.yaml
parent:'sql'transform_relation:-order:50comment:'Suppress PK NN redundancy.'search:' NOT NULL\nPRIMARY KEY'replace:'\nPRIMARY KEY'
NB. Les gabarits définis par l'utilisateur n'ont pas vocation à être placés dans le répertoire relation_templates de la distribution. Votre nouveau gabarit new_sql ne peut donc pas être invoqué aussi simplement que les gabarits officiels (i.e., avec -t new_sql). Si vous faites une modification ou un ajout que vous voulez voir inclus dans la distribution, n'hésitez pas à faire une PR.
Le mécanisme de mise à jour des clés dépend de la scalarité de leur valeur associée.
Cas scalaire.
Pour les valeurs scalaires (chaînes, nombres, booléens, null), selon la présence d'une entrée donnée dans le gabarit enfant et/ou le gabarit parent, l'association clé-valeur correspondante sera créée, mise à jour ou conservée :
enfant
parent
résultat
oui
non
création
oui
oui
mise à jour
non
oui
conservation
Il n'y a pas de moyen de supprimer une association clé-valeur, mais étant donné que toutes celles qui sont absentes se voient substituer une association par défaut, il est équivalent de spécifier explicitement la valeur-défaut.
Cas non scalaire.
Dans les gabarits de relations de Mocodo, une seule catégorie de valeur non scalaire est possible : la liste de dictionnaires. Dans l'exemple précédent, c'est le cas de transform_relation.
Une clé "order" est systématiquement présente dans chacun des dictionnaires de la liste. Selon la présence d'un dictionnaire de numéro d'ordre donné dans le gabarit enfant et/ou le gabarit parent, ce dictionnaire sera inséré, supprimé, mis à jour ou conservé :
enfant
parent
résultat
oui
non
insertion
oui
oui
si les clés sont parmi {"order", "comment"} : suppression s'il y a plusieurs clés : mise à jour
non
oui
conservation
Nouveauté de la version 4.0. Si une clé d'un gabarit enfant est associée à une liste vide, cette association remplace l'éventuelle association existante.
Remarque. Les numéros d'ordre utilisés dans les dictionnaires sont des multiples de 100. Si vous voulez créer des gabarits dérivés, évitez par précaution les multiples de 10 : cela augmentera les chances que votre gabarit survive à une mise à jour de l'un de ses ancêtres. Vous pouvez aussi partir d'une copie de ceux-ci, sachant que vous renoncez alors aux bénéfices de leurs éventuelles mises à jour.
"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"init_cell": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Mocodo 4.3.2 loaded.\n"
]
}
],
"source": [
"%reload_ext mocodo"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"init_cell": true
},
"outputs": [],
"source": [
"from IPython import display\n",
"from pathlib import Path\n",
"import os\n",
"if Path.cwd().name != \"mocodo_notebook\":\n",
" Path(\"mocodo_notebook\").mkdir(parents=True, exist_ok=True)\n",
" os.chdir(\"mocodo_notebook\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Introduction"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mocodo est un logiciel d'aide à l'enseignement et à l'apprentissage des [bases de données relationnelles](https://fr.wikipedia.org/wiki/Base_de_données_relationnelle).\n",
"\n",
"- En entrée, il prend un [MCD](https://fr.wikipedia.org/wiki/Modèle_entité-association) (modèle conceptuel de données) décrit dans un langage dédié minimaliste.\n",
"- En sortie, il produit un diagramme entité-association et, à la demande, un [MLD](https://fr.wikipedia.org/wiki/Merise_%28informatique%29%23MLD_%3A_mod%C3%A8le_logique_des_donn%C3%A9es) (schéma relationnel, sous forme graphique ou textuelle), un [DDL](https://fr.wikipedia.org/wiki/Langage_de_définition_de_données) (script SQL de création de la base), un [diagramme de classes UML](https://fr.wikipedia.org/wiki/Diagramme_de_classes), etc.\n",
"- En bonus, il est capable de réarranger automatiquement votre MCD de façon esthétique, et de lui appliquer des opérations de réécriture qui vont du mondain (typographie) à l'académique (décomposition d'associations), en passant par le merveilleux (inférence de types, génération d'exercices et d'exemples).\n",
"\n",
"Vous pouvez utiliser Mocodo :\n",
"\n",
"- à distance, sans rien installer, avec [Mocodo _online_](https://www.mocodo.net) ;\n",
"- en local, comme n'importe quel programme Python ;\n",
"- dans un document [Jupyter Notebook](https://jupyter.org) (à l'instar de cette documentation)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tracé du modèle conceptuel"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ci-dessous, un exemple d'utilisation sous Jupyter Notebook. L'appel du programme est en première ligne ; le texte-source proprement dit, lignes suivantes. En sortie, le diagramme conceptuel, égayé au passage par l'option `--colors` :"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --colors ocean\n",
"Client: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)]\n",
"Passer, 0N Client, 11 Commande\n",
"Commande: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)]\n",
"Inclure, 1N Commande, 0N Produit: Quantité [INTEGER]\n",
"Produit: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Opérations de conversion"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'appel précédent a automatiquement enregistré le texte-source sous le nom de `sandbox.mcd`. Renommons-le `ccp.mcd` pour y avoir accès tout au long de ce document."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"Path(\"sandbox.mcd\").rename(\"ccp.mcd\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"On peut le récupérer avec `--input` pour lui appliquer diverses opérations. Ainsi, l'appel suivant génère et affiche son MLD, son diagramme relationnel et son DDL :"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t crow:plantuml --defer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Opérations de réécriture"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Une **réécriture** transforme un MCD Mocodo en un autre MCD Mocodo (au contraire d'une **conversion**, qui produit un animal d'une espèce différente).\n",
"\n",
"Heureusement, l'utilisateur n'a pas à réfléchir si la transformation qu'il souhaite appliquer est une réécriture ou une conversion : dans les deux cas, il invoque `-t` (c'est-à-dire `--transform`), et Mocodo se débrouille.\n",
"\n",
"En guise de premier exemple de réécriture, mettons les noms des entités et associations (`boxes`) en majuscules, et les libellés (`labels`) en ASCII et _snake case_ :"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n",
"\n",
"\n",
"\n",
"ccp.mcd\n",
"\n",
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"%%mocodo --colors brewer+3\n",
"CLIENT: Ref_client [VARCHAR(8)], Nom [VARCHAR(255)], Prenom [VARCHAR(255)], Adresse [VARCHAR(255)]\n",
"PASSER, 0N CLIENT, 11 COMMANDE\n",
"COMMANDE: Num_commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)]\n",
"INCLURE, 1N COMMANDE, 0N PRODUIT: Quantite [INTEGER]\n",
"PRODUIT: Ref_produit [VARCHAR(8)], Libelle [VARCHAR(50)], Prix_unitaire [DECIMAL(10,2)]\n"
]
}
],
"source": [
"%mocodo -i ccp -t upper:boxes ascii:labels snake:labels --colors brewer+3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Remarquez que l'exécution d'une réécriture affiche, au-dessous du diagramme, le code-source résultant. Celui-ci est précédé de la commande magique originale, _privée de l'option `-i` et de toute opération de réécriture_. Ces dispositions permettent de continuer à travailler directement dessus si on le copie-colle dans une autre cellule."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Plusieurs opérations de réécriture de nature sémantique sont également offertes. Par exemple, on peut décomposer un MCD quelconque en un MCD équivalent, mais n'employant que des dépendances fonctionnelles et des entités faibles :"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n",
"\n",
"\n",
"\n",
"ccp.mcd\n",
"\n",
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"%%mocodo --seed=3 --colors brewer+3\n",
"Client: Réf. client [VARCHAR(8)], Nom [VARCHAR(255)], Prénom [VARCHAR(255)], Adresse [VARCHAR(255)]\n",
"Passer, 0N Client, 11 Commande\n",
"Commande: Num. commande [VARCHAR(8)], Date [DATE], Montant [DECIMAL(10,2)]\n",
"DF, _11 Inclure, 1N Commande\n",
"Inclure: _Quantité [INTEGER]\n",
"DF, _11 Inclure, 0N Produit\n",
"Produit: Réf. produit [VARCHAR(8)], Libellé [VARCHAR(50)], Prix unitaire [DECIMAL(10,2)]\n"
]
}
],
"source": [
"%mocodo -i ccp -t explode:weak,arity=2 arrange:wide --seed=3 --colors brewer+3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notez l'argument `arrange:wide`. Il a procédé à une réorganisation aléatoire des boîtes, ce que l'insertion de deux nouvelles associations de dépendance fonctionnelles avait rendu nécessaire. Quant à l'option `--seed=3`, elle garantit que le résultat sera le même à chaque exécution."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Après cet aperçu de quelques-unes des fonctionnalités de Mocodo, entrons dans les détails."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Installation et lancement du programme"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Comme nous l'avons dit, vous pouvez utiliser Mocodo sans rien installer. Il vous suffit d'aller sur [mocodo.net](https://www.mocodo.net) et de commencer à taper votre MCD. Appuyez à tout moment sur le ventilateur pour rafraîchir les diagrammes conceptuel et relationnel. Quand le résultat vous convient, appuyez sur le bouton de téléchargement pour récupérer une archive ZIP contenant tous les fichiers d'entrée et de sortie spécifiés.\n",
"\n",
"Mocodo _online_ est conçu pour une utilisation occasionnelle et/ou interactive, et son interface vise avant tout à la simplicité. Vous n'avez donc accès qu'aux options essentielles du programme. Si vous en voulez davantage, tant en termes de paramétrage que de calcul ou de fonctionnalités, nous vous conseillons d'installer Mocodo sur votre machine."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Installation minimale"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mocodo est un programme écrit en [Python 3](https://www.python.org). Si vous êtes sous macOS ou Linux, il est déjà installé. Dans le cas contraire, vous devrez peut-être [le faire](https://www.python.org/downloads/).\n",
"\n",
"Une fois Python présent sur votre machine, tapez sous un terminal:\n",
"\n",
" python -m pip install mocodo\n",
"\n",
"Puis testez-le :\n",
"\n",
" mocodo\n",
"\n",
"Invoqué sous cette forme, Mocodo récupère le texte d'entrée du MCD dans le répertoire courant sous le nom de `sandbox.mcd`. Si ce fichier n'existe pas, il y sera automatiquement créé avec un MCD d'exemple. Par la suite, vous n'aurez qu'à le garder ouvert sous un éditeur de texte, afin de le modifier à votre fantaisie avant de relancer la commande.\n",
"\n",
"Si votre système se plaint que cette commande n'existe pas, localisez le fichier `mocodo` et ajoutez à votre `PATH` le chemin du répertoire contenant:\n",
"- [sous Linux ou macOS](http://www.commentcamarche.net/faq/3585-bash-la-variable-d-environnement-path#v-ajouter-un-repertoire-a-la-variable-path) ;\n",
"- [sous Windows](http://sametmax.com/ajouter-un-chemin-a-la-variable-denvironnement-path-sous-windows/)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Installation complète (recommandé)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Installez la [distribution Anaconda](https://www.anaconda.com/download), qui contient Python 3, Jupyter Notebook et bien plus encore.\n",
"\n",
"\n",
"- La ligne donnée précédemment installe en fait, en plus de Mocodo, sa « commande magique » pour [Jupyter Notebook](https://jupyter.org). Nous l'avons déjà invoquée plusieurs fois dans ce document :\n",
"\n",
" python -m pip install mocodo\n",
"\n",
"- Mocodo fonctionne parfaitement sans aucune dépendance. Cependant, si vous souhaitez :\n",
" - utiliser l'option `--svg_to` pour générer des figures en PDF ou en PNG ;\n",
" - copier directement dans le presse-papier le résultat d'une réécriture ;\n",
"\n",
" ... vous pouvez installer les dépendances optionnelles aux bibliothèques [CairoSVG](https://cairosvg.org) et [Pyperclip](https://github.com/asweigart/pyperclip) ainsi :\n",
" \n",
" python -m pip install 'mocodo[svg,clipboard]'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pour mettre la « commande magique » `mocodo` à disposition d'un _notebook_ donné, évaluez dans celui-ci la cellule suivante:\n",
"\n",
" %reload_ext mocodo\n",
"\n",
"Techniquement, `%load_ext mocodo` suffit, mais cette forme vous épargnera un message d'erreur si vous réévaluez ultérieurement la cellule.\n",
"\n",
"Pour tester la commande :"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"MISSION: accomplie"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pour charger automatiquement `mocodo` à chaque ouverture d'un notebook (ce qui dispense d'évaluer `%load_ext mocodo`) :\n",
"\n",
"- exécutez sous un terminal :\n",
"\n",
"```\n",
"ipython profile create\n",
"```\n",
"\t\n",
"- éditez le fichier créé (p. ex.: `~/.ipython/profile_default/ipython_config.py`) en remplaçant la ligne suivante :\n",
"\n",
"```\n",
"# c.TerminalIPythonApp.extensions = []\n",
"```\n",
"\n",
" par celle-ci :\n",
"\n",
"```\n",
"c.InteractiveShellApp.extensions = [\"mocodo\"]\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Utilisation par importation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Nouveauté de la version 4.1.3.** Un _wrapper_ minimaliste permet de simuler programmatiquement l'appel à la commande `mocodo`."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"from mocodo.api import mocodo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ainsi, l'équivalent de :\n",
"\n",
"```\n",
"mocodo -i ccp --colors mondrian\n",
"```\n",
"\n",
"... s'écrira :"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"messages = mocodo(\"-i ccp --colors mondrian\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Par défaut (second argument `quiet=True`), la fonction n'affiche pas les messages de succès, mais renvoie leur concaténation :"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fichier de sortie « ~/Dropbox/Sites/mocodo/doc/mocodo_notebook/ccp.svg » généré avec succès.\n"
]
}
],
"source": [
"print(messages)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Plus important, les fichiers de sortie sont bel bien créés :"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"display.SVG(\"ccp.svg\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Si vous travaillez sous une implantation de Jupyter Notebook qui ne prend pas en charge les commandes magiques, l'importation conseillée est :"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"from mocodo.magic import mocodo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Elle permet de simuler les commandes magiques, aussi bien celles qui sont réduites à une ligne :"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"mocodo(\"%mocodo -i ccp --colors ocean\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"... que celles qui portent sur toute une cellule :"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"mocodo(\"\"\"\n",
"%%mocodo --colors pond\n",
"Mission: accomplie\n",
"\"\"\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Le préfixe `\"%mocodo\"` ou `\"%%mocodo\"` est ignoré, mais l'autoriser permet de copier toute une ligne ou toute une cellule (vraiment) magique, et de la coller directement comme argument de la fonction."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Nouveauté de la version 4.2.** Avec cette technique, Mocodo peut être utilisé sous [Basthon](https://basthon.fr) v. 0.62.12 ou ultérieure. Basthon est une plateforme permettant de travailler sous Jupyter Notebook sans avoir à rien installer. Son auteur, Romain Casati, a ajouté un exemple d'utilisation de Mocodo dans la [galerie](https://basthon.fr/galerie.html) du logiciel, à tester [ici](https://notebook.basthon.fr/?from=examples/python3-mocodo.ipynb). Sous Mocodo _online_, cliquez sur le symbole de lecture de Basthon (dans le pied de page) pour ouvrir un notebook avec le MCD en cours d'édition."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Syntaxe de description d'un MCD"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Notions élémentaires"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Entités, associations, attributs, identifiants, cardinalités"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"CLIENT: Réf. client, Nom, Prénom, Adresse\n",
"PASSER, 0N CLIENT, 11 COMMANDE\n",
"COMMANDE: Num. commande, Date, Montant\n",
"INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité\n",
"PRODUIT: Réf. produit, Libellé, Prix unitaire"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Passons en revue les principes de la syntaxe. Ils ne devraient pas poser trop de problèmes.\n",
"\n",
"**La première ligne** ne fait pas partie de la définition du MCD. Sous Jupyter Notebook, elle dénote l'appel à une « commande magique », qui lance Mocodo sur le reste de la cellule. En dehors d'un notebook, vous n'en avez pas besoin, et toute ligne commençant par un pourcentage (`%`) sera considérée comme un commentaire."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Une entité** est définie par :\n",
"\n",
"$$\n",
"\\overbrace{\\texttt{COMMANDE}}^{\\text{son nom}}\n",
"\\quad\n",
"\\overbrace{\\texttt{:}}^{\\text{deux-points}}\n",
"\\quad\n",
"\\overbrace{\\texttt{Num. commande, Date, Montant}}^{\\text{ses attributs séparés par des virgules}}\n",
"$$ \n",
"\n",
"Notez que le premier attribut d'une entité est considéré par défaut comme son identifiant, et donc souligné."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Une association** est définie par :\n",
"\n",
"$$\n",
"\\overbrace{\\texttt{INCLURE}}^{\\text{son nom}}\n",
"\\quad\n",
"\\overbrace{\\texttt{,}}^{\\text{une virgule}}\n",
"\\quad\n",
"\\overbrace{\n",
" \\underbrace{\\texttt{1N}}_{\\text{cardinalités}}\n",
" \\underbrace{\\texttt{COMMANDE}}_{\\text{entité}}\n",
" \\quad\n",
" ,\n",
" \\quad\n",
" \\underbrace{\\texttt{0N}}_{\\text{cardinalités}}\n",
" \\underbrace{\\texttt{PRODUIT}}_{\\text{entité}}\n",
"}^{\\text{ses pattes séparées par des virgules}}\n",
"\\quad\n",
"\\overbrace{\\texttt{:}}^{\\text{deux-points}}\n",
"\\quad\n",
"\\overbrace{\\texttt{Quantité}}^{\\text{ses attributs séparés par des virgules}}\n",
"$$ \n",
"\n",
"Notez que pour les associations sans attributs (comme PASSER), le deux-points est facultatif."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Astuce.** Si vous recopiez un MCD ou que vous l'avez bien en tête, commencez par les associations et, à tout moment, faites apparaître les entités manquantes (double clic sur le lapin magique sous Mocodo online). Elles auront comme identifiant le nom de l'entité en minuscules, par défaut précédé de « id. » (sauf pour les entités DATE et PÉRIODE). Vous n'aurez plus qu'à remplir les autres attributs :"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"| Entité ou association | Libellé de l'attribut | Type |\n",
"|:----------------------|:----------------------|:-------------|\n",
"| AYANT-DROIT | lien | |\n",
"| \" | nom ayant-droit | VARCHAR(255) |\n",
"| COMPOSER | quantité | INTEGER |\n",
"| DÉPARTEMENT | nom département | VARCHAR(255) |\n",
"| \" | num. département | VARCHAR(8) |\n",
"| EMPLOYÉ | matricule | |\n",
"| \" | nom employé | VARCHAR(255) |\n",
"| FOURNIR | qté fournie | INTEGER |\n",
"| PIÈCE | libellé pièce | |\n",
"| \" | réf. pièce | VARCHAR(8) |\n",
"| PROJET | nom projet | VARCHAR(255) |\n",
"| \" | num. projet | VARCHAR(8) |\n",
"| REQUÉRIR | qté requise | INTEGER |\n",
"| SOCIÉTÉ | num. société | VARCHAR(8) |\n",
"| \" | raison sociale | |\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t guess:types data_dict --select cv\n",
"AYANT-DROIT: nom ayant-droit, lien\n",
"DIRIGER, 0N [responsable] EMPLOYÉ, 01 PROJET\n",
"REQUÉRIR, 1N PROJET, 0N PIÈCE: qté requise\n",
"PIÈCE: réf. pièce, libellé pièce\n",
"COMPOSER, 0N [composée] PIÈCE, 0N [composante] PIÈCE: quantité\n",
"\n",
"DF, _11 AYANT-DROIT, 0N EMPLOYÉ\n",
"EMPLOYÉ: matricule, nom employé\n",
"PROJET: num. projet, nom projet\n",
"FOURNIR, 1N PROJET, 1N PIÈCE, 1N SOCIÉTÉ: qté fournie\n",
"\n",
"DÉPARTEMENT: num. département, nom département\n",
"EMPLOYER, 11 EMPLOYÉ, 1N DÉPARTEMENT\n",
"TRAVAILLER, 0N EMPLOYÉ, 1N PROJET\n",
"SOCIÉTÉ: num. société, raison sociale\n",
"CONTRÔLER, 0N< [mère] SOCIÉTÉ, 01 [filiale] SOCIÉTÉ"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Tracé d'un MCD : principes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mocodo combine les avantages de l'approche _diagram as code_ (comme PlantUML, Mermaid ou Graphviz), avec la liberté de positionnement offerte par les logiciels WYSIWYG (_what you see is what you get_).\n",
"\n",
"Sa grande originalité est de se baser sur l'ordre et la séparation des lignes de la description pour définir une mise en page qui se révèle suffisante dans la majorité des cas."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Le pont aux ânes du langage Mocodo"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Premier principe : les boîtes (entités et associations) définies sur des **lignes consécutives** sont tracées sur **une seule rangée**.\n",
"\n",
"Si l'on écrit les définitions sur des lignes consécutives, on se retrouve donc vite avec des chevauchements qui ne sautent pas forcément aux yeux :"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"CLIENT: Réf. client, Nom, Prénom, Adresse\n",
"COMMANDE: Num. commande, Date, Montant\n",
"PRODUIT: Réf. produit, Libellé, Prix unitaire\n",
"PASSER, 0N CLIENT, 11 COMMANDE\n",
"INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'option `--detect_overlaps` (active par défaut sous Mocodo online) signale le problème et donne la solution :"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Mocodo Err.29 - Mauvaise disposition des boîtes :\n",
" - La patte « INCLURE — COMMANDE » chevauche « PRODUIT ».\n",
" - La patte « INCLURE — PRODUIT » chevauche « PASSER ».\n",
" - La patte « PASSER — CLIENT » chevauche « COMMANDE ».\n",
" - Les pattes « PASSER — COMMANDE » et « INCLURE — COMMANDE » se chevauchent.\n",
"Pour corriger cela, réordonnez et/ou sautez des lignes dans le texte-source, soit à la\n",
"main, soit à l'aide de l'option -t arrange (tablette de chocolat sous Mocodo online)."
]
}
],
"source": [
"%mocodo -i sandbox --detect_overlaps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Limitation.** Seuls les chevauchements mettant en jeu des pattes horizontales ou verticales sont détectés."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### MCD sur plusieurs lignes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Deuxième principe : pour commencer une nouvelle rangée, il faut sauter une ligne :"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"SCELERISQUE LOREM: blandit, elit, ligula\n",
"EROS, 11 SCELERISQUE LOREM, 1N PELLENTESQUE IPSUM: metus, congue\n",
"\n",
"NIBH, 1N SCELERISQUE LOREM, 11 PELLENTESQUE IPSUM\n",
"PELLENTESQUE IPSUM: tincidunt, bibendum, consequat, integer"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Les centres des boîtes sont placés aux intersections d'une grille invisible. Ils sont donc alignés aussi bien horizontalement que verticalement. De plus, dans un but esthétique, le dessin fait l'objet d'une compression dans les deux dimensions. Par exemple, ci-dessus, un espace horizontal négatif a été créé entre le bord droit de l'entité de gauche et le bord gauche de l'entité de droite."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Mise en miroir"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mocodo permet de calculer facilement les symétriques d'un MCD :\n",
"\n",
"- verticalement : `-t flip:v` ;\n",
"- horizontalement : `-t flip:h` ;\n",
"- selon la seconde diagonale (transposition) : `-t flip:d`.\n",
"- selon la première diagonale (transposition) : `-t flip:vhd`."
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i sandbox --select mcd -t flip:h"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Corrections de la version 4.0.**\n",
"- Les sens de « horizontal » et « vertical » sont permutés pour être plus conformes à l'intuition.\n",
"- La documentation parlait de première diagonale, c'était la seconde, et vice-versa."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Astuce.** Transposez le MCD temporairement pour réaliser plus facilement certaines opérations d'édition en colonne, en particulier sous Mocodo online."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Insertion d'espacements"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mocodo centre les rangées qui contiennent moins de boîtes que les autres. Ce comportement n'est pas toujours idéal :"
]
},
{
"cell_type": "code",
"execution_count": 32,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo \n",
"Ultrices, 01 Aliquet, 0N Aliquet\n",
"Aliquet: hendrerit, metus, lacus, quis\n",
"Risus, 1N Aliquet, 0N Massa\n",
"Massa: metus, posuere\n",
"Euismod, 0N Massa, 0N Massa\n",
"\n",
"Convallis, 0N Aliquet, 0N Ante: vestibulum\n",
"Tincidunt, 11 Ante, 0N Massa\n",
"\n",
"Gravida: ornare\n",
"Rutrum, 0N Gravida, 0N Ante, 0N Aliquet: faucibus, curae\n",
"Ante: vitae, tempor"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'utilisateur peut alors spécifier les espacements qu'il désire en complétant les rangées par des boîtes invisibles dont le seul rôle est de « pousser » les autres à l'emplacement voulu. Cela se fait en insérant manuellement des lignes réduites au caractère deux-points :"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"Ultrices, 01 Aliquet, 0N Aliquet\n",
"Aliquet: hendrerit, metus, lacus, quis\n",
"Risus, 1N Aliquet, 0N Massa\n",
"Massa: metus, posuere\n",
"Euismod, 0N Massa, 0N Massa\n",
"\n",
":\n",
":\n",
"Convallis, 0N Aliquet, 0N Ante: vestibulum\n",
"Tincidunt, 11 Ante, 0N Massa\n",
"\n",
"Gravida: ornare\n",
"Rutrum, 0N Gravida, 0N Ante, 0N Aliquet: faucibus, curae\n",
":\n",
"Ante: vitae, tempor"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Réarrangement automatique"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cependant, en général, ces manipulations sont inutiles : Mocodo est capable de calculer tout seul des plongements à la fois compacts et esthétiques. Voici une compilation des réarrangements automatiques du MCD précédent produits par :\n",
"```\n",
"%mocodo -i sandbox -t arrange --seed 1\n",
"%mocodo -i sandbox -t arrange --seed 2\n",
"%mocodo -i sandbox -t arrange --seed 3\n",
"%mocodo -i sandbox -t arrange --seed 4\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 34,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ../examples/four_random_layouts --scale 0.8"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Extensions du modèle"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Entité faible (ou identification relative)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Préfixez d'un tiret bas (`_`) une cardinalité 11 pour indiquer que l'entité distinguée est faible."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"ŒUVRE: Cote œuvre, Titre, Date parution\n",
"DF, 1N ŒUVRE, _11 EXEMPLAIRE\n",
"EXEMPLAIRE: Num. exemplaire, État du livre, Date d'achat"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dans le diagramme, les identifiants (ou plutôt, discriminateurs) d'une telle entité seront soulignés en pointillés, tandis que le 11 sera par défaut souligné d'un trait plein.\n",
"\n",
"Notez qu'un discriminateur n'est pas toujours obligatoire :"
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo \n",
"Employé: Num. employé, Nom employé, Prénom employé\n",
"Df, _11 Conjoint, 01 Employé\n",
"Conjoint: _Nom conjoint, Prénom conjoint"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Son absence implique cependant que l'occurrence de l'entité forte qui « renforce » une occurrence de l'entité faible ne peut en renforcer une autre : d'où la cardinalité maximale 1 sur la patte distinguant EMPLOYÉ.\n",
"\n",
"Mocodo rejettera donc la version « polygame » du MCD précédent (avec 0N à la place de 01) :"
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Mocodo Err.50 - L'entité faible « CONJOINT » devrait avoir un discriminateur."
]
}
],
"source": [
"%%mocodo\n",
"Employé: Num. employé, Nom employé, Prénom employé\n",
"Df, _11 Conjoint, 0N Employé\n",
"Conjoint: _Nom conjoint, Prénom conjoint"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Héritage (généralisation / spécialisation)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'héritage permet de regrouper dans une entité, dite « mère », les attributs communs à plusieurs autres entités, dites « filles », qui se distinguent les unes des autres par des attributs spécifiques."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pour définir une spécialisation, insérez une ligne spécifiant :\n",
"\n",
"\n",
"$$\n",
"\\overbrace{\\texttt{/}\\,\\texttt{XT}\\,\\texttt{\\\\}}^{\\text{sa nature entre barres obliques}}\n",
"\\qquad\n",
"\\overbrace{\\texttt{<}\\,\\texttt{-}}^{\\text{son type de flèche}}\n",
"\\qquad\n",
"\\overbrace{\\texttt{mère}}^{\\text{une entité}}\n",
"\\qquad\n",
"\\overbrace{\\texttt{,}}^{\\text{une virgule}}\n",
"\\qquad\n",
"\\overbrace{\\texttt{fille 1, fille 2, fille 3}}^{\\text{des entités séparées par des virgules}}\n",
"\\qquad\n",
"\\overbrace{\\texttt{: discriminateur}}^{\\text{optionnellement, un attribut}}\n",
"$$ "
]
},
{
"cell_type": "code",
"execution_count": 38,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"Personne: num SS, nom, prénom\n",
"\n",
"/XT\\ Personne <- Homme, Femme: sexe\n",
"\n",
"Homme: \n",
":\n",
"Femme: nom de jeune fille"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Inscrivez dans le triangle les symboles de votre choix pour prendre une part active aux guerres culturelles de notre temps :"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"| Totalité | Exclusion | Symboles | Exemple de population | Toute personne est : |\n",
"|----------|-----------|:--------:|:---------------------:|:---------------------|\n",
"| oui | oui | `/XT\\` | ♂♂♂♂♂♂♂ ♀♀♀♀♀♀♀♀♀♀ | - soit un homme - soit une femme |\n",
"| non | oui | `/X\\` | ♂♂♂♂♂♂♂♂ ♀♀♀♀♀♀ ○○○○ | - soit un homme - soit une femme - soit aucun des deux |\n",
"| oui | non | `/T\\` | ♂♂♂♂♂♂ ♀♀♀♀♀♀♀♀ ⚥⚥⚥⚥ | - soit un homme - soit une femme - soit les deux à la fois |\n",
"| non | non | `/\\` | ♂♂♂♂♂ ♀♀♀♀♀♀ ○○○○ ⚥⚥⚥⚥ | - soit un homme - soit une femme - soit aucun des deux - soit les deux à la fois |"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Contrainte inter-associations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pour définir une telle contrainte, ajoutez n'importe où une ligne spécifiant :\n",
"\n",
"$$\n",
"\\overbrace{\\texttt{(}\\,\\texttt{FOO}\\,\\texttt{)}}^{\\text{son nom entre parenthèses}}\n",
"\\quad\n",
"\\overbrace{\n",
" \\texttt{-}\\,\\texttt{>}\n",
" \\texttt{boîte 1,}\n",
" \\quad\n",
" \\texttt{.}\\,\\texttt{.}\n",
" \\texttt{boîte 2,}\n",
" \\quad\n",
" \\texttt{-}\\,\\texttt{-}\n",
" \\texttt{boîte 3,}\n",
" \\quad\n",
" \\texttt{boîte 4}\n",
"}^{\\text{les types de liens et les boîtes qu'elle met en jeu, séparés par des virgules}}\n",
"\\quad\n",
"\\overbrace{\\texttt{:}\\,\\,\\texttt{horizontale, verticale}}^{\\text{optionnellement, ses coordonnées}}\n",
"$$ \n",
"\n",
"**Précisions.**\n",
"- Comme leur nom ne l'indique pas, ces contraintes peuvent aussi bien porter sur des entités que des associations.\n",
"- Le nom d'une contrainte peut être formé de zéro à trois symboles quelconques.\n",
"- Un lien est soit vide (trait invisible), soit formé d'un ou plusieurs symboles `-` (trait plein) ou `.` (trait pointillé), optionnellement précédés d'un symbole `<` (flèche vers la contrainte) et/ou suivis d'un symbole `>` (flèche vers la boîte).\n",
"- Au contraire des boîtes (entités et associations), les contraintes ne sont pas... contraintes par la grille sous-jacente : la mise en page du MCD n'en tient absolument pas compte. Celui de l'exemple ci-dessous a donc dû être « aéré » avec des `:` pour leur faire de la place.\n",
"- Les clauses de définition des contraintes peuvent apparaître n'importe où. Notez cependant que tout réarrangement automatique les enverra systématiquement à la fin du texte-source.\n",
"- Chaque coordonnée du centre de la contrainte peut être exprimée sous la forme :\n",
" - d'une référence à une boîte sur laquelle l'aligner horizontalement ou verticalement (respectivement) ;\n",
" - d'un pourcentage de la largeur ou de la hauteur (respectivement) du MCD.\n",
"- En l'absence de coordonnées, la contrainte est placée au barycentre des boîtes qu'elle relie.\n",
"\n",
"\n",
"Voici un exemple concret, adapté de la Fig. 7.37 de _Merise, deuxième génération_ (Dominique Nanci et Bernard Espinasse, 4e éd., 2001) :"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Article** (réf. article, prix)\n",
"- **Commande** (num. commande, date, _#num dépôt_)\n",
"- **Composer** (_#num. commande_, _#réf. article_)\n",
"- **Dépôt** (num dépôt, surface)\n",
"- **Stocker** (_#num dépôt_, _#réf. article_, quantité)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t\n",
":::\n",
"Dépôt: num dépôt, surface\n",
"\n",
":\n",
"Louer, 11 Commande, 0N Dépôt\n",
":\n",
"Stocker, 1N Dépôt, 1N Article: quantité\n",
"\n",
"Commande: num. commande, date\n",
"Composer, 1N Commande, 0N Article\n",
":\n",
"Article: réf. article, prix\n",
"\n",
"(I) ->Stocker, ..Dépôt, ..Article, --Composer, --Louer, Commande"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notez que pour Mocodo, ces contraintes sont purement décoratives : le passage au niveau relationnel ou physique n'en tient pas compte."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Agrégat (ou pseudo-entité)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dans le MCD ci-dessous, le petit rond et l'enveloppe pointillée indiquent qu'une réservation d'une chambre donnée à une date donnée ne peut être faite que par un seul client :"
]
},
{
"cell_type": "code",
"execution_count": 40,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --colors ocean # changement de palette pour faire apparaître un fond semi-transparent \n",
"Date: Date\n",
"Réserver, /1N Client, 1N Chambre, 0N Date: Durée\n",
"Chambre: Num. chambre, Prix\n",
"\n",
"Client: Id. client"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La syntaxe est minimale : une simple barre oblique `/` avant la cardinalité de l'entité-cible.\n",
"\n",
"S'il y a au maximum deux entités à agréger, et que l'angle formé par l'association et les entités est plat ou rectangle, une enveloppe de points matérialise la pseudo-entité.\n",
"\n",
"Sinon, pour des raisons de simplicité du code et de clarté du diagramme, l'enveloppe n'est pas affichée. Cependant, le petit rond subsiste et le reste des traitements est bien sûr inchangé.\n",
"\n",
"**Remarques.**\n",
"- La représentation traditionnelle demanderait à insérer une association DF entre CLIENT et RÉSERVER. Mais permettre l'association d'une entité et d'une association demanderait à apporter au code de Mocodo des changements conséquents, pour un bénéfice qui ne nous saute pas forcément aux yeux.\n",
"- Les agrégats sur association binaire sont soit inutiles (sur une DF), soit impossibles (sur une non-DF, ils reviennent à permettre à une même occurrence d'entité de renforcer plusieurs occurrences d'une entité faible sans discriminateur). Mocodo les interdit dans les deux cas."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### CIF à unicité complète"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La notion d'agrégation n'a pas très bonne presse dans les milieux autorisés. Ceux-ci lui préfèrent généralement celle (équivalente) de contrainte d'intégrité fonctionnelle à unicité complète. Pour rentrer dans leurs bonnes grâces (et désactiver du même coup la visualisation de l'enveloppe et du petit rond), il suffit d'expliciter la contrainte correspondante dans le MCD précédent. Mocodo peut le faire pour vous :"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"%%mocodo --seed=12 --colors ocean\n",
"Date: Date\n",
"Réserver, /1N Client, 1N Chambre, 0N Date: Durée\n",
"Chambre: Num. chambre, Prix\n",
"\n",
"-INVISIBLE_1, XX Client, XX Client\n",
"Client: Id. client\n",
":\n",
"\n",
"(CIF) ..Réserver, ->Client, Chambre, Date: INVISIBLE_1, INVISIBLE_1\n"
]
}
],
"source": [
"%mocodo -i sandbox -t create:cifs=light arrange --seed=12 --colors ocean"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Limitation.** Au niveau fonctionnel, c'est toujours la barre oblique qui conditionne le traitement des CIF. Par conséquent, les CIF à unicité _incomplète_ ne sont prises en charge qu'au niveau visuel."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Identifiants explicites dans les associations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mocodo 4.1.1 relaxe par défaut (sauf dans la version en ligne) la contrainte de Merise restreignant aux entités la présence d'identifiants. Si vous l'osez, vous pouvez donc obtenir le même schéma relationnel avec le MCD « allégé » suivant :"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Chambre** (Num. chambre, Prix)\n",
"- **Réserver** (Id. client, _#Num. chambre_, Date, Durée)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t\n",
"Client: Id. client\n",
"Réserver, 1N Client, 0N Chambre: _Date, Durée\n",
"Chambre: Num. chambre, Prix"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'introduction de cette possibilité, surtout destinée à simplifier le plongement des MCD touffus, est discutée [ici](#Par-suppression-de-ces-mêmes-entités)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Conversion d'un MCD"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Passage au relationnel"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Il se fait en deux étapes:\n",
"- la création d'une représentation **interne** complète du MLD ;\n",
"- la traduction de celle-ci en une représentation **externe** dans le ou les formats de sortie souhaités."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pour construire la **représentation interne**, l'algorithme de base réalise la séquence d'opérations suivante :\n",
"\n",
"1. Pour chaque entité, une relation (table) de même nom et de mêmes attributs est créée. Le ou les identifiants de l'entité constituent la clé primaire de la relation.\n",
"2. Toute relation issue d'une entité faible est renforcée, c'est-à-dire que la clé primaire de l'entité qu'elle détermine fonctionnellement vient s'adjoindre à sa clé primaire, au besoin de façon récursive.\n",
"3. Les associations sont traitées ainsi :\n",
" 1. si toutes les pattes de l'association portent la cardinalité maximale N, une relation de même nom et de mêmes attributs est créée. Sa clé primaire est constituée de l'ensemble des clés primaires des relations issues des entités mises en jeu ;\n",
" 2. dans le cas contraire, c'est-à-dire si l'une des pattes de l'association porte la cardinalité (1,1), ou à défaut (0,1), l'entité distinguée se voit adjoindre :\n",
" 1. en tant que clés étrangères, l'ensemble des clés primaires des autres entités mises en jeu;\n",
" 2. en tant que simples attributs, l'ensemble des attributs de l'association.\n",
"\n",
"**Remarque.** Un couple de cardinalités non standard, c'est-à-dire distinct de (0,1), (1,1), (0,N) et (1,N), est traité comme (0,1) si son deuxième symbole est un 1, et comme (0,N) sinon. Cela couvre en particulier les cas (\\*, 1), (\\*,N), (?,?) et (X,X)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pour construire la **représentation externe**, Mocodo formate la représentation interne à l'aide d'un gabarit. Près d'une centaine de gabarits sont fournis, qui permettent de produire quatre grandes catégories de résultats :\n",
"1. le schéma relationnel dans toutes les combinaisons de formats (HTML, Markdown, texte brut, $\\LaTeX$) et d'options (avec ou sans explications, avec ou sans _boilerplate_, avec ou sans visualisation des contraintes d'unicité et d'optionalité) ;\n",
"2. le diagramme relationnel au format Mocodo, avec ou sans visualisation des contraintes d'unicité et d'optionalité, ou dans des formats externes, comme DBML ou D2.\n",
"3. le DDL en SQL ANSI et dans les principaux dialectes (Microsoft SQL Server, MySQL, Oracle DB, PostgreSQL, SQLite), avec ou sans _boilerplate_ de création de la base ;\n",
"4. le graphe des dépendances."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Niveau logique"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Schéma relationnel"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La transformation `-t mld` opère la conversion d'un MCD (modèle conceptuel de données) en un MLD (modèle logique de données), autrement appelé **schéma relationnel** :"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/latex": [
"\\begin{Verbatim}[commandchars=\\\\\\{\\}]\n",
"\\PYZpc{}\\PYZpc{}mocodo\n",
":\n",
"Client: Réf. client, Nom, Prénom, Adresse\n",
":\n",
"Commande: Num. commande, Date, Montant, \\PYZsh{}Réf. client \\PYZgt{} Client \\PYZgt{} Réf. client\n",
":\n",
"Inclure: \\PYZsh{}Num. commande \\PYZgt{} Commande \\PYZgt{} Num. commande, \\PYZus{}\\PYZsh{}Réf. produit \\PYZgt{} Produit \\PYZgt{} Réf. produit, Quantité\n",
":\n",
"Produit: Réf. produit, Libellé, Prix unitaire\n",
":\n",
"\\end{Verbatim}\n"
],
"text/plain": [
"%%mocodo\n",
":\n",
"Client: Réf. client, Nom, Prénom, Adresse\n",
":\n",
"Commande: Num. commande, Date, Montant, #Réf. client > Client > Réf. client\n",
":\n",
"Inclure: #Num. commande > Commande > Num. commande, _#Réf. produit > Produit > Réf. produit, Quantité\n",
":\n",
"Produit: Réf. produit, Libellé, Prix unitaire\n",
":"
]
},
"execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"display.Code(\"ccp_mld.mcd\", language=\"text\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La syntaxe d'un MLD est effectivement la même que celle d'un MCD, à ceci près que les associations sont remplacées par des liens allant de l'attribut `a1` de l'entité `E1` à l'attribut `a2` de l'entité `E2`, et notés : `E1: ... a1 > E2 > a2`.\n",
"\n",
"Les relations sont placées dans le même ordre que les boîtes du MCD d'origine. Vous devrez quelquefois les réarranger (automatiquement ou manuellement) pour obtenir un résultat plus esthétique. Notez que des boîtes invisibles ont été automatiquement insérées une colonne sur deux afin de laisser de la place aux flèches."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Limitation.** Les clés étrangères composites sont actuellement représentées comme si elles étaient séparées (autant de flèches que de parties)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Nouveautés de la version 4.0.**\n",
"- Ce qui se notait `E1: ... a1->E2->a2` se note maintenant `E1: ... a1 > E2 > a2`.\n",
"- Sous Jupyter Notebook, un seule étape suffit pour visualiser le diagramme relationnel.\n",
"- Le nom du fichier intermédiaire se termine par `_mld.mcd` au lieu de `.mld`. L'ancienne extension est abandonnée."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Niveau physique : DDL"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### SQL"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La principale raison de vivre d'un MCD est de se métamorphoser _in fine_ en une chatoyante base de données relationnelles. Cela se fait au moyen d'un sous-ensemble du langage SQL appelé DDL (_Data Definition Language_). Sous Mocodo :"
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"```sql\n",
"CREATE TABLE Client (\n",
" PRIMARY KEY (Ref_client),\n",
" Ref_client VARCHAR(8) NOT NULL,\n",
" Nom VARCHAR(255),\n",
" Prenom VARCHAR(255),\n",
" Adresse VARCHAR(255)\n",
");\n",
"\n",
"CREATE TABLE Commande (\n",
" PRIMARY KEY (Num_commande),\n",
" Num_commande VARCHAR(8) NOT NULL,\n",
" Date DATE,\n",
" Montant DECIMAL(10,2),\n",
" Ref_client VARCHAR(8) NOT NULL\n",
");\n",
"\n",
"CREATE TABLE Inclure (\n",
" PRIMARY KEY (Num_commande, Ref_produit),\n",
" Num_commande VARCHAR(8) NOT NULL,\n",
" Ref_produit VARCHAR(8) NOT NULL,\n",
" Quantite INTEGER\n",
");\n",
"\n",
"CREATE TABLE Produit (\n",
" PRIMARY KEY (Ref_produit),\n",
" Ref_produit VARCHAR(8) NOT NULL,\n",
" Libelle VARCHAR(50),\n",
" Prix_unitaire DECIMAL(10,2)\n",
");\n",
"\n",
"ALTER TABLE Commande ADD FOREIGN KEY (Ref_client) REFERENCES Client (Ref_client);\n",
"\n",
"ALTER TABLE Inclure ADD FOREIGN KEY (Ref_produit) REFERENCES Produit (Ref_produit);\n",
"ALTER TABLE Inclure ADD FOREIGN KEY (Num_commande) REFERENCES Commande (Num_commande);\n",
"\n",
"```"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t sql"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notez le `NOT NULL` des clés primaires. Il s'agit d'une redondance, mais aussi d'une bonne pratique. Non seulement parce qu'_explicit is better than implicit_ : elle peut se révéler utile, par exemple pour désactiver les contraintes de clé primaire pendant une maintenance tout en continuant à interdire le remplissage avec des `NULL`. Si cela vous gêne, nous montrons en annexe comment l'éliminer."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Principales nouveautés de la version 4.0.**\n",
"- Génération automatique des contraintes `UNIQUE`, `NOT NULL` et `NULL` appropriées.\n",
"- Les libellés sont convertis en ASCII, et toute suite de caractères qui ne sont ni des lettres, ni des chiffres, ni des tirets bas sont remplacés par un unique tiret bas. Cela simplifie la référence aux tables et aux champs, et évite de noyer le DDL sous un essaim de délimiteurs (qui plus est spécifiques à chaque dialecte). Ainsi, « Réf. client » se trouvera transformé en `Ref_client`.\n",
"- Vos choix de casse (minuscules, MAJUSCULES, _snake_case_, _camelCase_, _PascalCase_, etc.) sont respectés."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Inférence de type"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Avec l'option de réécriture `-t create:types` Mocodo essaiera d'inférer des libellés des attributs tout type manquant. La langue définie avec l'option `--language` est prioritaire, mais l'anglais prend la relève en cas d'échec."
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"%%mocodo --select rw\n",
"Customer: Customer ID [VARCHAR(8)], Last Name [VARCHAR(255)], First Name [VARCHAR(255)], Address [VARCHAR(30)]\n",
"Make Order, 0N Customer, 11 Order\n",
"Order: Order Number [VARCHAR(8)], Date [DATE], Amount [DECIMAL(10,2)]\n",
"INCLUDE, 1N Order, 0N Product: Quantity [INTEGER]\n",
"Product: Product ID [VARCHAR(8)], Description [TEXT], Unit Price [DECIMAL(10,2)]\n"
]
}
],
"source": [
"%%mocodo -t create:types --select rw\n",
"Customer: Customer ID, Last Name, First Name, Address\n",
"Make Order, 0N Customer, 11 Order\n",
"Order: Order Number, Date, Amount\n",
"INCLUDE, 1N Order, 0N Product: Quantity\n",
"Product: Product ID, Description, Unit Price"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Il va de soi que les types inférés devront être systématiquement contrôlés, et parfois corrigés ou complétés. Une discussion sur StackOverflow, [Common MySQL fields and their appropriate data types](https://stackoverflow.com/questions/354763/common-mysql-fields-and-their-appropriate-data-types), a servi de point de départ. La liste des types utilisée est [celle de Wikipedia](https://en.wikibooks.org/wiki/Structured_Query_Language/Data_Types). Consultez les correspondances spécifiées dans `mocodo/resources/default_datatypes_fr.tsv`, etc. pour plus de détails, et n'hésitez pas à [ouvrir le débat](https://github.com/laowantong/mocodo/issues/new) si vous avez des corrections ou des suggestions."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Si vous préférez tout typer à la main, vous pouvez au moins créer les « cases » à remplir :"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"%%mocodo --select rw\n",
"Customer: Customer ID [], Last Name [], First Name [], Address []\n",
"Make Order, 0N Customer, 11 Order\n",
"Order: Order Number [], Date [], Amount []\n",
"INCLUDE, 1N Order, 0N Product: Quantity []\n",
"Product: Product ID [], Description [], Unit Price []\n"
]
}
],
"source": [
"%%mocodo -t create:types=[] --select rw\n",
"Customer: Customer ID, Last Name, First Name, Address\n",
"Make Order, 0N Customer, 11 Order\n",
"Order: Order Number, Date, Amount\n",
"INCLUDE, 1N Order, 0N Product: Quantity\n",
"Product: Product ID, Description, Unit Price"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Dialectes de SQL"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"L'argument `sql` peut être remplacé par un nom de dialecte parmi `mysql`, `oracle`, `postgresql`, `sqlite` et (nouveauté de la version 4) `mssql`. Quelques transformations seront alors appliquées au code-source généré. Du fait de l'uniformisation de la syntaxe apportée par la version 4, elles sont assez minimes.\n",
"\n",
"Notez les points suivants :\n",
"- Mocodo ne touche pas aux types spécifiés par l'utilisateur. Une traduction inter-dialectes automatique est envisageable dans le futur, mais c'est un problème qui n'a en réalité aucune solution satisfaisante.\n",
"- Mocodo protège les noms de tables ou de colonnes qui se trouveraient faire partie des mots réservés par le dialecte-cible. Par exemple, ci-dessous, les noms `USER` et `MEMBER` sont réservés en MySQL. Les listes viennent du site [modern-sql.com](https://modern-sql.com) de Markus Winand, et plus précisément de la page https://modern-sql.com/reserved-words-empirical-list.\n",
"- Avec la sous-sous-option `b`, un _boilerplate_ de création de la base est ajouté en préambule."
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **CLIENT** (Réf. client, Nom u1, Prénom u1, Adresse, Mail u2)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --select all -t mld:c --shapes trebuchet # changement de police de caractères pour mieux distinguer les 1 des I\n",
"CLIENT: Réf. client, 1_Nom, 1_Prénom, Adresse, 2_Mail"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"C'est l'occasion d'introduire quelques définitions :\n",
"\n",
"- On appelle **identifiant candidat** d'une entité tout sous-ensemble minimal d'attributs dont chaque occurrence est unique.\n",
"- Parmi ces sous-ensembles, l'un est élu **identifiant** (tout court), souligné, et appelé à devenir clé primaire lors du passage au relationnel.\n",
"- Les candidats malheureux sont appelés **identifiants alternatifs**.\n",
"\n",
"Ainsi, dans l'exemple précédent, l'ensemble des identifiants candidats de CLIENT est constitué de :\n",
"\n",
"- l'identifiant proprement dit _Réf. client_ ;\n",
"- l'identifiant alternatif (_Nom_, _Prénom_) (dont on supposera pour les besoins de la cause qu'il assure l'unicité) ;\n",
"- l'identifiant alternatif _Mail_."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Par défaut, dès la première déclaration d'un identifiant alternatif, Mocodo fait apparaître une gouttière à gauche des attributs de **toutes** les entités. Y sont portés :\n",
"\n",
"- un symbole « ID » (resp. « id ») pour l'identifiant fort (resp. faible).\n",
"- des chiffres de 1 à 9 correspondant aux numéros des identifiants alternatifs."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La notation de Mocodo permet de faire de n'importe quels sous-ensembles d'attributs des identifiants alternatifs. Ci-dessous, le premier identifiant alternatif est le triplet (_bar_, _biz_, _quux_), le second (_biz_, _buz_, _quux_) et le troisième (_qux_, _quux_) :"
]
},
{
"cell_type": "code",
"execution_count": 57,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"```sql\n",
"CREATE TABLE FOO (\n",
" PRIMARY KEY (foo),\n",
" foo VARCHAR(42) NOT NULL,\n",
" bar VARCHAR(42),\n",
" biz VARCHAR(42),\n",
" buz VARCHAR(42),\n",
" qux VARCHAR(42),\n",
" quux VARCHAR(42),\n",
" UNIQUE (bar, biz, quux),\n",
" UNIQUE (biz, buz, quux),\n",
" UNIQUE (qux, quux)\n",
");\n",
"\n",
"```"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --select all -t markdown:c sql --shapes trebuchet\n",
"FOO: foo, 1_bar, 12_biz, 2_buz, 3_qux, 123_quux"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Examinons le cas exceptionnel où l'identifiant à souligner a un attribut commun avec un identifiant alternatif. Ce dernier devant être distinct et minimal, cela implique que l'identifiant est composite."
]
},
{
"cell_type": "code",
"execution_count": 58,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --shapes trebuchet\n",
"Entité 1_: 1_foo, _bar, 1_biz\n",
"Entité 2_: foo, 1_bar, 1_biz\n",
"Entité 3_: foo, 01_bar, 1_biz"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Si l'attribut commun est en tête de liste (ici, _foo_, entité 1), rien ne change, on écrit `1_foo`.\n",
"- S'il n'est pas en tête de liste (ici, _bar_, entité 2), l'écriture `1_bar` dénote déjà l'appartenance à un identifiant alternatif. Elle ne peut dénoter simultanément l'appartenance à l'identifiant à souligner.\n",
"- C'est pourquoi on doit donc expliciter l'appartenance à l'identifiant à souligner qui, comme on l'a vu, est numéroté `0` (entité 3)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Un sous-cas demande encore réflexion : celui où le premier attribut ne fait pas partie de l'identifiant à souligner (entité 4). Si l'on veut alors que ce premier attribut appartienne à une clé alternative, il faut expliciter le `0` (entité 5)."
]
},
{
"cell_type": "code",
"execution_count": 59,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --shapes trebuchet\n",
"Entité 40: _foo, _bar, biz\n",
"Entité 50: 01_foo, _bar, 1_biz"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"En résumé, on explicite le `0`, soit pour **empêcher le soulignement du premier attribut**, soit pour **forcer le soulignement d'un attribut suivant**.\n",
"\n",
"Cela devrait vous rappeler quelque chose… Remplacez `0` par `_` dans la phrase précédente et vous retrouverez la règle que vous avez appris à connaître et à aimer : on explicite le `_`, soit pour empêcher le soulignement du premier attribut, soit pour forcer le soulignement d'un attribut suivant."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Gestion des entités faibles (identification relative)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dans ce joli exemple dû à Idris NEUMANN, [_Initiation à la conception de bases de données relationnelles avec MERISE_](http://ineumann.developpez.com/tutoriels/merise/initiation-merise/#LIV-A), les renforcements successifs aboutissent à faire entrer l'identifiant de RUE dans celui de APPARTEMENT, alors même que ces entités sont séparées par non moins de trois associations :"
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Appartement** (_#code rue_, _#num immeuble_, _#num étage_, num appart., nb pièces)\n",
"- **Étage** (_#code rue_, _#num immeuble_, num étage, nb appartements)\n",
"- **Immeuble** (_#code rue_, num immeuble, nb étages)\n",
"- **Rue** (code rue, nom rue)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t\n",
"Appartement: num appart., nb pièces\n",
"Composer, 0N Étage, _11 Appartement\n",
"Étage: num étage, nb appartements\n",
"Appartenir, 1N Immeuble, _11 Étage\n",
"Immeuble: num immeuble, nb étages\n",
"Se situer, 0N Rue, _11 Immeuble\n",
"Rue: code rue, nom rue"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Le résultat apparaît plus clairement sur le diagramme relationnel :"
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i sandbox -t diagram --colors mondrian"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Une phase préliminaire de l'algorithme de passage au relationnel consiste à « renforcer » les entités faibles de façon à traiter uniquement des identifiants forts dans la suite. Cela permet la gestion des renforcements en cascade comme ci-dessus, ainsi que la détection des problèmes de renforcement cyclique :"
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"Pick, 0N Land, _11 Peer\n",
"Land: true, hold\n",
"\n",
"Peer: foot, city\n",
"Zone, 1N Peer, _11 Land"
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Mocodo Err.17 - Cycle d'entités faibles dans \"Land\", \"Peer\"."
]
}
],
"source": [
"%mocodo -i sandbox -t"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Gestion de l'héritage"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La bonne gestion d'un héritage est primordiale, tant du point de vue de la préservation du patrimoine, que de l'optimisation des ressources financières, de la minimisation des conflits familiaux et du respect des dernières volontés du défunt. Je blague.\n",
"\n",
"Adaptons ici un exemple d'un [cours de Stéphane Crozat](https://stph.scenari-community.org/bdd/rel3.pdf). Nous reprenons sa terminologie et donnons en parallèle celle introduite par Martin Fowler dans _Patterns of Enterprise Application Architecture_, Addison-Wesley (2003).\n",
"\n",
"Ci-dessous, l'héritage est considéré comme _total_ (tout document est soit un ouvrage, soit une thèse, soit les deux). Crozat passe en revue trois mécanismes possibles pour le passage au relationnel."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"L'**héritage par référence** (en anglais, [_Class Table Inheritance_](http://martinfowler.com/eaaCatalog/classTableInheritance.html) ou _Table Per Type Inheritance_) se note en Mocodo par une flèche simple allant vers les filles. Une référence à la table-mère sera ajoutée dans chaque table-fille comme clé étrangère. L'intérêt de cette solution est en raison directe du nombre d'attributs non identifiants de l'entité-mère."
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **DOCUMENT** (cote, titre, auteur)\n",
"- **OUVRAGE** (_#cote_, éditeur)\n",
"- **THÈSE** (_#cote_, discipline, université)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --select all -t mld:c\n",
"DOCUMENT: cote, titre, auteur\n",
" \n",
"OUVRAGE: éditeur\n",
"/T\\ DOCUMENT -> OUVRAGE, THÈSE\n",
"THÈSE: discipline, université"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"\n",
"L'**héritage par absorption dans les tables-filles** (en anglais, [_Concrete Table Inheritance_](http://martinfowler.com/eaaCatalog/concreteTableInheritance.html) ou _Table Per Concrete Inheritance_) se note en doublant la flèche :\n",
"\n",
"1. les attributs de la table-mère sont reproduits dans chacune des filles. Pour chaque document qui est à la fois un ouvrage et une thèse, il y aura donc duplication des triplets (cote, titre, auteur).\n",
"2. la table-mère disparaît. Pour éviter de perdre les informations sur les documents qui ne seraient ni des ouvrages, ni des thèses, Mocodo lève une erreur si l'héritage n'est pas total (T ou XT)."
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Aborder** (_#thème_, _#id. formation_)\n",
" - Le champ _thème_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Thème_.\n",
" - Le champ _id. formation_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Formation_.\n",
"\n",
"- **Animateur** (num. animateur, nom animateur)\n",
" - Le champ _num. animateur_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Animateur_.\n",
" - Le champ _nom animateur_ était déjà un simple attribut de l'entité _Animateur_.\n",
"\n",
"- **Formation** (id. formation, durée)\n",
" - Le champ _id. formation_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Formation_.\n",
" - Le champ _durée_ était déjà un simple attribut de l'entité _Formation_.\n",
"\n",
"- **Intervenir** (_#num. animateur_, _#id. formation_, date, nb heures)\n",
" - Le champ _num. animateur_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Animateur_.\n",
" - Le champ _id. formation_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Formation_.\n",
" - Le champ _date_ fait partie de la clé primaire de la table. Sa table d'origine (_Date_) ayant été supprimée, il n'est pas considéré comme clé étrangère.\n",
" - Le champ _nb heures_ était déjà un simple attribut de l'association _Intervenir_.\n",
"\n",
"- **Thème** (thème)\n",
" - Le champ _thème_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Thème_.\n",
" \n",
"----\n",
"\n",
"\n",
"**NB.** La table _Date_ a été supprimée car elle était réduite à la clé primaire de son entité d'origine. Pour conserver de telles tables, préfixez d'un « + » la définition des entités d'origine.\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --select mld mcd -t mld:e\n",
"Animateur: num. animateur, nom animateur\n",
"Intervenir, 1N Animateur, 1N Formation, 1N Date: nb heures\n",
"Formation: id. formation, durée\n",
"Aborder, 1N Thème, 1N Formation\n",
"+Thème: thème\n",
"\n",
"Date: date\n",
":"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Notez l'explication de la suppression (en NB) et celle de la perte du caractère étranger de l'attribut _date_ (dans la table Intervenir). Pour une discussion sur cette problématique, cf. [issue #66](https://github.com/laowantong/mocodo/issues/66)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Forcer une table pour éviter un champ optionnel"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Une association de dépendance fonctionnelle ne donne normalement pas lieu à une création de table. Pour reprendre un exemple vu plus haut :"
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Entreprise** (id. entreprise, raison, activité, adresse)\n",
"- **Participant** (id. inscrit, nom, adresse, _#id. entreprise?_)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --select all -t mld:c\n",
"Entreprise: id. entreprise, raison, activité, adresse\n",
"Envoyer, 0N Entreprise, 01 Participant\n",
"Participant: id. inscrit, nom, adresse"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Dans le cas où les particuliers sont beaucoup plus nombreux que les employés d'entreprise, la clé étrangère _#id. entreprise_ est presque toujours à `NULL`. C'est une perte d'espace de stockage. On peut entourer l'association de crochets droits préfixer d'un `+` (à partir de la version 4) l'association pour forcer sa conversion en table. Mocodo produit alors une visualisation intermédiaire entre entité et association :"
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Entreprise** (id. entreprise, raison, activité, adresse)\n",
"- **Envoyer** (_#id. inscrit_, _#id. entreprise!_)\n",
"- **Participant** (id. inscrit, nom, adresse)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo --select all -t mld:c --colors mondrian\n",
"Entreprise: id. entreprise, raison, activité, adresse\n",
"+Envoyer, 0N Entreprise, 01 Participant\n",
"Participant: id. inscrit, nom, adresse"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cela permet du même coup d'éviter un champ optionnel, dont la gestion peut être délicate (notamment sous Microsoft SQL Server qui, au mépris du standard SQL, ne semble pas convaincu qu'une colonne `UNIQUE` peut contenir plus d'un `NULL` !)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Préciser le rôle d'une clé étrangère"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Ce MCD modélise la soutenance de stage des étudiants, ainsi que la visite d'amitié et de contrôle dont les honore leur enseignant responsable :"
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Enseignant** (num. enseignant, nom, coordonnées)\n",
"- **Étudiant** (num. étudiant, nom, coordonnées, date 1, note stage, date 2, _#num. enseignant_)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t\n",
"Soutenir, 01 Étudiant, 0N Date: note stage\n",
"Étudiant: num. étudiant, nom, coordonnées\n",
"\n",
"Date: date\n",
"Répondre de, 0N Date, 11 Étudiant, 0N Enseignant\n",
"Enseignant: num. enseignant, nom, coordonnées"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Force est de constater que la table Étudiant laisse quelque peu à désirer du point de vue sémantique :\n",
"\n",
"- on ne sait pas à quoi correspondent les attributs _date 1_ et _date 2_ : ils ne peuvent en tout cas certainement pas être laissés en l'état ;\n",
"- ensuite, on pourrait avoir envie d'expliciter la raison pour laquelle un enseignant apparaît parmi les attributs d'un étudiant.\n",
"\n",
"Ces précisions peuvent être apportées en insérant, entre la cardinalité et l'entité des pattes appropriées, une note entre crochets, appelée **rôle**. Ces rôles seront utilisés pour compléter le nom des clés étrangères correspondantes. Cela permet de réintroduire la sémantique perdue lors de la disparition des associations de dépendance fonctionnelle par lesquelles elle ont migré :"
]
},
{
"cell_type": "code",
"execution_count": 74,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Enseignant** (num. enseignant, nom, coordonnées)\n",
"- **Étudiant** (num. étudiant, nom, coordonnées, date soutenance, note stage, date visite resp, _#num. ens. resp._)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t mld\n",
"Soutenir, 01 Étudiant, 0N [soutenance] Date: note stage\n",
"Étudiant: num. étudiant, nom, coordonnées\n",
"\n",
"Date: date\n",
"Répondre de, 0N [+ visite resp] Date, 11 Étudiant, 0N [-num. ens. resp.] Enseignant\n",
"Enseignant: num. enseignant, nom, coordonnées"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La composition du rôle est entièrement paramétrable :\n",
"\n",
"- _date soutenance_ : par défaut, le rôle est concaténé à la clé, avec un séparateur dépendant du gabarit (espace par défaut, tiret bas pour SQL).\n",
"- _date visite resp._ : `+` en préfixe supprime le séparateur.\n",
"- _num. ens. resp._ : `-` en préfixe supprime le nom de la clé et le remplace par le rôle.\n",
"\n",
"Pour ne pas surcharger le dessin, le rôle n'est pas affiché à côté du lien, mais il apparaît au survol de la cardinalité."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Correction de la version 4.0.** Dans une association réflexive hiérarchique, c'est désormais le rôle porté par la patte `*N` qui sert à rétablir la sémantique. Auparavant c'était l'inverse, en contradiction avec le traitement des associations binaires et n-aires : si vous avez utilisé des rôles dans une association réflexive avant la version 4.0.0, vous devez donc les permutez pour que Mocodo les traite correctement. Reprenons la filiation patrilinéaire :"
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **HOMME** (num. SS, nom, prénom, _#num. SS père_)\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t\n",
"HOMME: num. SS, nom, prénom\n",
"ENGENDRER, 0N [père] HOMME, 01 [fils] HOMME"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Orienter la migration dans les DF à double sens"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lorsque toutes les cardinalités d'une dépendance fonctionnelle sont 11 (ou toutes 01), le sens de migration est spécifié à coût zéro en référençant l'entité réceptrice en tête de la liste des entités énumérées dans la clause de définition de l'association :"
]
},
{
"cell_type": "code",
"execution_count": 76,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"```sql\n",
"CREATE TABLE AUTHENTICATION (\n",
" PRIMARY KEY (user_id, email),\n",
" user_id VARCHAR(8) NOT NULL,\n",
" email VARCHAR(255) NOT NULL,\n",
" password_hash BINARY(64),\n",
" salt BINARY(16)\n",
");\n",
"\n",
"CREATE TABLE USER (\n",
" PRIMARY KEY (user_id),\n",
" user_id VARCHAR(8) NOT NULL,\n",
" name VARCHAR(100),\n",
" pseudo VARCHAR(100)\n",
");\n",
"\n",
"ALTER TABLE AUTHENTICATION ADD FOREIGN KEY (user_id) REFERENCES USER (user_id);\n",
"\n",
"```"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t mld diagram sql --colors mondrian\n",
"USER: user id [VARCHAR(8)], name [VARCHAR(100)], pseudo [VARCHAR(100)]\n",
"DF, _11 AUTHENTICATION, 11 USER\n",
"AUTHENTICATION: email [VARCHAR(255)], password hash [BINARY(64)], salt [BINARY(16)]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Les traitements alternatifs (fusion en une seule table ou migration dans les deux sens) ne sont pas pris en charge par Mocodo. La dernière pratique ressortit à la phase d'optimisation et de dénormalisation de la base, qui est en dehors de sa juridiction. La seule chose que vous pouvez faire, à des fins d'illustration, est de reprendre le MLD généré pour y ajouter à la main la migration inverse. Notez cependant que les schémas logique et physique générés seront alors dépourvus de toute clé étrangère (et nécessiteront donc également d'être retouchés à la main)."
]
},
{
"cell_type": "code",
"execution_count": 78,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"```sql\n",
"CREATE TABLE AUTHENTICATION (\n",
" PRIMARY KEY (email),\n",
" email VARCHAR(255) NOT NULL,\n",
" password_hash BINARY(64),\n",
" salt BINARY(16),\n",
" user_id VARCHAR(42)\n",
");\n",
"\n",
"CREATE TABLE USER (\n",
" PRIMARY KEY (user_id),\n",
" user_id VARCHAR(8) NOT NULL,\n",
" name VARCHAR(100),\n",
" pseudo VARCHAR(100),\n",
" email VARCHAR(42)\n",
");\n",
"\n",
"```"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo -t mld sql --colors mondrian --select all\n",
":\n",
"USER: user id [VARCHAR(8)], name [VARCHAR(100)], pseudo [VARCHAR(100)], #email > AUTHENTICATION > email\n",
":\n",
"AUTHENTICATION: email [VARCHAR(255)], password hash [BINARY(64)], salt [BINARY(16)], #user id > USER > user id\n",
":"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Autres sorties relationnelles"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Graphe des dépendances"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"La table A **dépend** de la table B lorsque A possède une clé étrangère qui est clé primaire de B. Cette notion est utile dans le cas où l'on doit importer une base de données à partir d'un ensemble de fichiers CSV (ou autres). Si l'on veut éviter de perdre le bénéfice du contrôles de clés étrangères (en faisant, p. ex. sous MySQL, `SET FOREIGN_KEY_CHECKS = 0`), il conviendra de lire ces fichiers dans un [ordre topologique](https://fr.wikipedia.org/wiki/Tri_topologique) quelconque. Mocodo peut générer un graphe des dépendances qui met cet ordre en évidence :"
]
},
{
"cell_type": "code",
"execution_count": 79,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t uml --defer --colors brewer+5"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### ERD avec la convention _Look across_"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Nouveauté de la version 4.0.** Force est de reconnaître que de nos jours, les MCD à la sauce Merise ne sont plus goûtés que par une poignée d'irréductibles Gaulois (et contractuellement leurs étudiants). Dans le cadre de son projet secret de domination planétaire, Mocodo commence à faire du pied à des notations mieux comprises du reste de l'univers."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Notation de Chen"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Un MCD peut être converti en un ERD (_Entity-Relationship Diagram_) dans la notation de Chen, sans ses attributs ou avec :"
]
},
{
"cell_type": "code",
"execution_count": 86,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t crow --defer --colors brewer+3"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Il est également possible de demander une sortie au format Mermaid. Vous en avez un exemple dans l'introduction, nous ne le répétons pas ici. Le DSL de Mermaid est de plus haut niveau, non encombré d'informations de style. Cependant, le format Graphviz peut être préféré pour plusieurs raisons :\n",
"\n",
"- on peut lui appliquer une palette de couleurs de Mocodo (cf. ci-dessus) ;\n",
"- il gère les accents ;\n",
"- il admet la virgule dans les types (Mermaid demande [actuellement](https://github.com/mermaid-js/mermaid/issues/1546) à transformer `DECIMAL(10,2)` en `DECIMAL(10-2)`) ;\n",
"- il peut produire de meilleurs plongements (notamment en jouant sur la valeur de l'option `--seed`) ;\n",
"- ceux-ci peuvent-être rectifiés à la main (quoique péniblement)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Dictionnaire des données"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vous pouvez extraire sous forme de table diverses informations sur les attributs de votre MCD.\n",
"\n",
"Un format possible est TSV. Dans ce cas, sous Jupyter Notebook, si la bibliothèque `pandas` est installée, elle sera rendue comme un _dataframe_ :"
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t data_dict:tsv"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Le format par défaut est Markdown. Vous pouvez préciser en sous-sous-option tout ou partie des colonnes suivantes dans l'ordre où vous les souhaitez :\n",
"\n",
"- `label`: le libellé de l'attribut ;\n",
"- `type` : son type ou un descriptif (auquel cas il conviendra de changer le nom de la colonne) ;\n",
"- `box` : le nom de l'entité ou association où il se trouve.\n",
"\n",
"Entourez ces noms de colonnes de balises Markdown pour les mettre en forme (pas d'incidence en TSV). Faites-les suivre de `=\"Nom de colonne personnalisé\"` pour éviter la valeur par défaut (dépendante de l'option `language`). Dans l'exemple ci-dessous, les sous-sous-options se décodent ainsi :\n",
"\n",
"- `**box**=\"Entité ou association\"` : boîtes en colonne 1, en-tête personnalisé, cellules en gras ;\n",
"- `label` : libellé des attributs en colonne 2 ;\n",
"- \\``type`\\``=`\\``Type`\\` : types en colonne 3, en-tête personnalisé et cellules dans une police non proportionnelle."
]
},
{
"cell_type": "code",
"execution_count": 90,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"- **Client** (Réf. client, Nom, Prénom, Adresse)\n",
" - Le champ _Réf. client_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Client_.\n",
" - Les champs _Nom_, _Prénom_ et _Adresse_ étaient déjà de simples attributs de l'entité _Client_.\n",
"\n",
"- **Commande** (Num. commande, Date, Montant, _#Réf. client_)\n",
" - Le champ _Num. commande_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Commande_.\n",
" - Les champs _Date_ et _Montant_ étaient déjà de simples attributs de l'entité _Commande_.\n",
" - Le champ _Réf. client_ est une clé étrangère. Il a migré par l'association de dépendance fonctionnelle _Passer_ à partir de l'entité _Client_ en perdant son caractère identifiant.\n",
"\n",
"- **Inclure** (_#Num. commande_, _#Réf. produit_, Quantité)\n",
" - Le champ _Num. commande_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Commande_.\n",
" - Le champ _Réf. produit_ fait partie de la clé primaire de la table. C'est une clé étrangère qui a migré directement à partir de l'entité _Produit_.\n",
" - Le champ _Quantité_ était déjà un simple attribut de l'association _Inclure_.\n",
"\n",
"- **Produit** (Réf. produit, Libellé, Prix unitaire)\n",
" - Le champ _Réf. produit_ constitue la clé primaire de la table. C'était déjà un identifiant de l'entité _Produit_.\n",
" - Les champs _Libellé_ et _Prix unitaire_ étaient déjà de simples attributs de l'entité _Produit_.\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t markdown:e"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Nous avons essayé d'être aussi précis que possible, tout en « factorisant » avec soin les lignes consécutives suseptibles de l'être. Vous pouvez adapter ces explications à votre enseignement en modifiant une copie du gabarit `html-ce.yaml`, dont les autres sont dérivés."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## MCD à compléter"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Les MCD à trous sont des exercices classiques d'introduction aux bases de données."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Supprimer le marquage d'un identifiant"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Pour éviter le marquage automatique du premier attribut d'une entité comme identifiant, il suffit de le préfixer par un tiret bas (`_`) : ce caractère est donc un commutateur, qui souligne un attribut non souligné par défaut, et désouligne un attribut souligné par défaut."
]
},
{
"cell_type": "code",
"execution_count": 104,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tPASSER\n",
"\t\n",
"\t0,N\n",
"\t1,1\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tINCLURE\n",
"\t\tQuantité\n",
"\t\n",
"\t1,N\n",
"\t0,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCLIENT\n",
"\tRéf. client\n",
"\tNom\n",
"\tPrénom\n",
"\tAdresse\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCOMMANDE\n",
"\tNum. commande\n",
"\tDate\n",
"\tMontant\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tPRODUIT\n",
"\tRéf. produit\n",
"\tLibellé\n",
"\tPrix unitaire\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"CLIENT: _Réf. client, Nom, Prénom, Adresse\n",
"PASSER, 0N CLIENT, 11 COMMANDE\n",
"COMMANDE: _Num. commande, Date, Montant\n",
"INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité\n",
"PRODUIT: _Réf. produit, Libellé, Prix unitaire"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Masquer un couple de cardinalités"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vous pouvez masquer n'importe quelles cardinalités en les remplaçant par `XX` ou en les préfixant d'un `-` :"
]
},
{
"cell_type": "code",
"execution_count": 105,
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tPASSER\n",
"\t\n",
"\t\n",
"\t\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tINCLURE\n",
"\t\tQuantité\n",
"\t\n",
"\t\n",
"\t\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCLIENT\n",
"\tRéf. client\n",
"\t\n",
"\tNom\n",
"\tPrénom\n",
"\tAdresse\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCOMMANDE\n",
"\tNum. commande\n",
"\t\n",
"\tDate\n",
"\tMontant\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tPRODUIT\n",
"\tRéf. produit\n",
"\t\n",
"\tLibellé\n",
"\tPrix unitaire\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"CLIENT: Réf. client, Nom, Prénom, Adresse\n",
"PASSER, XX CLIENT, XX COMMANDE\n",
"COMMANDE: Num. commande, Date, Montant\n",
"INCLURE, -1N COMMANDE, -0N PRODUIT: Quantité\n",
"PRODUIT: Réf. produit, Libellé, Prix unitaire"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Nouveauté de la version 4.0.** Si la cardinalité comporte un et un seul `X`, l'autre caractère sera affiché tout seul."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Masquer un attribut"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vous pouvez mettre deux virgules consécutives pour réserver la place d'un attribut manquant. Les espaces insécables sont préservés, ce qui permet de réserver plus d'espace horizontal, cf. ci-dessous premier attribut vide de INCLURE."
]
},
{
"cell_type": "code",
"execution_count": 106,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tPASSER\n",
"\t\n",
"\t\n",
"\t\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tINCLURE\n",
"\t\tQuantité\n",
"\t\n",
"\t1,N\n",
"\t0,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCLIENT\n",
"\tRéf. client\n",
"\t\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCOMMANDE\n",
"\tDate\n",
"\tMontant\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tPRODUIT\n",
"\tRéf. produit\n",
"\t\n",
"\tLibellé\n",
"\tPrix unitaire\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%%mocodo\n",
"CLIENT: Réf. client,,, \n",
"PASSER, XX CLIENT, XX COMMANDE\n",
"COMMANDE: , Date, Montant\n",
"INCLURE, 1N COMMANDE, 0N PRODUIT: Quantité,,,,\n",
"PRODUIT: Réf. produit, Libellé, Prix unitaire"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Régression de la version 4.0.** Les espaces insécables ne sont plus préservés. Il n'y a donc plus d'autre moyen de réserver davantage d'espace horizontal que d'employer le style `blank` (paragraphe suivant)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Ne faire apparaître que le squelette du schéma conceptuel"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vous pouvez transformer en exercice à trous n'importe quel MCD en rendant complètement transparentes les couleurs des attributs, associations et cardinalités. Le style `blank` a été prédéfini à cet effet:"
]
},
{
"cell_type": "code",
"execution_count": 107,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tPasser\n",
"\t\n",
"\t0,N\n",
"\t1,1\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tInclure\n",
"\t\tQuantité\n",
"\t\n",
"\t1,N\n",
"\t0,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tClient\n",
"\tRéf. client\n",
"\t\n",
"\tNom\n",
"\tPrénom\n",
"\tAdresse\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCommande\n",
"\tNum. commande\n",
"\t\n",
"\tDate\n",
"\tMontant\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tProduit\n",
"\tRéf. produit\n",
"\t\n",
"\tLibellé\n",
"\tPrix unitaire\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp --colors=blank"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Attention, n'utilisez cette méthode que pour la projection : l'information textuelle est toujours présente, susceptible d'être sélectionnée et collée ailleurs. Vous pouvez bien sûr empêcher cette possibilité en convertissant le SVG en PNG, mais le plus simple est d'appliquer une réécriture `empty` :"
]
},
{
"cell_type": "code",
"execution_count": 108,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tPasser\n",
"\t\n",
"\t\n",
"\t\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tInclure\n",
"\t\n",
"\t\n",
"\t\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tClient\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCommande\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tProduit\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/markdown": [
"\n",
"
\n",
"\n",
"\n",
"\n",
"ccp.mcd\n",
"\n",
"\n",
"
\n"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"%%mocodo \n",
"Client: , , , \n",
"Passer, XX Client, XX Commande\n",
"Commande: , , \n",
"Inclure, XX Commande, XX Produit: \n",
"Produit: , ,\n"
]
}
],
"source": [
"%mocodo -i ccp -t empty # équivalent de \"delete:types,notes,attrs,cards\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Obfuscation d'un MCD donné"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Obfusquer un MCD consiste à vider celui-ci de sa sémantique de surface en substituant des chaînes aléatoires à tous les libellés. Cela permet de créer des exemples d'illustration de telle ou telle notion « pure » sans risquer de voir son public se focaliser sur des détails-métier (c'est l'équivalent de la [variable méta-syntaxique](https://fr.wikipedia.org/wiki/Variable_métasyntaxique) `foobar` dans le contexte de la pédagogie de la programmation)."
]
},
{
"cell_type": "code",
"execution_count": 109,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tLibellum\n",
"\t\n",
"\t0,N\n",
"\t1,1\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tMulieres\n",
"\t\tCarus\n",
"\t\n",
"\t1,N\n",
"\t0,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tScriptam\n",
"\tSpicula\n",
"\t\n",
"\tPersequitur\n",
"\tDefensor\n",
"\tFrontem\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tFuit\n",
"\tArtes\n",
"\t\n",
"\tLeporis\n",
"\tSociis\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tTheatro\n",
"\tReperiri\n",
"\t\n",
"\tSerpentes\n",
"\tAmissum\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp --seed=1 --select mcd -t obfuscate # raccourci pour \"obfuscate:labels\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"En argument, vous pouvez ajouter le chemin d'un fichier texte quelconque où puiser les mots de substitution. Par exemple, le texte du `README` de ce projet :"
]
},
{
"cell_type": "code",
"execution_count": 110,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tNotebook\n",
"\t\n",
"\t0,N\n",
"\t1,1\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tEspèce\n",
"\t\tTexte\n",
"\t\n",
"\t1,N\n",
"\t0,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tVersion\n",
"\tCardinalités\n",
"\t\n",
"\tDécomposition\n",
"\tRelationnelle\n",
"\tMaster\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tAffichée\n",
"\tBonus\n",
"\t\n",
"\tIncluant\n",
"\tSvg\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tSpécialisation\n",
"\tÉgalement\n",
"\t\n",
"\tDélégué\n",
"\tExportation\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t obfuscate:labels=../../README.md --seed=1 --select mcd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Mocodo essaie d'abord de trouver ce fichier à l'endroit indiqué. En cas d'échec, il le cherche (avec extension `.txt` facultative) parmi ceux distribués avec le logiciel, à savoir:\n",
"\n",
"- `\"lorem.txt\"` (6464 mots) : le [faux-texte](https://fr.wikipedia.org/wiki/Faux-texte) le plus courant, augmenté d'une sélection des 10000 mots latins les plus courants [compilés par Kyle P. Johnson](https://kyle-p-johnson.com/blog/2015/04/23/most-common-greek-latin-words.html), le tout privé de ses doublons et des mots de moins de 3 lettres (`\"lorem_ipsum.txt\"` avant la version 4.0).\n",
"- `\"fr.txt\"` (3396 mots) : une liste des 4000 mots français les plus courants, privée de ceux comportant une apostrophe ou moins de 4 lettres. Source : http://wortschatz.uni-leipzig.de/index.html _via_ [Wikitionary](https://fr.wiktionary.org/wiki/Wiktionnaire:Listes_de_fréquence). Nouveauté de la version 4.0.\n",
"- `\"fr5.txt\"` (464 mots): la liste de `\"fr.txt\"`, restreinte aux mots de 5 lettres. Nouveauté de la version 4.0.\n",
"- `\"en4.txt\"` (640 mots): une sélection (SFW) de mots anglais de quatre lettres (`\"four_letter_words.txt\"` avant la version 4.0).\n",
"- `\"disparition.txt\"` (7489 mots) : le lexique du [célèbre roman lipogrammatique](https://fr.wikipedia.org/wiki/La_Disparition_(roman)) de Georges Perec, privé des mots de moins de 4 lettres.\n",
"\n",
"En cas de nouvel échec, il se rabat sur `\"lorem.txt\"`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**NB.** L'algorithme s'assure que la [distance de Damerau-Levenshtein](https://fr.wikipedia.org/wiki/Distance_de_Damerau-Levenshtein) entre deux libellés de substitution quelconques est d'au moins 3. En clair, cela signifie que, si vous donnez en examen un exercice de conversion en relationnel basé sur un tel MCD, les erreurs de transcription d'un étudiant stressé, inattentif, illettré, dyslexique, roublard, ou tout cela à la fois, ne devraient pas vous empêcher de lui octroyer les points qui lui reviennent."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Croissance stochastique"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Vous pouvez créer un MCD partiellement aléatoire à partir d'un MCD donné en lui ajoutant un nombre `n` d'associations (avec les entités nécessaires) :"
]
},
{
"cell_type": "code",
"execution_count": 111,
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tTernaire 9\n",
"\t\n",
"\t0,N\n",
"\t1,1\n",
"\t1,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tTernaire 7\n",
"\t\n",
"\t1,1\n",
"\t0,N\n",
"\t0,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tPasser\n",
"\t\n",
"\t0,N\n",
"\t1,1\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tInclure\n",
"\t\tQuantité\n",
"\t\n",
"\t1,N\n",
"\t0,N\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tRéflexive 11\n",
"\t\tat 11 1\n",
"\t\n",
"\t1,N\n",
"\t1,1\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\tRéflexive 10\n",
"\t\tat 10 1\n",
"\t\n",
"\t1,1\n",
"\t0,1\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tEntité 8\n",
"\tid 8 1\n",
"\t\n",
"\tat 8 2\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tEntité 6\n",
"\tid 6 1\n",
"\t\n",
"\tat 6 2\n",
"\tat 6 3\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tCommande\n",
"\tNum. commande\n",
"\t\n",
"\tDate\n",
"\tMontant\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tClient\n",
"\tRéf. client\n",
"\t\n",
"\tNom\n",
"\tPrénom\n",
"\tAdresse\n",
"\n",
"\n",
"\n",
"\n",
"\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\t\n",
"\t\n",
"\tProduit\n",
"\tRéf. produit\n",
"\t\n",
"\tLibellé\n",
"\tPrix unitaire\n",
"\n",
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"%mocodo -i ccp -t grow:n=4 arrange --seed=1 --select mcd"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"- Des sous-options pré-définies (données ci-dessous avec leur valeur par défaut après le `=`) permettent de spécifier finement le nombre désiré :\n",
" - d'associations réflexives (`arity_1=2`) ;\n",
" - d'associations ternaires (`arity_3=2`) ;\n",
" - d'associations quaternaires (`arity_4=0`) ;\n",
" - d'associations doubles, i.e., associant le même couple d'entités (`doubles=1`) ;\n",
" - d'identifiants composites (`composites=1`) ;\n",
" - d'attributs maximum par entité (`ent_attrs=4`) ;\n",
" - d'attributs maximum par association (`assoc_attrs=2`).\n",
"\n",
"- On ne peut pas préciser directement le nombre d'associations binaires : si le nombre total des autres associations spécifiées n'arrivent pas à `n`, elles viennent en complément.\n",
"\n",
"- Des sous-options de forme plus ou moins libre décrivent les cardinalités. Par exemple, `_11-*N=2` créera deux entités faibles et `/*N-*N` un agrégat. Les associations de complément sont `*N-*N`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Avec la sous-option `from_scratch`, le MCD de départ est vide. À titre d'exemple, voici la transformation complexe invoquée par Mocodo online pour créer un MCD d'entraînement à la conversion au relationnel, accompagné de cette dernière. Notez la création de rôles par défaut : ils permettent de simuler le rétablissement de la sémantique des associations disparues."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%mocodo --seed=2 --mld -t grow:from_scratch,arity_3=1,_11-*N=1 obfuscate create:roles lower:roles arrange"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Il n'est pas impossible que le MCD résultant soit incorrect (p. ex., apparition d'une identification relative circulaire), mais les contrôles effectués _a priori_ et _a posteriori_ devraient dans la majorité des cas produire quelque chose de raisonnable."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Génération d'un QR code"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Impressionnez votre public en accompagnant l'option `-t share` de `--defer` :"
]
},
{
"cell_type": "code",
"execution_count": 112,
"metadata": {},
"outputs": [
{
"data": {
"text/markdown": [
"\n",
"