Aller au contenu

Modèle de données

Les tables fraza_projects_* du module Projets, ses migrations, les votes, les abonnés, les désabonnements, et les images des descriptions dans le stockage.

Toutes les tables sont préfixées fraza_projects_. Un compte est désigné par l'identifiant du compte du core, en texte, sans clé étrangère : un compte supprimé laisse son identifiant, et l'admin l'affiche « Compte supprimé ».

Chaque fiche, colonne, étiquette et jalon appartient à un projet. Les slugs des fiches, les clés des colonnes et les noms des étiquettes sont uniques dans leur projet.

fraza_projects_project

Un projet, avec son tableau, ses colonnes, ses étiquettes et ses jalons.

ColonneContenu
slugSon adresse (/za/projects/<slug>, /admin/projects/<slug>), fixée à la création, unique. Jamais nouveau, suivi, mes-fiches, desabonnement ni journal.
nameSon nom, 60 caractères au plus.
descriptionEn Markdown, 20 000 caractères au plus.
templateLe modèle de ses colonnes de départ : roadmap, development ou support.
positionSon rang dans la liste.
is_publicSes pages publiques existent.
is_featuredLe projet mis en avant : celui que lit un site qui n'en nomme aucun. Un seul au plus, garanti par un index unique. Le module en garde toujours un parmi les projets actifs, tant qu'il en existe.
archived_atLe moment de son archivage, ou NULL pour un projet actif. Un projet archivé n'a plus de pages publiques ni de mise en avant.
created_atSa création.

createTables() crée le projet « Principal » (principal), mis en avant, avec les colonnes du modèle Feuille de route, seulement quand il crée la table fraza_projects_project. Un site qui a supprimé tous ses projets ne le retrouve pas à un fraza-db init suivant. Un projet ne se supprime que sans fiche : project_id est non nul, sans suppression en cascade, dans fraza_projects_item, _column et _tag. Ses jalons, eux, partent avec lui.

fraza_projects_column

Les colonnes d'un projet : project_id, key (tirée du nom à la création, lue par les sites, unique dans son projet), name, position, et leurs réglages is_public, is_default (une seule colonne par projet, garantie par un index unique), is_done, announce, announce_subject, announce_message. Une colonne qui contient des fiches ne se supprime pas.

fraza_projects_tag

Les étiquettes d'un projet : project_id, name, unique dans son projet sans tenir compte de la casse, et position. Supprimer une étiquette la retire des fiches (ON DELETE SET NULL).

fraza_projects_milestone

Les jalons d'un projet.

ColonneContenu
project_idSon projet. Supprimé avec lui.
nameSon nom, 80 caractères au plus.
descriptionEn texte simple, 2000 caractères au plus. Reprise en tête des notes de version.
due_dateSon échéance, une date sans heure, ou NULL.
positionSon rang, après l'échéance, dans la liste.
closed_atLe moment de sa fermeture, ou NULL pour un jalon ouvert.

L'avancement d'un jalon n'est pas stocké : il se compte à la lecture, sur les fiches qui le nomment et celles d'entre elles dans une colonne terminée. Supprimer un jalon le retire des fiches (ON DELETE SET NULL).

fraza_projects_item

Une fiche.

ColonneContenu
project_idSon projet.
slugSon adresse publique, fixée à la création, unique dans son projet. Jamais nouveau, suivi, mes-fiches, desabonnement ni journal. Un déplacement vers un projet où il est pris ajoute -2, -3…
title, descriptionCe que montre la page publique. La description est en Markdown, 20 000 caractères au plus.
column_idSa colonne, du même projet.
is_publicVisible sur la page publique, quand sa colonne et son projet le sont aussi, et que le projet n'est pas archivé.
tag_idSon étiquette, du même projet.
milestone_idSon jalon, du même projet, ou NULL.
kindSon type : task, bug, idea ou improvement. task par défaut.
prioritySa priorité : low, normal, high ou urgent. normal par défaut.
due_dateSon échéance, une date sans heure, ou NULL. Dépassée hors d'une colonne terminée, la fiche est en retard.
task_total, task_doneLes sous-tâches de la description (- [ ], - [x]), comptées à chaque enregistrement de la description : une carte affiche « 3/5 » sans relire le texte.
shipped_noteLa note de livraison, 200 caractères au plus.
vote_countLe nombre de votes, tenu à jour dans la même transaction que chaque vote : la feuille de route se trie sans compter. Une fusion et le retrait d'un vote par l'équipe le recomptent.
positionSa place dans sa colonne du tableau, de 1 en haut, posée par l'équipe. NULL pour une fiche qui vient d'entrer dans sa colonne : elle se range après les fiches placées, par votes. Remis à NULL à chaque changement de colonne ou de projet. Index (column_id, position). La feuille de route publique ne le lit pas.
created_byLe compte de l'équipe qui l'a créée, ou NULL.
status_changed_atLe dernier changement de colonne ou de projet. Sert à l'ordre d'une colonne terminée et des notes de version.
created_at, updated_atSa création et sa dernière modification, responsables compris.

