Skip to content

feat(cl2k): split one Drive folder into the artwork type layout - #470

Merged
chodeus merged 4 commits into
developfrom
feat/cl2k-split-drive-by-type
Aug 7, 2026
Merged

feat(cl2k): split one Drive folder into the artwork type layout#470
chodeus merged 4 commits into
developfrom
feat/cl2k-split-drive-by-type

Conversation

@chodeus

@chodeus chodeus commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Summary

Routing artwork to logos / backgrounds / squareart meant finding and pasting three Drive folder IDs by hand — you had to create the folders in Drive yourself, then dig each ID out of its URL.

This adds a per-row "Split by type" action. Point one row at the parent folder you already have, press it, and CHUB creates (or reuses) the three subfolders and replaces that row with one routed row per type, carrying their real IDs.

Related issue

N/A

Type of change

  • Bug fix (non-breaking)
  • New feature (non-breaking)
  • Breaking change (config migration, schema, endpoint, or UI behavior)
  • Docs only
  • Refactor / internal cleanup

Not everyone wants subfolders — nothing here changes that

Flat remains the default and requires doing nothing. A single row claiming several types keeps putting all of them in one folder, exactly as today. There is no migration, no schema change, and no new config field — the button only rewrites the rows you explicitly press it on.

Two deliberate choices for the flat case:

  • Only the types the parent row claimed are carried to children, so splitting a logos-only Drive doesn't silently start uploading backgrounds.
  • It is purely additive on Drive: nothing is moved, renamed or deleted, and unrelated sibling folders under the parent are left alone. A test asserts no destructive rclone verb is ever issued.

Why this shape rather than subpath support

