Initial public release
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from uuid import UUID
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
# Direct execution from /app/scripts must resolve the repository's backend
|
||||
# package before similarly named installed packages.
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BACKEND_ROOT = ROOT / "backend" if (ROOT / "backend" / "app").is_dir() else ROOT
|
||||
if str(BACKEND_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(BACKEND_ROOT))
|
||||
|
||||
from app.db.session import SessionLocal # noqa: E402 - imported after backend path bootstrap
|
||||
from app.models import Project # noqa: E402 - imported after backend path bootstrap
|
||||
|
||||
|
||||
CANONICAL_PROJECT_NAMES = frozenset(
|
||||
{
|
||||
"Kempen Regional Workbench",
|
||||
"Mol Municipality Workbench",
|
||||
}
|
||||
)
|
||||
|
||||
TECHNICAL_PROJECT_PATTERNS = (
|
||||
re.compile(r"^GeoIntel Detection Quality Matrix", re.IGNORECASE),
|
||||
re.compile(r"^GeoIntel hard-negative", re.IGNORECASE),
|
||||
re.compile(r"^GeoIntel Detection Calibration", re.IGNORECASE),
|
||||
re.compile(r"^GeoIntel Real Data Validation", re.IGNORECASE),
|
||||
re.compile(r"^GeoIntel Operational YOLO .* Smoke", re.IGNORECASE),
|
||||
re.compile(r"^GeoIntel Demo - Building QA$", re.IGNORECASE),
|
||||
re.compile(r"^GeoIntel training", re.IGNORECASE),
|
||||
re.compile(r"^Mol Building QA", re.IGNORECASE),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ArchivePlan:
|
||||
project_ids: tuple[UUID, ...]
|
||||
names: tuple[str, ...]
|
||||
|
||||
@property
|
||||
def count(self) -> int:
|
||||
return len(self.project_ids)
|
||||
|
||||
|
||||
def is_technical_project_name(name: str) -> bool:
|
||||
normalized = name.strip()
|
||||
if normalized in CANONICAL_PROJECT_NAMES:
|
||||
return False
|
||||
return any(pattern.search(normalized) for pattern in TECHNICAL_PROJECT_PATTERNS)
|
||||
|
||||
|
||||
def build_archive_plan(db: Session) -> ArchivePlan:
|
||||
rows = (
|
||||
db.query(Project)
|
||||
.filter(Project.status == "active")
|
||||
.order_by(Project.created_at.asc())
|
||||
.all()
|
||||
)
|
||||
selected = [row for row in rows if is_technical_project_name(row.name)]
|
||||
return ArchivePlan(
|
||||
project_ids=tuple(row.id for row in selected),
|
||||
names=tuple(row.name for row in selected),
|
||||
)
|
||||
|
||||
|
||||
def apply_archive_plan(db: Session, plan: ArchivePlan) -> int:
|
||||
if not plan.project_ids:
|
||||
return 0
|
||||
updated = (
|
||||
db.query(Project)
|
||||
.filter(Project.id.in_(plan.project_ids), Project.status == "active")
|
||||
.update({Project.status: "archived"}, synchronize_session=False)
|
||||
)
|
||||
db.commit()
|
||||
return int(updated)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Archive allowlisted GeoIntel operator and benchmark projects. "
|
||||
"Dry-run is the default; pass --apply to persist status changes."
|
||||
)
|
||||
)
|
||||
parser.add_argument("--apply", action="store_true", help="Persist status='archived'.")
|
||||
parser.add_argument(
|
||||
"--show-names",
|
||||
action="store_true",
|
||||
help="Include every matched project name in the JSON output.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
with SessionLocal() as db:
|
||||
plan = build_archive_plan(db)
|
||||
archived_count = apply_archive_plan(db, plan) if args.apply else 0
|
||||
payload = {
|
||||
"mode": "apply" if args.apply else "dry-run",
|
||||
"matched_count": plan.count,
|
||||
"archived_count": archived_count,
|
||||
"canonical_projects_preserved": sorted(CANONICAL_PROJECT_NAMES),
|
||||
}
|
||||
if args.show_names:
|
||||
payload["matched_names"] = list(plan.names)
|
||||
print(json.dumps(payload, indent=2))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user