fraza_projects_item_assignee

Les responsables d'une fiche : item_id, user_id, assigned_at. Une ligne par compte et par fiche, dix au plus par fiche. Supprimées avec leur fiche.

fraza_projects_item_event

L'historique d'une fiche, écrit dans la transaction de chaque changement : item_id, kind, actor_id (le compte de l'équipe qui a fait le changement), data en JSON, created_at. Supprimés avec leur fiche.

kinddata
created{ column }, le nom de la colonne
column{ from, to }, les noms des colonnes
project{ from, to }, les noms des projets
assigned{ added, removed }, les identifiants des comptes
due{ from, to }, des dates AAAA-MM-JJ ou null
priority{ from, to }
kind{ from, to }
milestone{ from, to }, les noms des jalons, ou null
visibility{ public }
title{ from, to }
merged{ from, votes, updates }, le titre de la fiche fusionnée dans celle-ci, les votes et les mises à jour repris
vote_removed{ removed }, l'identifiant du compte dont l'équipe a retiré le vote

Les noms sont copiés au moment du changement : renommer une colonne ou un jalon ne réécrit pas l'historique.

fraza_projects_item_update

Les mises à jour d'une fiche : item_id, kind (public ou internal), body en Markdown (20 000 caractères au plus), author_id (le compte de l'équipe), created_at, updated_at. Supprimées avec leur fiche.

fraza_projects_vote

Un vote : item_id, et soit user_id (un compte), soit voter_hash (l'empreinte SHA-256 du cookie d'un navigateur), jamais les deux, et created_at. Un seul vote par votant et par fiche, garanti par deux index uniques partiels. Supprimés avec leur fiche. Une fusion copie les votes du doublon vers l'autre fiche : la copie d'un votant qui avait voté pour les deux est écartée par ces index.

fraza_projects_follower

Un compte qui suit une fiche sans forcément voter : item_id, user_id, created_at. Clé primaire (item_id, user_id), index sur user_id. Supprimés avec leur fiche. Ajoutée par la migration 002-followers-and-optouts.

fraza_projects_email_optout

Une adresse qui ne veut plus les courriels du module : email_hash, l'empreinte SHA-256 en hexadécimal de l'adresse en minuscules et sans espaces autour, clé primaire, et created_at. L'adresse elle-même n'est jamais gardée. Une ligne disparaît quand le compte de cette adresse suit une fiche. Ajoutée par la migration 002-followers-and-optouts. Voir Se désabonner.

Les images des descriptions

Rangées dans le stockage sous projects/images/, avec un nom tiré au hasard, et servies à tous par GET /za/api/projects/public/images/<nom>. Cette route ne sert que ce dossier, et seulement un nom de la forme que le module écrit. Aucune table ne suit les images : le texte des descriptions et des mises à jour dit lesquelles servent.

fraza_projects_storage_deletion

La file des objets à retirer du stockage. Chaque heure, la tâche projects-images y inscrit les images du dossier qu'aucun texte ne nomme et qui y sont depuis plus d'une journée, puis vide la file. Avant, elle retire de la file une image qu'un texte nomme de nouveau, recollée par exemple : elle reste dans le stockage. Un objet que le stockage refuse reste dans la file avec son erreur, et est retenté plus tard, avec un délai qui double jusqu'à une journée.

dropTables() supprime les tables, pas les images du stockage.

Migrations

createTables() crée le schéma courant, et fraza-db init pose une ligne 000-baseline après lui. fraza-db migrate applique ensuite les migrations dans l'ordre, chacune une fois. Sur une base neuve, elles trouvent leur changement déjà fait par createTables() et passent sans rien toucher. Voir Écrire une migration.

MigrationEffet
001-item-positionAjoute position à fraza_projects_item, à NULL pour toutes les fiches, et son index. Chaque colonne garde son ordre par votes tant que l'équipe n'y place aucune carte.
002-followers-and-optoutsCrée fraza_projects_follower et fraza_projects_email_optout.
003-reserve-new-segmentsRenomme une fiche dont le slug est journal ou desabonnement en <slug>-fiche, ou <slug>-fiche-<id> quand ce slug est déjà pris dans son projet, et un projet desabonnement en desabonnement-projet, ou desabonnement-projet-<id>. Ces adresses publiques changent : les pages du journal et du désabonnement les prennent.

Une base déployée avant ces migrations les reçoit à son prochain npx fraza-db migrate.

Sur cette page