AppGen: Application Generator Guide

SDCStudio's AppGen feature generates a standalone Django application from a published data model. The download is a complete project, with Docker configuration, that stores every record as a validated Governed Data Record. For an app of your own in TypeScript on Supabase, the model page also offers a Supabase kit. The applications and the kit are licensed under Apache 2.0.

Overview

The generated application includes:

  • A data entry wizard: a step-by-step form built from the model's components, with an exceptional value selector on every field.
  • A record model: one Django model per data model that stores each Governed Data Record as XML, with a JSON projection for queries.
  • Validation: every record is validated against the model's XSD with the Semantic Data Charter validator before it is stored; recoverable errors are tagged with exceptional values.
  • List, detail, download and delete views: records can be browsed, searched, downloaded as XML or JSON, and deleted.
  • Bulk import: XML files can be imported from a ZIP archive or a server directory.
  • A triplestore: records are extracted to RDF and loaded into Apache Fuseki (Lightweight) or GraphDB (Enterprise).
  • A REST API for uploading model files and validating records, documented in the Generated Application API Reference.
  • Docker Compose configuration for the whole stack.

Prerequisites

To use AppGen, the following conditions must be met:

  1. A published model whose package has been generated. The Generate App (choose one) card appears on the model page after Generate.
  2. 1,500 credits in your wallet for the first download of the model. Generating the build is free, and every later download of the same model is free.

How to Use

1. Create a Data Model

  1. Navigate to the Data Models section in SDCStudio.
  2. Create a new Data Model or open an existing one.
  3. Add components to define your data structure. The generator reads the model's cluster tree and turns each component into a field on the wizard's data entry step:

    Component type Wizard field
    XdString single-line text input, with the model's minimum length, maximum length and pattern
    XdToken single-line text input
    XdBoolean checkbox
    XdCount integer input, with the model's minimum and maximum
    XdQuantity decimal input, with the model's minimum and maximum
    XdFloat, XdDouble number input, with the model's minimum and maximum
    XdTemporal date input
    XdOrdinal select list
    XdLink URL input
    XdFile file input
    List types (XdStringList, XdTokenList, XdBooleanList, XdIntegerList, XdDecimalList, XdDoubleList, XdNonNegativeIntegerList, XdPositiveIntegerList) single-line text input
    Cluster a collapsible card that groups its components

    Every component field also gets an Exceptional Value selector, and components whose model allows them get inputs for valid time begin, valid time end, time recorded, modified, latitude and longitude. The form marks no field as required; the XSD decides what is required when the record is validated.

  4. Publish the model and click Generate so the package exists.

2. Generate the App