The alternative was storing one parent ID plus a "use type subfolders" flag and appending logos/ at upload time (posterflow's model). Rejected: upload_file, move_file and list_files all address a Drive by a bare folder ID, so list_files would need recursion or poster_self_heal's live-Drive source would stop seeing artwork entirely — silently re-breaking what #466 just fixed. It would also have meant relaxing _reject_unsafe_id, which exists to stop a crafted value reaching rclone as a flag.

Resolving real child IDs up front keeps the runtime plumbing completely untouched and leaves the config self-describing: three rows, three real IDs, and the Poster Healer coverage panel (#467) shows the literal truth.

The subfolder names come from a fixed internal map (TYPE_SUBFOLDERS), never user input, so the rclone argument surface is unchanged and the parent ID is still validated by _reject_unsafe_id.

Testing

  • ruff check . → exit 0
  • python -m pytest → 1581 passed (9 new)
  • npm run lint, npx prettier --check "src/**/*.{js,jsx,css,json,md}" (whole tree), npx stylelint, npm run build → all exit 0
  • npx vitest run → 38 passed

tests/test_cl2k_type_subfolders.py stubs every rclone call, so nothing touches Drive. Covers: creating all three under an empty parent; reusing existing ones and issuing no mkdir (and only one listing — no pointless second round-trip); creating only the missing one; leaving unrelated sibling folders alone while asserting no destructive verb is issued; mkdir failure; listing failure; Drive returning no ID for a subfolder (fail loudly rather than store a blank ID that would upload to the parent root); an unsafe parent ID rejected before any rclone runs; and a missing OAuth token.

Screenshots

Not captured — the button sits beside the existing "Test upload" button on each Drive row, same styling, and reports via the usual toast.

Checklist

  • Config schema updated if applicable (backend/util/config.py) — N/A, no schema change; it writes existing gdrive_uploads fields
  • Frontend builds cleanly (cd frontend && npm run build)
  • No new DAPS references introduced
  • Docs / wiki drafts updated if behavior or shape changed — endpoint documented in its own docstring
  • CHANGELOG entry added for user-visible changes — N/A, develop-only extension; release-please runs on main only

All four touched files are develop-only, so the branch-isolation invariant holds.

Summary by CodeRabbit

  • New Features

    • Added a Google Drive “Split by type” action for routing artwork into separate logos, backgrounds, and squareart folders.
    • Existing type folders are reused, while missing folders are created automatically.
    • Artwork types without assigned folders remain unclaimed.
  • Bug Fixes

    • Added validation and clear error handling for invalid Drive folders, unavailable authorization, and Drive operation failures.

chodeus added 2 commits August 7, 2026 14:54
Routing art to logos/backgrounds/squareart meant finding and pasting three
Drive folder IDs by hand. Add a per-row "Split by type" action: it creates (or
reuses) the three subfolders under the parent you already entered and replaces
that row with one routed row per type, carrying their real IDs.

Opt-in per Drive. A drive that should stay flat never presses it and keeps a
single row claiming several types, exactly as before — there is no migration
and no default change.

Purely additive on Drive: nothing is moved, renamed or deleted, and unrelated
sibling folders under the parent are left alone. The subfolder names come from
a fixed internal map, never user input, so the rclone argument surface is
unchanged; the parent id is still validated by _reject_unsafe_id.

Only the types the parent row claimed are carried to children, so splitting a
logos-only Drive doesn't silently start uploading backgrounds.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 8dd8535e-c3f1-4548-b739-91283599f061

📥 Commits

Reviewing files that changed from the base of the PR and between 819c168 and 6801f48.

📒 Files selected for processing (3)
  • backend/util/cl2k/gdrive_upload.py
  • frontend/src/extensions/cl2k/SaveLocationsFields.jsx
  • tests/test_cl2k_type_subfolders.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/util/cl2k/gdrive_upload.py
  • frontend/src/extensions/cl2k/SaveLocationsFields.jsx

📝 Walkthrough

Walkthrough

Adds a backend endpoint and Drive utility to create or reuse logos, backgrounds, and squareart folders. Adds a frontend action that splits eligible Drive rows by artwork type.

Changes

Google Drive type routing

Layer / File(s) Summary
Type folder provisioning
backend/util/cl2k/gdrive_upload.py, tests/test_cl2k_type_subfolders.py
Defines the type-to-folder mapping. Creates or reuses missing Drive subfolders, validates folder IDs, and tests success and failure cases.
Backend subfolder endpoint
backend/api/cl2k_maker.py
Adds POST /api/cl2k-maker/gdrive/type-subfolders. The endpoint validates the parent folder and OAuth token, calls the provisioning utility, and returns distinct errors.
Frontend split action
frontend/src/extensions/cl2k/SaveLocationsFields.jsx
Adds a Drive entry action that calls the endpoint and replaces eligible rows with one row per artwork type.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SplitSubfoldersButton
  participant gdrive_type_subfolders
  participant ensure_type_subfolders
  participant rclone
  SplitSubfoldersButton->>gdrive_type_subfolders: request type subfolders
  gdrive_type_subfolders->>ensure_type_subfolders: provision or reuse folders
  ensure_type_subfolders->>rclone: list or create Drive folders
  rclone-->>ensure_type_subfolders: return folder metadata
  ensure_type_subfolders-->>gdrive_type_subfolders: return folder metadata
  gdrive_type_subfolders-->>SplitSubfoldersButton: return success or error
  SplitSubfoldersButton->>SplitSubfoldersButton: replace eligible Drive row
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the new feature that splits one Google Drive folder into artwork-type subfolders.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cl2k-split-drive-by-type

Comment @coderabbitai help to get the list of available commands.

@chodeus

chodeus commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chodeus

chodeus commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chodeus

chodeus commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chodeus

chodeus commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/util/cl2k/gdrive_upload.py`:
- Around line 325-327: Validate complete subfolder records before replacing a
row: in backend/util/cl2k/gdrive_upload.py lines 325-327, update the check
around TYPE_SUBFOLDERS to reject expected folders whose IDs are absent, blank,
or non-string; in tests/test_cl2k_type_subfolders.py lines 133-140, add
regression coverage for blank IDs in logos, backgrounds, and squareart records;
in frontend/src/extensions/cl2k/SaveLocationsFields.jsx lines 415-417, validate
before onSplit that there is exactly one record per expected type and that each
name and folder_id is non-empty.

In `@frontend/src/extensions/cl2k/SaveLocationsFields.jsx`:
- Around line 511-522: The subfolder filter in the row-update callback must not
treat an empty parent.types list as claiming every type. Change the filter
around claimed and image_type to require claimed.includes(s.image_type) so
unclaimed parent rows produce no children and remain unchanged through the
existing empty-children return.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e87f5019-8943-424c-83e1-4a8c670ce975

📥 Commits

Reviewing files that changed from the base of the PR and between 3786479 and 819c168.

📒 Files selected for processing (4)
  • backend/api/cl2k_maker.py
  • backend/util/cl2k/gdrive_upload.py
  • frontend/src/extensions/cl2k/SaveLocationsFields.jsx
  • tests/test_cl2k_type_subfolders.py

Comment thread backend/util/cl2k/gdrive_upload.py
Comment thread frontend/src/extensions/cl2k/SaveLocationsFields.jsx
…laimed types

Two defects this PR introduced.

The completeness check only asked whether the NAME was present in the id map, so
a record like {"Name": "logos", "ID": ""} passed and produced a routed row with
an empty folder_id. _drive_targets skips a blank id, so that art type would have
stopped uploading with no error anywhere. Blank, absent and non-string ids are
now filtered out before the check, which makes the existing "did not return an
id" guard actually catch them. Parametrised over "", "   ", None and a non-string
— all four verified red against the old check.

The split filter treated an empty parent.types as claiming every type, so a row
claiming nothing split into all three routed rows — the opposite of what this
PR's own description promises. Only claimed types now carry to children; an
unclaimed row produces no children and is left untouched.

The button also refuses to rewrite the row unless every art type came back with a
non-empty folder_id, so a partial answer can't be saved.
@chodeus

chodeus commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chodeus
chodeus merged commit e7b4110 into develop Aug 7, 2026
12 checks passed
@chodeus
chodeus deleted the feat/cl2k-split-drive-by-type branch August 7, 2026 09:20
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