Skip to content

Feature/ftrack provider - #207

Draft
Eiken wants to merge 5 commits into
AcademySoftwareFoundation:mainfrom
goodbyekansas:feature/ftrack-provider
Draft

Eiken wants to merge 5 commits into
AcademySoftwareFoundation:mainfrom
goodbyekansas:feature/ftrack-provider

Conversation

@Eiken

@Eiken Eiken commented Sep 23, 2026

Copy link
Copy Markdown

Summary

Describe your change.

Testing

  • I have tested these changes locally
  • I have run all relevant automated tests
  • I have verified this does not break existing workflows
  • For changes that can be tested in UI, I have included screenshots or gif animations of the changes.

How I Tested

Describe exact steps used to test.

Eiken and others added 5 commits September 23, 2026 11:05
Implements the full ProdtrackProviderBase surface over ftrack, alongside
the existing ShotGrid and mock providers. Select with
PRODTRACK_PROVIDER=ftrack.

Three decisions shape the implementation:

Entity ids. ftrack keys entities by UUID while DNA ids are int
throughout (EntityBase, route params, Mongo bookkeeping, frontend
types). The provider assigns a stable int per UUID and persists the
pairing in ftrack_id_map.py, so a reverse lookup resolves on any
instance after any restart. The map must outlive the process: draft
notes, transcripts and stored segments all reference these ids.

Playlists. ftrack has two candidates — AssetVersionList (Lists, the
default) and ReviewSession (Client Reviews). Both are PlaylistAdapter
subclasses, and playlist_adapter() resolves the type per call from
FTRACK_PLAYLIST_ENTITY, so it can be swapped for a user setting later
without touching anything built on top.

Query shape. Projections are flat: scalar columns and foreign keys
only, never a dotted path. Deep projections make the ftrack server
build the joins, which is slower than fetching each layer separately.
_hydrate_versions walks versions -> assets -> contexts -> object types
and stitches the result, one batched query per layer, so a playlist
costs a constant number of queries regardless of size. Two tests reject
a dotted path in any projection constant or playlist-load query.

Also adds transcript_entity_type() to the provider base so main.py no
longer reads ShotGrid's env var directly, and a thumbnail proxy
endpoint because ftrack's own thumbnail URL carries the API key in its
query string.

Known gaps vs ShotGrid (note subject in metadata, cc merged into
recipients, updated_at unavailable, opt-in media paths) are documented
in backend/README.md.

Not yet run against a live ftrack server, and the repo test suite has
not run against it either — see the handoff doc for what that leaves
unverified.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Provides the toolchain that was missing on the host and blocked running
the backend tests outside Docker: python311 (matching
backend/Dockerfile), uv, nodejs_22, mongodb-ce, black, isort and
docker-compose.

Also packages the two prodtrack SDKs that are absent from nixpkgs,
which pulls in two more:

  shotgun_api3 3.9.2      github shotgunsoftware/python-api
  ftrack-python-api 3.0.6 github ftrackhq/ftrack-python-api
  clique 1.6.1            PyPI (not tagged on GitHub past 1.5.0)
  arrow 0.17.0            github arrow-py/arrow

arrow is pinned below 1.0 deliberately. ftrack calls
arrow.now().replace(tzinfo="utc") in entity/user.py, and arrow 1.0
removed tzinfo from .replace(); nixpkgs ships 1.4, which would raise on
a path get_versions_for_playlist reaches. pyparsing and websocket-client
are relaxed instead, since they are only used by the event hub and the
provider connects with auto_connect_event_hub=False.

ftrack needs build patching: upstream uses poetry-dynamic-versioning,
which reads the version from a git tag, so a source tarball would build
as 0.1.0. The derivation pins the version, swaps to plain poetry-core,
and drops sphinx-notfound-page (declared as a runtime dep but docs-only).

The shell does not install backend/requirements.txt automatically — it
prints the uv commands instead, so entering it is fast and works
offline. Tests still run in Docker.

Adds backend/docs/ftrack-provider-handoff.md covering the provider's
design, its verified and unverified assumptions, and this flake.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
requirements.txt pinned only top-level packages, so transitive
dependencies floated per install and anthropic was a range. Declare the
dependencies in backend/pyproject.toml, lock the full tree in
backend/uv.lock, and build the nix dev shell's Python environment from
it with uv2nix. requirements.txt is now exported from the lock, so the
dev shell, Docker and CI install the same set.

This replaces the hand-built shotgun_api3, ftrack-python-api, arrow and
clique derivations; uv resolves them from PyPI and honours ftrack's pins
instead of relaxing them.

Also allow the unfree mongodb-ce (SSPL) through allowUnfreePredicate,
without which the flake failed to evaluate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
NOTE_PROJECTION fetches only author_id, so reading note["author"] made
ftrack auto-populate the link with an extra query per note. The author
is now fetched explicitly by id when links are resolved, and passed into
_note_from rather than read off the note. A freshly created note keeps
its locally populated author.

Adds tests for single-entity reads, hydration of each entity type, the
thumbnail fetch, and the /api/ftrack-thumbnails endpoint.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Adds two apps that run mongo, the backend and the frontend on the host
from the working tree, so uncommitted changes are what gets exercised:

  nix run .#dev     all three under process-compose, backend reloading,
                    mock tracker and no auth by default
  nix run .#smoke   the same headless on spare ports and a throwaway
                    database; checks health, projects -> playlists ->
                    versions through the tracker, a draft note round
                    trip through storage, that the frontend serves and
                    compiles its entry, and CORS; then stops everything

Settings live in nix/stack-env.sh. Exported variables or .dna-dev/env
override them, e.g. PRODTRACK_PROVIDER=ftrack with its credentials.
Both apps refuse to start on a busy port instead of crash-looping.

The dev shell now lists the backend checks as CI runs them instead of
pointing at Docker, marks its prompt with (dna), and adds
process-compose and jq.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@linux-foundation-easycla

Copy link
Copy Markdown

CLA Not Signed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant