Python core - fxq¶
Reference for the backend core, generated from docstrings and type hints. This
is the implementation behind the HTTP API; most integrators should
prefer the SDK or the HTTP API rather than importing fxq directly.
Application¶
app
¶
FastAPI application factory.
create_app is the single entry point used by the dev server and the test
suite. It wires together the database engine, session factory, media storage,
all routers, and optional static SPA serving.
Functions:
| Name | Description |
|---|---|
create_app |
Construct and return the fxquinox FastAPI application. |
Classes¶
Functions:¶
create_app
¶
create_app(database_url: str | None = None, seed: bool = False, secret: str | None = None, cors_origins: list[str] | None = None, media_root: str | None = None, static_dir: str | None = None) -> FastAPI
Construct and return the fxquinox FastAPI application.
Creates the SQLAlchemy engine, applies the built-in schema seed (when
seed=True), registers all routers, and optionally mounts a built SPA
from static_dir (or the FXQ_STATIC_DIR env var).
The /healthz endpoint is always available without auth.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database_url
|
str | None
|
SQLAlchemy connection string. Falls back to
|
None
|
seed
|
bool
|
If |
False
|
secret
|
str | None
|
HMAC-SHA-256 secret for JWT signing. |
None
|
cors_origins
|
list[str] | None
|
List of allowed CORS origins. Defaults to the
local Vite dev server ( |
None
|
media_root
|
str | None
|
Path for local media storage. Falls back to
|
None
|
static_dir
|
str | None
|
Directory containing the built SPA (optional). |
None
|
Returns:
| Type | Description |
|---|---|
FastAPI
|
A configured |
Examples:
Configuration¶
config
¶
Application configuration loaded from environment variables or a .env file.
All variables are prefixed with FXQ_ (e.g. FXQ_DATABASE_URL).
Defaults are suitable for local development with SQLite; override for
production.
Classes:
| Name | Description |
|---|---|
Settings |
Pydantic settings model for fxquinox. |
Functions:
| Name | Description |
|---|---|
get_settings |
Return a fresh |
Classes¶
Settings
¶
Bases: BaseSettings
Pydantic settings model for fxquinox.
Reads FXQ_* environment variables and a .env file (if present).
Unknown variables are silently ignored (extra="ignore").
Attributes:
| Name | Type | Description |
|---|---|---|
database_url |
str
|
SQLAlchemy connection string. Defaults to a local
SQLite file ( |
redis_url |
str
|
Redis connection string (reserved for future use). |
s3_endpoint_url |
str | None
|
Custom S3-compatible endpoint, or |
s3_bucket |
str
|
Bucket name for object storage. |
media_root |
str
|
Filesystem path for the local media store. |
debug |
bool
|
Enable verbose debug output. |
Examples:
>>> s = Settings(database_url="sqlite+pysqlite:///:memory:")
>>> s.database_url
'sqlite+pysqlite:///:memory:'
Functions:¶
get_settings
¶
get_settings() -> Settings
Return a fresh Settings instance resolved from the environment.
Called at startup and in tests; cheap to construct since pydantic-settings caches nothing — callers that need a singleton should cache the result.
Returns:
| Type | Description |
|---|---|
Settings
|
A fully-resolved |
Examples:
Database¶
db
¶
SQLAlchemy engine and session-factory helpers.
Base is the shared declarative base inherited by every ORM model.
make_engine and make_session_factory are called once at application
startup by create_app; tests create their own in-memory instances.
Classes:
| Name | Description |
|---|---|
Base |
Shared SQLAlchemy declarative base for all fxquinox ORM models. |
Functions:
| Name | Description |
|---|---|
make_engine |
Create a SQLAlchemy engine with sensible defaults for the dialect. |
make_session_factory |
Create a |
Classes¶
Base
¶
Bases: DeclarativeBase
Shared SQLAlchemy declarative base for all fxquinox ORM models.
Functions:¶
make_engine
¶
make_engine(database_url: str) -> Engine
Create a SQLAlchemy engine with sensible defaults for the dialect.
SQLite gets check_same_thread=False so FastAPI's threadpool workers
can share the connection. In-memory SQLite additionally uses
StaticPool so all threads see the same database (needed for tests and
the dev server when database_url is ":memory:").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
database_url
|
str
|
A SQLAlchemy-style connection string, e.g.
|
required |
Returns:
| Type | Description |
|---|---|
Engine
|
A configured |
Examples:
make_session_factory
¶
Create a sessionmaker bound to the given engine.
autoflush=False and expire_on_commit=False are deliberate: the
API layer controls flush/commit explicitly, and we do not want SQLAlchemy
to lazily re-fetch objects after a commit (they are serialised to JSON
immediately).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
engine
|
Engine
|
A connected SQLAlchemy |
required |
Returns:
| Type | Description |
|---|---|
sessionmaker
|
A |
Examples:
Schema engine¶
types
¶
Immutable in-memory schema representation built from the database.
These frozen dataclasses are populated once per request by
schema.loader.load_schema and passed to the query compiler. They are
read-only views — mutations go through services.schema_admin.
Classes:
| Name | Description |
|---|---|
FieldDefn |
Immutable snapshot of one field's definition. |
EntityDefn |
Immutable snapshot of one entity type's definition. |
Schema |
Immutable snapshot of all entity type definitions for one request. |
Attributes:
| Name | Type | Description |
|---|---|---|
LINK_TYPES |
|
Attributes¶
Classes¶
FieldDefn
dataclass
¶
FieldDefn(code: str, display_name: str, data_type: str, config: dict = dict(), is_required: bool = False, inherit: bool = False)
Immutable snapshot of one field's definition.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
str
|
Field identifier (e.g. |
display_name |
str
|
Human-readable label. |
data_type |
str
|
Storage/widget type (e.g. |
config |
dict
|
Type-specific configuration dict (link types, enum options, …). |
is_required |
bool
|
Whether the field must have a value. |
inherit |
bool
|
Whether the value is inherited from the parent entity. |
EntityDefn
dataclass
¶
EntityDefn(code: str, display_name: str, hierarchy_mode: str = 'none', parent_field: str | None = None, fields: dict[str, FieldDefn] = field(default_factory=dict))
Immutable snapshot of one entity type's definition.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
str
|
Unique entity code (e.g. |
display_name |
str
|
Human-readable name. |
hierarchy_mode |
str
|
|
parent_field |
str | None
|
Field code pointing to the parent entity, or |
fields |
dict[str, FieldDefn]
|
Dict of field code → |
Methods:
| Name | Description |
|---|---|
field |
Return the named field definition. |
has_field |
Return |
Attributes¶
fields
class-attribute
instance-attribute
¶
Methods:¶
Schema
dataclass
¶
Schema(entities: dict[str, EntityDefn] = dict())
Immutable snapshot of all entity type definitions for one request.
Built by schema.loader.load_schema from the database; passed to the
query compiler and any service that needs to inspect field types.
Attributes:
| Name | Type | Description |
|---|---|---|
entities |
dict[str, EntityDefn]
|
Dict of entity code → |
Methods:
| Name | Description |
|---|---|
entity |
Return the definition for the named entity type. |
has_entity |
Return |
Attributes¶
entities
class-attribute
instance-attribute
¶
entities: dict[str, EntityDefn] = field(default_factory=dict)
Methods:¶
entity
¶
entity(code: str) -> EntityDefn
Return the definition for the named entity type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
Entity type code (e.g. |
required |
Returns:
| Type | Description |
|---|---|
EntityDefn
|
The corresponding |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the entity type is not in the schema. |
Examples:
loader
¶
Load the current schema from the database into immutable dataclasses.
load_schema is called on every query request so the in-memory view is
always up to date with any runtime schema changes. The result is a
lightweight, frozen Schema object safe to pass across the call stack.
Functions:
| Name | Description |
|---|---|
load_schema |
Build a |
Classes¶
Functions:¶
load_schema
¶
load_schema(session: Session) -> Schema
Build a Schema snapshot from all EntityType rows in the DB.
Reads every EntityType and its associated FieldDef rows (via the
eagerly loaded relationship) and converts them to frozen dataclasses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
An open SQLAlchemy session. |
required |
Returns:
| Type | Description |
|---|---|
Schema
|
A |
Examples:
Query DSL¶
dsl
¶
Filter DSL — a JSON-serialisable query language for the entity store.
Filters are nested Python lists that mirror a simple expression tree:
- A leaf condition:
[path, op, value]e.g.["status", "is", "inProgress"] - A logical group:
["and", child1, child2, ...]or["or", ...]
The path string supports dot-notation traversal through link fields:
"entity.Shot.sequence" joins the linked Shot's sequence.
parse_filters converts one of these lists into a Condition or
Group dataclass tree that the compiler can turn into a SQLAlchemy clause.
Classes:
| Name | Description |
|---|---|
DslError |
Raised when a filter list is structurally invalid. |
Condition |
A single leaf predicate. |
Group |
A logical conjunction or disjunction of child nodes. |
Functions:
| Name | Description |
|---|---|
parse_filters |
Parse a filter list into a |
Attributes:
| Name | Type | Description |
|---|---|---|
OPERATORS |
|
Attributes¶
OPERATORS
module-attribute
¶
OPERATORS = {'is', 'is_not', 'in', 'not_in', 'contains', 'starts_with', 'ends_with', 'gte', 'lte', 'gt', 'lt', 'between', 'is_null'}
Classes¶
DslError
¶
Condition
dataclass
¶
A single leaf predicate.
Attributes:
| Name | Type | Description |
|---|---|---|
path |
list[str]
|
The field path split on |
op |
str
|
One of the strings in |
value |
object
|
The right-hand side of the comparison. |
Group
dataclass
¶
Functions:¶
parse_filters
¶
Parse a filter list into a Condition or Group tree.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node
|
list
|
A non-empty list in DSL form. Either a logical group
|
required |
Returns:
| Type | Description |
|---|---|
Condition | Group
|
A |
Raises:
| Type | Description |
|---|---|
DslError
|
If the structure is invalid (wrong type, unknown operator, empty group, etc.). |
Examples:
>>> parse_filters(["status", "is", "inProgress"])
Condition(path=['status'], op='is', value='inProgress')
>>> parse_filters(["and", ["status", "is", "inProgress"], ["step", "is", "anim"]])
Group(op='and', children=[Condition(path=['status'], op='is', value='inProgress'), Condition(path=['step'], op='is', value='anim')])
>>> parse_filters(["status", "is_null"])
Condition(path=['status'], op='is_null', value=None)
compiler
¶
SQL compiler for the filter DSL.
Translates a Condition / Group tree produced by query.dsl.parse_filters
into a SQLAlchemy select statement against the Record table. Link-field
traversal (entity.Shot.sequence) is handled by joining aliased Record
rows at compile time.
Functions:
| Name | Description |
|---|---|
build_select |
Build a SQLAlchemy |
run_query |
Execute a DSL query and return matching |
Classes¶
Functions:¶
build_select
¶
build_select(schema: Schema, entity_code: str, filters: Condition | Group | None = None, sort: list[tuple[str, str]] | None = None, limit: int | None = None, offset: int | None = None)
Build a SQLAlchemy select for active records of one entity type.
Applies optional filters (via the DSL compiler), sort columns, and
pagination. The base WHERE always requires active = True and
entity_type = entity_code.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
Schema
|
The loaded schema used to resolve field data types. |
required |
entity_code
|
str
|
The entity type to query (e.g. |
required |
filters
|
Condition | Group | None
|
Optional parsed filter tree from |
None
|
sort
|
list[tuple[str, str]] | None
|
List of |
None
|
limit
|
int | None
|
Maximum number of rows to return. |
None
|
offset
|
int | None
|
Number of rows to skip (for pagination). |
None
|
Returns:
| Type | Description |
|---|---|
|
A SQLAlchemy |
Examples:
run_query
¶
run_query(session: Session, schema: Schema, entity_code: str, filters: Condition | Group | None = None, sort: list[tuple[str, str]] | None = None, limit: int | None = None, offset: int | None = None) -> list[Record]
Execute a DSL query and return matching Record objects.
Convenience wrapper that calls build_select and executes it against
the given session.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
An open SQLAlchemy session. |
required |
schema
|
Schema
|
The loaded schema. |
required |
entity_code
|
str
|
Entity type to query. |
required |
filters
|
Condition | Group | None
|
Optional parsed filter tree. |
None
|
sort
|
list[tuple[str, str]] | None
|
Optional sort specification. |
None
|
limit
|
int | None
|
Optional row limit. |
None
|
offset
|
int | None
|
Optional row offset. |
None
|
Returns:
| Type | Description |
|---|---|
list[Record]
|
A list of |
Examples:
Publish chain¶
service
¶
Publish service: the Task → Product → Version → Representation chain.
A publish creates (or reuses) a Product for the named product on a task,
then appends a new Version with an auto-incremented version number, and
creates one Representation + FileLocation per supplied spec.
Functions:
| Name | Description |
|---|---|
publish |
Publish a new version of a product on a task. |
Classes¶
Functions:¶
publish
¶
publish(session: Session, *, task_id: str, product: str, product_type: str, representations: list[dict], author: str | None = None, comment: str | None = None, inputs: list[str] | None = None, source_workfile_id: str | None = None, frame_start: int | None = None, frame_end: int | None = None, fps: float | None = None, site_id: str | None = None) -> dict
Publish a new version of a product on a task.
Finds or creates the Product, increments the version number, creates
a Version record with status="waitingToStart", and creates a
Representation + FileLocation for each supplied spec.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
Open DB session. |
required |
task_id
|
str
|
UUID of the parent |
required |
product
|
str
|
Product name (e.g. |
required |
product_type
|
str
|
camelCase product-type code (e.g. |
required |
representations
|
list[dict]
|
List of representation specs, each a dict with
|
required |
author
|
str | None
|
Display name of the publishing artist. |
None
|
comment
|
str | None
|
Optional publish comment. |
None
|
inputs
|
list[str] | None
|
Optional list of input |
None
|
source_workfile_id
|
str | None
|
Optional UUID of the source |
None
|
frame_start
|
int | None
|
First frame number of the published range. |
None
|
frame_end
|
int | None
|
Last frame number. |
None
|
fps
|
float | None
|
Frames per second. |
None
|
site_id
|
str | None
|
Override the site for |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
A dict with |
dict
|
each containing |
Examples:
lineage
¶
Version lineage: upstream (inputs) and downstream (dependents) resolution.
Lineage is stored as Version.data["inputs"] — a list of {type, id}
link dicts. Upstream walks those links; downstream scans all versions for
any that list the target as an input.
Functions:
| Name | Description |
|---|---|
upstream |
Return the |
dependency_tree |
The full upstream dependency tree of a version (VAL-51). |
downstream |
Return all active |
Classes¶
Functions:¶
upstream
¶
Return the Version records that are direct inputs of version_id.
Preserves the order declared in data["inputs"].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
Open DB session. |
required |
version_id
|
str
|
UUID of the version to inspect. |
required |
Returns:
| Type | Description |
|---|---|
list[Record]
|
List of input |
list[Record]
|
empty list if the version does not exist or has no inputs. |
Examples:
dependency_tree
¶
The full upstream dependency tree of a version (VAL-51).
Each node carries enough to render a usable inspector — label, product,
task, status, author, resolved path — plus its recursive inputs. Cycles
are broken with a cycle flag; depth is capped at max_depth.
Returns None if the version does not exist.
downstream
¶
Return all active Version records that list version_id as an input.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session
|
Session
|
Open DB session. |
required |
version_id
|
str
|
UUID of the version to find dependents of. |
required |
Returns:
| Type | Description |
|---|---|
list[Record]
|
List of |
list[Record]
|
unspecified (table scan order). |
Examples:
paths
¶
Path template resolution for the publish pipeline.
Templates use Python str.format syntax with a {root} variable
automatically filled from the current site's roots dict. Example template::
"{root}/projects/{project_code}/{entity_type}/{entity_code}/{step}/v{version:03d}"
Functions:
| Name | Description |
|---|---|
current_platform |
Return the canonical platform name for the current machine. |
resolve |
Resolve a path template with the given context dict. |
resolve_for_site |
Resolve a path template using the site root for the current platform. |
Functions:¶
resolve
¶
Resolve a path template with the given context dict.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
template
|
str
|
A |
required |
context
|
dict
|
Keyword arguments for template substitution. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The resolved path string. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If the template references a variable not in |
Examples:
resolve_for_site
¶
resolve_for_site(template: str, context: dict, site_roots: dict, platform: str | None = None) -> str
Resolve a path template using the site root for the current platform.
Looks up the platform root in site_roots (falling back to the
"linux" entry) and injects it as {root} before resolving.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
template
|
str
|
A |
required |
context
|
dict
|
Additional template variables. |
required |
site_roots
|
dict
|
Dict of |
required |
platform
|
str | None
|
Override for the auto-detected platform. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The resolved filesystem path string. |
Examples:
Models¶
record
¶
The universal Record table — the single-table store for every entity.
Every fxquinox entity (Project, Shot, Asset, Task, User, Note, …) is a row
in this one table. Entity-specific fields live in the data JSON column
so new types require no schema migration.
Classes:
| Name | Description |
|---|---|
Record |
Universal entity row. |
Classes¶
Record
¶
Bases: Base
Universal entity row.
Every fxquinox entity maps to one Record. Structural metadata
(entity_type, project_id, status, active) is stored in
real columns for efficient filtering; all other entity-specific fields
live in the data JSON blob.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
UUID primary key. |
entity_type |
Mapped[str]
|
Canonical entity code, e.g. |
project_id |
Mapped[str | None]
|
Foreign-key-style link to a |
status |
Mapped[str | None]
|
Current status code, e.g. |
data |
Mapped[dict]
|
JSON dict holding all entity-specific fields. |
active |
Mapped[bool]
|
|
thumbnail_id |
Mapped[str | None]
|
Key of the thumbnail in the media store. |
created_by |
Mapped[str | None]
|
ID of the user (or API key) that created this record. |
created_at |
Mapped[datetime]
|
UTC creation timestamp. |
updated_at |
Mapped[datetime]
|
UTC last-modified timestamp, updated automatically. |
Attributes¶
id
class-attribute
instance-attribute
¶
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
entity_type
class-attribute
instance-attribute
¶
entity_type: Mapped[str] = mapped_column(String(64), index=True)
project_id
class-attribute
instance-attribute
¶
project_id: Mapped[str | None] = mapped_column(String(36), index=True)
status
class-attribute
instance-attribute
¶
status: Mapped[str | None] = mapped_column(String(64), index=True)
active
class-attribute
instance-attribute
¶
active: Mapped[bool] = mapped_column(Boolean, default=True)
thumbnail_id
class-attribute
instance-attribute
¶
thumbnail_id: Mapped[str | None] = mapped_column(String(36))
created_by
class-attribute
instance-attribute
¶
created_by: Mapped[str | None] = mapped_column(String(36))
meta
¶
Schema-metadata ORM models (entity types, field definitions, statuses, etc.).
These tables define the shape of the data in the record table. They
are populated at startup by schema.seed.seed_builtin_schema and can be
extended at runtime through services.schema_admin.
Classes:
| Name | Description |
|---|---|
EntityType |
A registered entity type (e.g. |
FieldDef |
A field definition for one field on an |
Status |
A configurable status option (e.g. |
Workflow |
A global status transition edge for one entity type. |
Step |
A configurable pipeline/department step (Layout, Anim, Comp, ...). |
Priority |
A configurable task priority level (None, Low, Medium, High, Urgent). |
Setting |
Singleton-ish key/value app settings (e.g. custom branding). |
Classes¶
EntityType
¶
Bases: Base
A registered entity type (e.g. Shot, Asset, Task).
Built-in types are seeded once and cannot be deleted. Custom types
created via the schema-admin API have is_builtin=False and may be
removed when no records reference them.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
UUID primary key. |
code |
Mapped[str]
|
Unique identifier used in the API and stored in |
display_name |
Mapped[str]
|
Human-readable label. |
is_builtin |
Mapped[bool]
|
|
icon |
Mapped[str | None]
|
Material Symbol icon key rendered in the sidebar / entity picker. |
hierarchy_mode |
Mapped[str]
|
|
parent_field |
Mapped[str | None]
|
Name of the |
fields |
Mapped[list[FieldDef]]
|
Ordered list of |
Attributes¶
id
class-attribute
instance-attribute
¶
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
code
class-attribute
instance-attribute
¶
code: Mapped[str] = mapped_column(String(64), unique=True, index=True)
display_name
class-attribute
instance-attribute
¶
display_name: Mapped[str] = mapped_column(String(128))
is_builtin
class-attribute
instance-attribute
¶
is_builtin: Mapped[bool] = mapped_column(Boolean, default=False)
hierarchy_mode
class-attribute
instance-attribute
¶
hierarchy_mode: Mapped[str] = mapped_column(String(16), default='none')
FieldDef
¶
Bases: Base
A field definition for one field on an EntityType.
The data_type drives how the value is stored in Record.data and
how the frontend renders the editor widget. config is a free-form
dict whose shape depends on data_type (e.g. {"link_types": [...]},
{"options": [...], "option_labels": {...}} for enums).
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
UUID primary key. |
entity_type_id |
Mapped[str]
|
FK to |
code |
Mapped[str]
|
Field identifier, unique within the entity type. |
display_name |
Mapped[str]
|
Human-readable column label. |
data_type |
Mapped[str]
|
One of the types in |
config |
Mapped[dict]
|
Type-specific configuration dict. |
is_builtin |
Mapped[bool]
|
|
is_required |
Mapped[bool]
|
Whether the field must have a value. |
sort_order |
Mapped[int]
|
Determines column order in the UI (definition order for built-ins; appended for custom fields). |
default |
Mapped[dict | None]
|
Optional default value stored as a JSON scalar/object. |
inherit |
Mapped[bool]
|
Whether the field value is inherited from the parent entity. |
entity_type |
Mapped[EntityType]
|
Back-reference to the owning |
Attributes¶
id
class-attribute
instance-attribute
¶
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
entity_type_id
class-attribute
instance-attribute
¶
entity_type_id: Mapped[str] = mapped_column(ForeignKey('entity_type.id'), index=True)
display_name
class-attribute
instance-attribute
¶
display_name: Mapped[str] = mapped_column(String(128))
config
class-attribute
instance-attribute
¶
is_builtin
class-attribute
instance-attribute
¶
is_builtin: Mapped[bool] = mapped_column(Boolean, default=False)
is_required
class-attribute
instance-attribute
¶
is_required: Mapped[bool] = mapped_column(Boolean, default=False)
inherit
class-attribute
instance-attribute
¶
inherit: Mapped[bool] = mapped_column(Boolean, default=False)
entity_type
class-attribute
instance-attribute
¶
entity_type: Mapped[EntityType] = relationship(back_populates='fields')
Status
¶
Bases: Base
A configurable status option (e.g. waitingToStart, inProgress).
Status codes are camelCase by convention. The state field maps each
code to one of five lifecycle buckets used by the frontend for grouping and
colour-coding: not_started, in_progress, blocked, done,
cancelled.
Global statuses (entity_type_id=None) apply to all entity types.
Entity-scoped statuses (entity_type_id set) are restricted to that type.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
UUID primary key. |
code |
Mapped[str]
|
Unique camelCase code stored in |
name |
Mapped[str]
|
Human-readable label (e.g. |
color |
Mapped[str]
|
Hex colour rendered in status badges. |
icon |
Mapped[str | None]
|
Material Symbol icon key. |
short_name |
Mapped[str | None]
|
Abbreviated label for compact displays (e.g. |
state |
Mapped[str]
|
Lifecycle bucket — one of |
is_done |
Mapped[bool]
|
|
is_wip |
Mapped[bool]
|
|
is_retake |
Mapped[bool]
|
|
is_feedback_request |
Mapped[bool]
|
|
entity_type_id |
Mapped[str | None]
|
Optional FK scoping the status to one entity type. |
Attributes¶
id
class-attribute
instance-attribute
¶
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
color
class-attribute
instance-attribute
¶
color: Mapped[str] = mapped_column(String(16), default='#888888')
short_name
class-attribute
instance-attribute
¶
short_name: Mapped[str | None] = mapped_column(String(16))
state
class-attribute
instance-attribute
¶
state: Mapped[str] = mapped_column(String(16), default='in_progress')
is_done
class-attribute
instance-attribute
¶
is_done: Mapped[bool] = mapped_column(Boolean, default=False)
is_wip
class-attribute
instance-attribute
¶
is_wip: Mapped[bool] = mapped_column(Boolean, default=False)
is_retake
class-attribute
instance-attribute
¶
is_retake: Mapped[bool] = mapped_column(Boolean, default=False)
Workflow
¶
Bases: Base
A global status transition edge for one entity type.
Each row permits moving from from_status to to_status for the
entity type identified by entity_type_id. role_gate (currently
unused by the enforcement layer) is reserved for future role-restricted
transitions.
Per-project overrides are stored in
Project.data["overrides"]["transitions"] rather than in this table.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
UUID primary key. |
entity_type_id |
Mapped[str]
|
FK to the entity type this edge belongs to. |
from_status |
Mapped[str]
|
Source status code (e.g. |
to_status |
Mapped[str]
|
Target status code (e.g. |
role_gate |
Mapped[str | None]
|
Optional role required to perform this transition. |
Attributes¶
id
class-attribute
instance-attribute
¶
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=_uuid)
entity_type_id
class-attribute
instance-attribute
¶
entity_type_id: Mapped[str] = mapped_column(ForeignKey('entity_type.id'), index=True)
Step
¶
Bases: Base
A configurable pipeline/department step (Layout, Anim, Comp, ...).
Global list (not per entity type); referenced by the Task.step field and rendered with the configured color across the UI.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
|
code |
Mapped[str]
|
|
name |
Mapped[str]
|
|
color |
Mapped[str]
|
|
icon |
Mapped[str | None]
|
|
sort_order |
Mapped[int]
|
|
Priority
¶
Bases: Base
A configurable task priority level (None, Low, Medium, High, Urgent). Global list with a color and a numeric value for sorting/reporting.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
|
code |
Mapped[str]
|
|
name |
Mapped[str]
|
|
color |
Mapped[str]
|
|
icon |
Mapped[str | None]
|
|
value |
Mapped[int]
|
|
sort_order |
Mapped[int]
|
|
event
¶
Immutable audit log for every create / update / delete operation.
EventLog rows are written by services.records alongside every
mutation and are also fanned-out via the in-process event bus to any live
WebSocket subscribers.
Classes:
| Name | Description |
|---|---|
EventLog |
A single immutable audit-log entry for one entity mutation. |
Classes¶
EventLog
¶
Bases: Base
A single immutable audit-log entry for one entity mutation.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
Mapped[str]
|
UUID primary key. |
entity_type |
Mapped[str]
|
The type of entity that changed, e.g. |
entity_id |
Mapped[str]
|
UUID of the affected |
action |
Mapped[str]
|
|
actor |
Mapped[str | None]
|
Display name (or email) of the user who triggered the change,
|
changes |
Mapped[dict]
|
Snapshot of the fields that were written (may be the full data dict on create, or just the changed keys on update). |
created_at |
Mapped[datetime]
|
UTC timestamp when the event was recorded. |
Attributes¶
id
class-attribute
instance-attribute
¶
entity_type
class-attribute
instance-attribute
¶
entity_type: Mapped[str] = mapped_column(String(64), index=True)
entity_id
class-attribute
instance-attribute
¶
entity_id: Mapped[str] = mapped_column(String(36), index=True)
changes
class-attribute
instance-attribute
¶
Events¶
bus
¶
In-process pub/sub event bus.
Callbacks registered with subscribe are called synchronously by
emit. The events router uses this to push EventLog entries to
WebSocket clients in real time. Exceptions in callbacks are swallowed so
one bad subscriber cannot disrupt others.
Functions:
| Name | Description |
|---|---|
subscribe |
Register a callback to receive future events. |
unsubscribe |
Remove a previously registered callback. |
emit |
Broadcast an event dict to all registered subscribers. |
Functions:¶
subscribe
¶
unsubscribe
¶
emit
¶
emit(event: dict) -> None
Broadcast an event dict to all registered subscribers.
Iterates over a snapshot of the subscriber list so that callbacks which
call unsubscribe during iteration are safe. Exceptions raised by
individual callbacks are silently swallowed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
dict
|
Arbitrary event payload dict. |
required |
Examples:
Media¶
storage
¶
Local filesystem media store.
Keys are relative path strings (e.g. "thumbnails/abc123.jpg"). All
operations are relative to the configured root directory. Intermediate
directories are created automatically on write.
Classes:
| Name | Description |
|---|---|
LocalStorage |
Simple key-based local file store backed by a root directory. |
Classes¶
LocalStorage
¶
LocalStorage(root: str)
Simple key-based local file store backed by a root directory.
Attributes:
| Name | Type | Description |
|---|---|---|
root |
Absolute or relative |
Examples:
>>> import tempfile, pathlib
>>> with tempfile.TemporaryDirectory() as tmp:
... s = LocalStorage(tmp)
... key = s.put("test/hello.txt", b"hi")
... s.exists("test/hello.txt")
True
Initialise storage at root.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
root
|
str
|
Path to the directory where files are stored. Created lazily when the first file is written. |
required |
Methods:
| Name | Description |
|---|---|
fs_path |
Return the absolute filesystem path for a key. |
put |
Write raw bytes to a key, creating parent directories as needed. |
put_file |
Copy a local file into the store under |
exists |
Return |
open |
Return a binary read handle for the stored file. |
Attributes¶
Methods:¶
fs_path
¶
Return the absolute filesystem path for a key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Storage key (relative path). |
required |
Returns:
| Type | Description |
|---|---|
str
|
Absolute path string. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the key escapes the storage root. |
Examples:
put
¶
put_file
¶
exists
¶
open
¶
Return a binary read handle for the stored file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Storage key of an existing file. |
required |
Returns:
| Type | Description |
|---|---|
BinaryIO
|
An open binary file object. |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the key does not exist. |
Examples:
transcode
¶
ffmpeg-based thumbnail and proxy generation.
All functions require ffmpeg to be on PATH. Use has_ffmpeg() to
check availability before calling. Failures raise subprocess.CalledProcessError.
Functions:
| Name | Description |
|---|---|
has_ffmpeg |
Return |
make_thumbnail |
Extract the "best" frame from a video or image and write a JPEG thumbnail. |
downscale_image |
Write a downscaled JPEG of a still image (never upscales) — used so an |
probe_duration |
Return a media file's duration in seconds (0.0 if unknown). |
probe_media |
Return basic media metadata for |
make_filmstrip |
Write a horizontal filmstrip sprite of |
extract_frame |
Extract a single frame (by index) from a video as a JPEG. |
make_proxy |
Transcode any ffmpeg-readable file to a browser-compatible H.264 MP4. |
Functions:¶
make_thumbnail
¶
Extract the "best" frame from a video or image and write a JPEG thumbnail.
Uses ffmpeg's thumbnail filter to pick the representative frame,
then scales to width pixels (preserving aspect ratio).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src
|
str
|
Path to the source file (any ffmpeg-readable format). |
required |
dst
|
str
|
Destination path for the JPEG thumbnail. |
required |
width
|
int
|
Thumbnail width in pixels. |
320
|
Returns:
| Type | Description |
|---|---|
str
|
|
Raises:
| Type | Description |
|---|---|
CalledProcessError
|
If ffmpeg exits with a non-zero code. |
Examples:
downscale_image
¶
Write a downscaled JPEG of a still image (never upscales) — used so an object thumbnail is a small image, not the user's full-resolution upload.
probe_duration
¶
Return a media file's duration in seconds (0.0 if unknown).
Uses ffprobe; returns 0.0 for stills or when probing fails, so
callers can branch on duration > 0 to detect video.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src
|
str
|
Path to the source file. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Duration in seconds, or |
Examples:
probe_media
¶
Return basic media metadata for src via ffprobe.
Reads the first video stream's geometry / frame rate and the container duration. Missing or unprobeable values are simply omitted, so callers get a partial dict rather than an exception (a still image, for instance, has a width/height but no real duration).
Returns:
| Type | Description |
|---|---|
dict
|
A dict with any of |
dict
|
float) and |
Examples:
make_filmstrip
¶
Write a horizontal filmstrip sprite of frames evenly-spaced frames.
Samples the clip at frames / duration fps and tiles the result into a
single frames-column JPEG, so the web grid can scrub a Version by
mapping the hover position to a column. Short clips simply leave trailing
columns black (the sprite is always frames columns wide).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src
|
str
|
Path to the source video. |
required |
dst
|
str
|
Destination path for the JPEG sprite. |
required |
frames
|
int
|
Number of columns in the sprite. |
12
|
width
|
int
|
Per-frame width in pixels (sprite width is |
320
|
Returns:
| Type | Description |
|---|---|
str
|
|
Raises:
| Type | Description |
|---|---|
CalledProcessError
|
If ffmpeg fails. |
ValueError
|
If |
Examples:
extract_frame
¶
Extract a single frame (by index) from a video as a JPEG.
Seeks to (frame + 0.5) / fps seconds — the mid-frame, matching the
player's seek math — and writes one frame. Used to render the exact
annotated frame for the PDF review report.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src
|
str
|
Path to the source video. |
required |
dst
|
str
|
Destination image path. |
required |
frame
|
int
|
Zero-based frame index. |
required |
fps
|
float
|
Frames per second. |
24.0
|
Returns:
| Type | Description |
|---|---|
str
|
|
Raises:
| Type | Description |
|---|---|
CalledProcessError
|
If ffmpeg fails. |
Examples:
make_proxy
¶
Transcode any ffmpeg-readable file to a browser-compatible H.264 MP4.
-movflags +faststart moves the MOOV atom to the front of the file
so the browser can begin playback before the download completes.
Audio is stripped (-an) to keep the proxy small.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
src
|
str
|
Path to the source file. |
required |
dst
|
str
|
Destination path for the MP4 proxy. |
required |
Returns:
| Type | Description |
|---|---|
str
|
|
Raises:
| Type | Description |
|---|---|
CalledProcessError
|
If ffmpeg fails. |
Examples: