Initial import: Slack export triage UI for Mattermost migration.
Web UI for channel/message cherry-pick, attachment preview, hydrate script, and workspace skip presets.
This commit is contained in:
Executable
+130
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pre-mark channels as skip in manifest.json using a JSON preset file."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
MANIFEST_PATH = REPO_ROOT / "manifest.json"
|
||||
DEFAULT_PRESET = REPO_ROOT / "presets" / "levkin-skips.json"
|
||||
DEFAULT_EXPORT = Path.home() / "Downloads/Levkin Slack export enriched"
|
||||
|
||||
|
||||
def load_preset(path: Path) -> dict:
|
||||
data = json.loads(path.read_text(encoding="utf-8"))
|
||||
skip_a = set(data.get("tier_a") or [])
|
||||
skip_b = set(data.get("tier_b") or [])
|
||||
skip_other = set(data.get("other") or [])
|
||||
return {
|
||||
"skip_all": skip_a | skip_b | skip_other,
|
||||
"tier_a": skip_a,
|
||||
"tier_b": skip_b,
|
||||
"review_priority": data.get("review_priority") or [],
|
||||
"review_later": data.get("review_later") or [],
|
||||
}
|
||||
|
||||
|
||||
def channel_entry_skip(notes: str) -> dict:
|
||||
return {
|
||||
"import": False,
|
||||
"status": "skip",
|
||||
"reviewed": True,
|
||||
"import_mode": "all",
|
||||
"notes": notes,
|
||||
"mattermost_channel": "",
|
||||
"mattermost_team": "",
|
||||
"messages": {},
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
export_path = Path(
|
||||
sys.argv[1] if len(sys.argv) > 1 and not sys.argv[1].startswith("-") else DEFAULT_EXPORT
|
||||
).expanduser().resolve()
|
||||
preset_path = DEFAULT_PRESET
|
||||
for i, arg in enumerate(sys.argv[1:], 1):
|
||||
if arg == "--preset" and i < len(sys.argv) - 1:
|
||||
preset_path = Path(sys.argv[i + 1]).expanduser().resolve()
|
||||
|
||||
if not export_path.is_dir():
|
||||
print(f"Export not found: {export_path}", file=sys.stderr)
|
||||
return 1
|
||||
if not preset_path.is_file():
|
||||
print(f"Preset not found: {preset_path}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
preset = load_preset(preset_path)
|
||||
skip_all = preset["skip_all"]
|
||||
|
||||
channels_json = export_path / "channels.json"
|
||||
if not channels_json.is_file():
|
||||
print(f"No channels.json in {export_path}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
all_names = {c["name"] for c in json.loads(channels_json.read_text()) if c.get("name")}
|
||||
for d in export_path.iterdir():
|
||||
if d.is_dir() and any(d.glob("*.json")):
|
||||
all_names.add(d.name)
|
||||
|
||||
manifest: dict = {
|
||||
"version": 1,
|
||||
"export_path": str(export_path),
|
||||
"updated_at": datetime.now(tz=timezone.utc).isoformat(),
|
||||
"global_notes": f"Preset skips from {preset_path.name}",
|
||||
"channels": {},
|
||||
"users": {},
|
||||
}
|
||||
|
||||
if MANIFEST_PATH.is_file():
|
||||
existing = json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||
if isinstance(existing.get("users"), dict):
|
||||
manifest["users"] = existing["users"]
|
||||
for name, entry in (existing.get("channels") or {}).items():
|
||||
if name not in skip_all and isinstance(entry, dict):
|
||||
manifest["channels"][name] = entry
|
||||
|
||||
skipped = []
|
||||
for name in sorted(skip_all):
|
||||
if name in all_names or (export_path / name).is_dir():
|
||||
tier = (
|
||||
"tier-a"
|
||||
if name in preset["tier_a"]
|
||||
else "tier-b"
|
||||
if name in preset["tier_b"]
|
||||
else "other"
|
||||
)
|
||||
manifest["channels"][name] = channel_entry_skip(
|
||||
f"Preset skip ({tier})"
|
||||
)
|
||||
skipped.append(name)
|
||||
|
||||
undecided = [
|
||||
n
|
||||
for n in sorted(all_names)
|
||||
if n not in skip_all and n not in manifest["channels"]
|
||||
]
|
||||
|
||||
MANIFEST_PATH.write_text(
|
||||
json.dumps(manifest, indent=2, ensure_ascii=False) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
print(f"Wrote {MANIFEST_PATH}")
|
||||
print(f" Skipped ({len(skipped)}): {', '.join('#' + n for n in skipped)}")
|
||||
print(f" Still undecided ({len(undecided)}): {', '.join('#' + n for n in undecided)}")
|
||||
print("\nSuggested review order:")
|
||||
for n in preset["review_priority"]:
|
||||
if n in undecided:
|
||||
print(f" → #{n}")
|
||||
for n in sorted(preset["review_later"]):
|
||||
if n in undecided:
|
||||
print(f" (later) #{n}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user