MishkaDocument.Dev.Seeds (Mishka Document v0.0.1)

Copy Markdown View Source

Dev seeds for MishkaDocument.

Models a realistic documentation lifecycle for five MishkaCMS products: Mishka CMS, Mishka Gervaz, Mishka Installer, GuardedStruct and Mishka Chelekom. Each product has an English canonical workspace — the master — plus one translated workspace per entry in its :translations list, linked to the master via language_group_id. Persian, French and Arabic editions carry translated names, descriptions and section titles; note bodies outside Persian reuse the English copy.

Every product aims at a different completion band, so the admin card grid renders each colour: Mishka CMS 100 (blue), Mishka Gervaz 90 (green), Mishka Installer 65 (indigo), GuardedStruct 40 (amber) and Mishka Chelekom 15 (red).

Each English workspace progresses through six versions (0.0.1 → 0.0.6) using the version system — not separate workspaces; translated workspaces get three (0.0.1 → 0.0.3). Later versions clone sections from earlier ones (source_section_id + shared section_lineage_id) and record the clone relationship in WorkspaceVersionLineage. A translated section joins the language group of the English section holding the same position in the version of the same number, so read :language_variants has data at the section level too.

Drafts live only on the latest stable English version (0.0.6), covering a new chapter, an edit and a rewrite; a third of them reach applied_at, and two note drafts hang off a sibling section draft rather than a live section. Twenty issues are spread over sections and notes — a quarter of them on a translated workspace — with long-tailed comment threads, the longest on the ones that end up resolved or closed. When a master is among the users, about one comment in five is theirs, so a tenant admin sees comments it can read but not edit or delete.

Views, bookmarks and follows are seeded so the reader-facing counters are not uniformly zero; those three carry their real creation time rather than a backdated one.

Review status

A section, note or draft carries two independent review axes, and completion is the share of a workspace's items where both are approved. One stable hash per item decides full approval at the product's target rate, so the 100/90/65/40/15 bands land exactly; a second hash gives every other item a divergent pair — content approved while technical is still pending is the ordinary mid-review state — and sends a fifth of them to :rejected or :needs_revision so the Sections filters and the draft :draft_applied column have rows in every branch. :archived is never written here: the destroy action force-sets both fields when a section or note is soft deleted.

Archived vs. archived

archived_at (AshArchival soft delete, what the admin Archived tab filters on) and a status of :archived are separate things. The seed only ever reaches the first one through Ash.destroy!/2 on the archival :destroy action, which force-sets status/content_review_status/ technical_review_status to :archived by itself on the resources that have such a field. No workspace is created carrying the :archived label alone — MishkaDocumentWeb.Templates.DocumentCard.archived?/1 matches the status before it looks at archived_at, so such a row would render as archived while still sitting in the Active tab. MishkaDocument.WorkspaceVersion is the one place where the label is the whole story: it has no archived_at at all, and versions 0.0.1 → 0.0.3 keep status: :archived.

Archiving runs last, after every create, status transition and timestamp backdate. prevent_archived_*_updates raises on any update of a row that is already archived, and no resource declares archive_related, so children are archived before parents and language masters are never archived while a live translation still points at them.

Summary

Functions

Creates realistic document seed data for a single site.

Stress seed: large volumes of document data for a site.

Functions

dev_seed(site_id, users, progress \\ fn _ -> :ok end)

@spec dev_seed(String.t(), [struct()], (String.t() -> any())) :: :ok | :already_seeded

Creates realistic document seed data for a single site.

RUN AGAIN ON A SITE THAT HAS DOCUMENTS, this adds none of the products and answers :already_seeded. The five products would come back under the same names — unique_slug_per_site would refuse the second set outright — and everything hanging off them, versions, sections, notes, drafts and issues, is generated with nothing to recognise it by afterwards. So the thing that is either there or not is the documentation, and a site that has it keeps what its authors have done to it. The permission and robots examples are asked apart from it, each by its own slug, so a site seeded before they existed gets them on its next run.

stress_seed(site_id, users, opts \\ [])

Stress seed: large volumes of document data for a site.

Runs the same building blocks as dev_seed/3 at scale — language-paired tags, long-tailed note counts, per-record authors, commented issues and a closing archival pass — so a stress run exercises the code paths the admin pages actually render rather than a flat grid of identical rows.