Skip to content

ADFA-5094: offline topic tree with sizes — box serves Studio-shaped subtrees - #408

Merged
luisguzman-adfa merged 4 commits into
mainfrom
feat/ADFA-5094-box-tree-import
Aug 14, 2026
Merged

luisguzman-adfa merged 4 commits into
mainfrom
feat/ADFA-5094-box-tree-import

Conversation

@luisguzman-adfa

Copy link
Copy Markdown
Collaborator

Box half of ADFA-5094's depth work: serve a channel's topic tree offline, with byte sizes,
so the picker browses imported channels with no network.

What changes

  • Box: GET /k2go-api/kolibri/subtree/:nodeId returns Studio's contentnode_tree shape built
    from the local DB. buildLocalSubtree(nodeId) reads content_contentnode over its MPTT range
    and sums content_localfile.file_size per node, since no Kolibri API carries per-node bytes
    (granular has counts only; importexportsizeview is per-selection outstanding).
  • App: LocalTreeSource points at the new nodeId-keyed path and parses it with the same
    StudioCatalogMapper it uses for Studio. Distinct from the wizard's granular
    /kolibri/tree/:channelId.
  • ADR-5094-offline-tree-depth corrected to this contract.

Why
Today topic browsing needs Studio (online). Any channel already imported has its metadata on
the box, so its whole tree — with sizes — can be served locally.

Scope / safety

  • Fallback-safe: old box (no /subtree) or unimported channel -> 404/500 -> LocalTreeSource
    null -> Studio, i.e. today's behaviour. Additive route; no existing endpoint changed.
  • The SQL is validated on a box with a real Kolibri DB (no live DB in CI); a wrong query
    degrades to Studio.

Follow-up (not here)

  • Metadata-only import ("browse a channel offline before downloading it"): /download can't do
    it today (empty node_ids -> whole channel), so it needs an explicit node_ids:[] trigger plus
    a UX decision. Tracked for a follow-up PR.

…izes from local DB

The tree endpoint already exists as granular /kolibri/tree/:channelId (no bytes, web wizard).
The app uses a distinct nodeId-keyed /kolibri/subtree/:nodeId returning Studio-shaped JSON
with per-node sizes summed from content_localfile over the MPTT range.
…olibri/subtree/:nodeId)

buildLocalSubtree(nodeId) reads content_contentnode (MPTT range) + content_file/localfile and
emits Studio's contentnode_tree shape with files[].file_size, so the app parses it with the
same mapper it uses for Studio and shows sizes at every level offline. New route with 32-hex
id validation; 404 when the channel isn't imported (app falls back to Studio). The SQL is
device-verified (no live Kolibri DB in CI); a wrong query 500s and degrades to Studio.
… sized)

The box's /kolibri/tree/:channelId is the wizard's granular tree (no bytes). The app now
reads the nodeId-keyed /kolibri/subtree/:nodeId, which returns Studio-shaped JSON with byte
sizes, so StudioCatalogMapper parses it unchanged. Test path assertions updated.
Two size-under-count bugs in buildLocalSubtree, both only reachable when a subtree exceeds
the node cap:

- The files subquery had an ORDER-BY-less LIMIT, so under truncation it could select a
  different node subset than the rows query and leave a built node without its files. Drop
  the LIMIT: the subquery now covers every kept node; files for nodes past the cap are just
  never looked up.
- Truncation marked only the root incomplete, so deeper truncated folders under-counted
  silently. Mark every node whose range extends past the cutoff (rght > last kept lft) as
  incomplete, so the mapper reports unknown size instead of under-counting. Over-marking is
  safe; under-marking was the bug.

Subtrees within the cap are unaffected: no truncation, complete children, exact sizes.
@luisguzman-adfa
luisguzman-adfa merged commit 3f2e31a into main Aug 14, 2026
4 checks passed
@luisguzman-adfa
luisguzman-adfa deleted the feat/ADFA-5094-box-tree-import branch August 14, 2026 23:15
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