Generate App: Lightweight or Enterprise

  1. Open the published model's page and scroll to Generate App (choose one), below the Model Generation card.
  2. Pick Lightweight (PostgreSQL, Fuseki, Redis and Celery, Django's built-in auth; for development and small deployments) or Enterprise (adds GraphDB, SirixDB and Keycloak; needs 6 GB of free RAM) and click its Generate.
  3. Wait for the build; Retry is offered if it fails.
  4. Click Download App to fetch the ZIP. Each profile is built and stored separately.

Cost: 1,500 credits for the first Download App of a model, taken at download; every later download of that model by you, either profile or the Supabase kit, is free (a revision of the model is a new model). Since 4.16.0; before that every download was charged. Generating the build is free, and a failed build charges nothing.

From a script or an agent. POST /api/v1/dmgen/dms/<ct_id>/generate_app/ with {"profile": "lightweight", "async": true} (or "enterprise") answers 202 with a job to poll (see the assembly reference); GET .../download_app/ or .../download_enterprise_app/ downloads it and says whether this download was charged. The key needs Spend for a first download. sdcmodeler's generate_app tool does both and refuses until the person has agreed to the charge (see The SDC MCP Servers).

3. Run the Generated App

The Download App button after generation

The ZIP is named {app_name}_sdc_project.zip, where app_name is the model's title in lower case with spaces and hyphens replaced by underscores. It unpacks to sdc_project/sdc4/, which is the Django project.

You need Docker, or Podman with podman-compose. The steps below use docker compose; podman-compose takes the same arguments.

  1. Unpack and enter the project: bash unzip blood_pressure_sdc_project.zip cd sdc_project/sdc4

  2. Create the environment file: bash cp .env.example .env The file sets the host ports, the default passwords and SECRET_KEY, DEBUG and ALLOWED_HOSTS. The defaults are for local use.

  3. Start the stack: bash docker compose up -d --build On startup the web service runs makemigrations, migrate, create_default_superuser, init_fuseki (Lightweight) or init_graphdb (Enterprise), collectstatic, and then the development server. create_default_superuser creates the user admin with password admin123 when no user exists. init_fuseki waits for Fuseki, checks that the dataset configured in mediafiles/fuseki/configuration/ answers, and loads the reference model ontologies and the model's RDF schema; init_graphdb waits for GraphDB, creates the repository if it is missing, and loads the same files. Enterprise's first start takes a few minutes while GraphDB and Keycloak initialize.

  4. Open the application. The default host ports are:

    URL What
    http://localhost:18000/ home page listing every installed data model app
    http://localhost:18000/{app_name}/ the model's record list; New Instance starts the wizard at /{app_name}/wizard/
    http://localhost:18000/{app_name}/bulk-import/ bulk import, with Download XML Template
    http://localhost:18000/admin/ Django admin, including API Keys
    http://localhost:18000/admin/install-app/ the Install SDC4 App page, linked from the home page menu
    http://localhost:18000/docs/readme/ the project README rendered in the browser; also /docs/triplestore/ and /docs/license/
    http://localhost:18000/api/v1/ the REST API, mounted in every generated project; see the API reference
    http://localhost:13030/ Fuseki (Lightweight)
    http://localhost:17200/ GraphDB Workbench (Enterprise)
    http://localhost:18080/admin/ Keycloak admin console (Enterprise)
    https://localhost:19443/ SirixDB, self-signed certificate (Enterprise)

    PostgreSQL is published on 15432 and Redis on 16379. Every service uses admin / admin123 by default. Every page requires a login: the admin, and the data model pages (see Current Limitations).

  5. Create more admin users when needed: bash docker compose exec web python manage.py createsuperuser

  6. Stop the stack with docker compose down. Data stays in mediafiles/.

Generated Application Structure

sdc_project/
└── sdc4/
    ├── manage.py
    ├── Dockerfile
    ├── docker-compose.yml
    ├── requirements.txt
    ├── .env.example
    ├── .sdc-generator-version      SDCStudio and reference model versions that built the app
    ├── README.md
    ├── LICENSE
    ├── sdc4/                       project settings, urls, views, celery
    ├── {app_name}/                 the data model app
    │   ├── models.py               the {Model}Instance record model
    │   ├── admin.py
    │   ├── urls.py
    │   ├── views/                  wizard, list, detail, bulk_import
    │   ├── forms/                  setup, context, data_entry, review, bulk_import
    │   ├── utils/                  wizard_config, wizard_state, xml_builder,
    │   │                           xml_skeleton.xml, field_mapping.json,
    │   │                           json_extractor, rdf_extractor, bulk_import
    │   ├── templates/{app_name}/
    │   ├── templatetags/
    │   ├── migrations/
    │   ├── fields.py, widgets.py, signals.py, tasks.py, apps.py
    ├── api/                        REST API app
    ├── core/                       management commands, docs views, admin install page
    ├── generic_storage/            records for models with no installed app
    ├── sdc4_shared/utils/          validator, DM registry, app installer, triplestore client
    ├── docs/                       TRIPLESTORE_GUIDE.md
    ├── ontologies/                 sdc4.xsd, sdc4.ttl, sdc4-meta.ttl and the OWL files
    ├── mediafiles/
    │   ├── dmlib/                  catalog.xml, sdc4.xsd, dm-{ct_id}.* model files
    │   ├── postgres/
    │   └── fuseki/                 Lightweight; Enterprise has graphdb/, sirixdb/, keycloak/
    ├── import_data/                server-side directory for bulk import
    ├── static/vendor/
    ├── staticfiles/
    └── templates/                  home page, admin install page, docs viewer

mediafiles/dmlib/ holds every published file for the model (dm-{ct_id}.xsd, .xml, .json, .html, .ttl, _shacl.ttl and the others that exist), plus catalog.xml and a local copy of sdc4.xsd so validation resolves the reference model without network access. {app_name}/utils/xml_skeleton.xml is the model's published XML template; the wizard fills it in and prunes what is left empty. When the model was published with README-AI-PROMPT.md and SHACL_README.md, they are copied into docs/.

The project can host more than one data model app. A second model's ZIP is installed with python manage.py install_app <zip> or the Install SDC4 App page, followed by a restart; settings.py and urls.py discover installed apps automatically.

The Supabase Kit

For a TypeScript app on Supabase, the model page offers a third box beside Lightweight and Enterprise: Supabase kit. Click Download Kit to fetch supabase-kit-dm-<ct_id>.zip, a new project's supabase/ directory. Nothing is built ahead: the kit is generated from the published XSD when you download it.

The kit holds:

  • The kit itself, under supabase/functions/_shared/sdc/: one TypeScript file per component, shared by every model that uses it; one file per model, with its record's type in a nested JSON projection; the runtime that builds and projects records; a README.md; and a LICENSE (Apache 2.0).
  • The gate, an Edge Function (supabase/functions/gate/) and the one way a record is stored. It builds the XML Governed Data Record from the JSON it is sent, validates it against the published model, recovers what an Exceptional Value can cover, and stores it.
  • Migrations: a base migration once per project (the model registry and the records table, whose write grants are revoked from every role but the gate's), and one per model with its published XSD and a view of its records that obeys the table's row-level security.

Apply it with the Supabase CLI: supabase db push, then supabase functions deploy gate. The app writes by POSTing a record's JSON (or its XML) to the gate and reads JSON from the model's view, public.dm_<ct_id>. The kit's README gives the gate's answers and the read policy to replace with your own.

Several Models in One Project

A project holds as many models as you need. Add each further model's kit to the same project with sdcmodeler, from the command line or as an MCP tool:

sdcmodeler kit <ct_id> --project .

It writes the model's file and only the components the project does not have yet, so a component two models share is one file that both use. It adds one migration numbered after the project's latest, rewrites the model index, and never rewrites a migration. If a component file in the project differs from the published one, it refuses before writing anything. Push the migration and deploy the gate again afterwards. The same files are served at GET /api/v1/dmgen/dms/<ct_id>/supabase_kit/ for a client of your own.

Use the ZIP for a project's first model only: it includes the base migration, which a project applies once.

Current Limitations

  • One charge per model, whichever you download. The Lightweight and Enterprise builds are stored separately and the Supabase kit is generated on request; the first download of the model, of any of the three, is charged and every later one is free.
  • No edit view. The wizard creates records and the detail page deletes them. There is no route for changing a stored record, and the admin shows records read-only.
  • List components render as text. The list types have no dedicated widget; they appear as a single-line text input.
  • Sign-in required. Every data model page (wizard, list, detail, downloads, delete) requires a signed-in user, created in the admin; bulk import is for staff users. The Lightweight profile signs in at /accounts/login/ with Django's accounts; the Enterprise profile through Keycloak.
  • Development defaults. Every service starts with admin / admin123, Django runs with DEBUG=True, and Enterprise starts Keycloak in development mode. The README's Production Deployment section lists what to change.
  • Bulk import keeps invalid files. Files that fail validation are still stored, marked invalid.

Troubleshooting

  • The build fails. The card shows the server's message and a Retry button. Nothing is charged for a failed build. The server refuses to build when the model is not published or its package has not been generated.
  • Download App returns 402. This is the model's first download and the wallet has fewer than 1,500 credits (a later download is free and never answers 402). The message reads Insufficient credits. This needs 1,500 credits; you have N. Add funds and try again; see Wallet and Billing.
  • Enterprise services do not start. The Enterprise stack needs at least 6 GB of memory allocated to Docker or Podman. Check docker compose logs graphdb and docker compose logs keycloak. If Keycloak fails on its database, bring up postgres first and then keycloak.
  • Port already allocated. The compose file publishes 18000, 15432, 16379 and 13030 (Lightweight) or 17200, 19443 and 18080 (Enterprise). Change WEB_PORT, DB_PORT, REDIS_PORT, FUSEKI_PORT, GRAPHDB_PORT, SIRIX_PORT or KEYCLOAK_PORT in .env.
  • Container name conflicts when two generated projects run at once: set a different APP_NAME in each project's .env.
  • Permission denied on a volume at first start. Fuseki runs as UID 1000 and PostgreSQL as UID 999: sudo chown -R 1000:1000 ./mediafiles/fuseki and sudo chown -R 999:999 ./mediafiles/postgres. Under rootless Podman use podman unshare chown instead. Enterprise uses mediafiles/graphdb and mediafiles/sirixdb (UID 1000).
  • The triplestore has no data. The init command gives up if the triplestore is not ready in time. Run it again: docker compose exec web python manage.py init_fuseki or init_graphdb.
  • Code changes are not showing. Rebuild the image: docker compose down then docker compose up -d --build.