Skip to content

feat: export archived records and back-fill file content in team sync#3795

Merged
snopoke merged 5 commits into
mainfrom
cs/more_updates
Jul 10, 2026
Merged

feat: export archived records and back-fill file content in team sync#3795
snopoke merged 5 commits into
mainfrom
cs/more_updates

Conversation

@SmittieC

@SmittieC SmittieC commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Product Description

Makes team-to-team sync a faithful, complete snapshot. Two gaps are closed:

  1. Archived records are now exported. Previously, archiving a chatbot, pipeline, collection, file, etc. silently dropped it (and its file bytes) from the export, so an archived resource never made it to the target server.
  2. Missing file content is back-filled on import. When a file's bytes aren't already present in the target's storage, the importer fetches them from the source rather than failing.

Technical Description

Export archived records (feat: export archived records in the team export API)

  • Versioned models filter is_archived=False on their default manager. team_scoped_queryset() now calls model.objects.get_all() (the VersionsObjectManagerMixin escape hatch) when available to bypass that filter, so archived rows are served for every versioned model.
  • get_team_files_queryset() (the file-blob zip) also uses File.objects.get_all(), so archived files' bytes ship too — otherwise imported archived File rows would reference blobs that were never uploaded. (Archiving a file never removes its storage blob, so the bytes are available.)

Back-fill missing file content (feat: back-fill missing file content when syncing a team)

  • On import, if a file's blob is missing from target storage, fetch it from the source's file-content API and write it before saving the row, instead of failing.
  • A 404 from the source means the source is missing the blob too: the row is imported without content (so references still resolve) and the file is listed in the sync report. Other fetch errors abort the sync.
  • Force-delete now warns that it also clears the team's files from backend storage (they must be re-imported).

Fix archived-collection deletion (fix: delete files of archived collections in delete_collection_task)

  • delete_collection_task runs after Collection.archive() has set is_archived=True, but it loaded the row via Collection.objects, whose manager hides archived rows — so the task was a silent no-op that left CollectionFile rows orphaned. It now loads the row with get_all().

Migrations

No migrations in this PR.

  • The migrations are backwards compatible

Demo

N/A

Docs and Changelog

  • This PR requires docs/changelog update

🤖 Generated with Claude Code

SmittieC and others added 2 commits July 9, 2026 07:47
Team sync copies a file row's storage path but not its bytes, relying on the
operator to move blobs into the target storage separately. When a blob is still
missing, importing the file now fetches it from the source's file content API and
writes it before saving the row, rather than failing.

A 404 means the source is missing the blob too: the file's metadata is imported
without content (so rows that reference it still resolve) and the file is listed
in the sync report. Other fetch errors abort the sync. Force-delete now warns
that it also clears the team's files from backend storage, so they must be
re-imported.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Versioned models filter is_archived=False on their default manager, so
archived rows (and their file bytes) were silently left out of the team
export. Use get_all() to bypass that filter:

- team_scoped_queryset() serves archived rows for every versioned model.
- Drop the collection_files is_archived filter; it only existed because
  archived collections weren't exported, which is no longer true.
- get_team_files_queryset() includes archived files' bytes in the zip so
  imported archived rows don't point at missing blobs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@SmittieC SmittieC marked this pull request as draft July 9, 2026 07:46
codescene-delta-analysis[bot]

This comment was marked as outdated.

delete_collection_task runs after Collection.archive() has set
is_archived=True, but it loaded the collection via Collection.objects,
whose manager hides archived rows -- so the task was a silent no-op and
left CollectionFile rows orphaned. Load the row with get_all() instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

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 Plus

Run ID: 6e01b5b7-6f45-444a-a2ed-51d5088ca87c

📥 Commits

Reviewing files that changed from the base of the PR and between 32767bc and ddd8d8b.

📒 Files selected for processing (12)
  • apps/documents/tasks.py
  • apps/documents/tests/test_tasks.py
  • apps/teams/export/client.py
  • apps/teams/export/importer.py
  • apps/teams/export/manifest.py
  • apps/teams/export/tests/test_client.py
  • apps/teams/export/tests/test_command.py
  • apps/teams/export/tests/test_importer.py
  • apps/teams/export/tests/test_manifest.py
  • apps/teams/management/commands/sync_team.py
  • apps/teams/tasks.py
  • apps/teams/tests/test_file_export.py

📝 Walkthrough

Walkthrough

This PR adds a file-content backfill mechanism to the team export/import pipeline: a new client method and exception fetch missing source file blobs, the importer recovers missing blobs during import (recording unresolvable filenames), and sync_team reports missing files and updates the force-delete confirmation. Separately, manifest export querysets, the team files export task, and collection deletion now use unfiltered managers (get_all()/_base_manager) so archived and soft-deleted records are included/matched rather than excluded. Corresponding tests were added or updated for all changes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • dimagi/open-chat-studio#3697: Both PRs extend the same importer/sync-team file-blob handling functionality during team sync.
  • dimagi/open-chat-studio#3712: Both PRs modify team_scoped_queryset in apps/teams/export/manifest.py, overlapping at the same manifest scoping logic.
  • dimagi/open-chat-studio#3751: Both PRs expand Command._report(...) in apps/teams/management/commands/sync_team.py, overlapping at the same reporting code path.

Suggested reviewers: snopoke

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: exporting archived records and back-filling file content in team sync.
Description check ✅ Passed The description follows the template and covers product, technical details, migrations, demo, and changelog status clearly.
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.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

codescene-delta-analysis[bot]

This comment was marked as outdated.

@SmittieC SmittieC marked this pull request as ready for review July 9, 2026 08:19
@SmittieC SmittieC requested a review from snopoke July 9, 2026 08:19
Comment thread apps/teams/export/client.py
Comment thread apps/teams/export/manifest.py Outdated
SmittieC and others added 2 commits July 9, 2026 15:33
The importer's existence check used the default manager, which hides archived and soft-deleted rows. On re-import those rows weren't found and got recreated as duplicates -- and for channels that violates the unique external_id. Look up through _base_manager so hidden target rows are matched and updated instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Use _base_manager in team_scoped_queryset so ExperimentChannel's deleted=False filter is bypassed alongside the versioning is_archived filter, giving a faithful snapshot that includes soft-deleted channels.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@codescene-delta-analysis codescene-delta-analysis 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.

Code Health Improved (1 files improve in Code Health)

Gates Failed
Enforce advisory code health rules (1 file with Lines of Code in a Single File)

Our agent can fix these. Install it.

Gates Passed
3 Quality Gates Passed

Reason for failure
Enforce advisory code health rules Violations Code Health Impact
test_importer.py 1 advisory rule 9.39 → 8.82 Suppress

See analysis details in CodeScene

View Improvements
File Code Health Impact Categories Improved
importer.py 9.39 → 10.00 Overall Code Complexity

Quality Gate Profile: Clean Code Collective
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

from datetime import UTC, datetime

import pytest
import requests

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Lines of Code in a Single File
This module has 611 lines of code, improve code health by reducing it to 600

Suppress

@SmittieC SmittieC requested a review from snopoke July 10, 2026 07:27
@snopoke snopoke merged commit 6b911ad into main Jul 10, 2026
29 of 30 checks passed
@snopoke snopoke deleted the cs/more_updates branch July 10, 2026 07:55
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.

2 participants