feat: move report artifacts to OSS storage

This commit is contained in:
Wyndham ARR
2026-08-04 12:37:26 +08:00
parent 727643f1f2
commit a2b86cdf10
45 changed files with 1288 additions and 1417 deletions

View File

@@ -21,16 +21,18 @@ from arr_web.downloads import (
)
from arr_web.repository import PostgresPortalRepository, UnavailablePortalRepository
from arr_web.processing_runtime import (
ObjectStoreRuntime,
ProcessingInputRuntime,
compose_object_store,
compose_programmatic_processing,
)
from arr_web.server import serve
from arr_web.services import ProgramMonthlyCoordinator
from monthly_reports.publishing import ArtifactToolBuilder, AtomicReportPublisher
from monthly_reports.publishing import OpenpyxlWorkbookBuilder, AtomicReportPublisher
from monthly_reports.repository import DatabaseConfig, PostgresReportRepository
from monthly_reports.service import MonthlyReportService
from company_reports.publishing import (
ArtifactToolBuilder as CompanyArtifactToolBuilder,
OpenpyxlWorkbookBuilder as CompanyOpenpyxlWorkbookBuilder,
AtomicReportPublisher as AtomicCompanyReportPublisher,
)
from company_reports.repository import (
@@ -81,8 +83,6 @@ def _parser() -> argparse.ArgumentParser:
action="store_true",
help="mark browser session cookies Secure for an HTTPS deployment",
)
parser.add_argument("--node-binary", type=Path)
parser.add_argument("--artifact-tool-module", type=Path)
return parser
@@ -108,67 +108,6 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
repository = UnavailablePortalRepository()
database_ready = False
monthly = None
monthly_ready = False
if args.enable_monthly_generation and database_ready:
try:
report_repository = PostgresReportRepository(
DatabaseConfig("controlled"),
connect=connect,
) if connect is not None else PostgresReportRepository(DatabaseConfig.from_environment())
builder = ArtifactToolBuilder(
PROJECT_ROOT / "monthly_reports" / "xlsx" / "build_workbook.mjs",
node_binary=str(args.node_binary) if args.node_binary else None,
artifact_tool_module=args.artifact_tool_module,
)
output_root = PROJECT_ROOT / "outputs" / "monthly_reports"
monthly = ProgramMonthlyCoordinator(
MonthlyReportService(
report_repository,
builder,
AtomicReportPublisher(PROJECT_ROOT, output_root),
output_root / ".staging",
)
)
monthly_ready = True
except Exception:
monthly = None
monthly_ready = False
company_reports = None
company_reports_ready = False
if args.enable_company_reports and database_ready:
try:
company_repository = (
PostgresCompanyReportRepository(
CompanyDatabaseConfig("controlled"),
connect=connect,
)
if connect is not None
else PostgresCompanyReportRepository(
CompanyDatabaseConfig.from_environment()
)
)
company_builder = CompanyArtifactToolBuilder(
PROJECT_ROOT / "company_reports" / "xlsx" / "build_workbook.mjs",
node_binary=str(args.node_binary) if args.node_binary else None,
artifact_tool_module=args.artifact_tool_module,
)
company_output_root = PROJECT_ROOT / "outputs" / "company_reports"
company_service = CompanyReportService(
company_repository,
company_builder,
AtomicCompanyReportPublisher(PROJECT_ROOT, company_output_root),
company_output_root / ".staging",
)
company_reports = PersistentCompanyReportCoordinator(
PROJECT_ROOT,
company_output_root,
ProgramCompanyReportExecutor(company_service),
)
company_reports_ready = True
except Exception:
company_reports = None
company_reports_ready = False
processing_input: Optional[ProcessingInputRuntime] = None
processing_ready = False
if args.enable_processing and database_ready:
@@ -181,6 +120,81 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
except Exception:
processing_input = None
processing_ready = False
report_storage_runtime: Optional[ObjectStoreRuntime] = None
report_object_store = (
processing_input.object_store if processing_input is not None else None
)
if report_object_store is None and database_ready and (
args.enable_monthly_generation or args.enable_company_reports
):
try:
report_storage_runtime = compose_object_store()
report_object_store = report_storage_runtime.object_store
except Exception:
report_storage_runtime = None
report_object_store = None
monthly = None
monthly_ready = False
if args.enable_monthly_generation and database_ready and report_object_store is not None:
try:
report_repository = PostgresReportRepository(
DatabaseConfig("controlled"),
connect=connect,
) if connect is not None else PostgresReportRepository(DatabaseConfig.from_environment())
output_root = PROJECT_ROOT / "outputs" / "monthly_reports"
monthly = ProgramMonthlyCoordinator(
MonthlyReportService(
report_repository,
OpenpyxlWorkbookBuilder(),
AtomicReportPublisher(
PROJECT_ROOT,
output_root,
object_store=report_object_store,
),
output_root / ".staging",
)
)
monthly_ready = True
except Exception:
monthly = None
monthly_ready = False
company_reports = None
company_reports_ready = False
if args.enable_company_reports and database_ready and report_object_store is not None:
try:
company_repository = (
PostgresCompanyReportRepository(
CompanyDatabaseConfig("controlled"),
connect=connect,
)
if connect is not None
else PostgresCompanyReportRepository(
CompanyDatabaseConfig.from_environment()
)
)
company_output_root = PROJECT_ROOT / "outputs" / "company_reports"
company_service = CompanyReportService(
company_repository,
CompanyOpenpyxlWorkbookBuilder(),
AtomicCompanyReportPublisher(
PROJECT_ROOT,
company_output_root,
object_store=report_object_store,
),
company_output_root / ".staging",
)
company_reports = PersistentCompanyReportCoordinator(
PROJECT_ROOT,
company_output_root,
ProgramCompanyReportExecutor(company_service),
object_store=report_object_store,
)
company_reports_ready = True
except Exception:
company_reports = None
company_reports_ready = False
booking_sources = None
company_source_upload_ready = False
if args.enable_company_reports and database_ready and processing_input is not None:
@@ -217,6 +231,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
else None
),
local_reader=ControlledProjectArtifactReader(PROJECT_ROOT),
oss_reader=(
ManagedObjectArtifactReader(report_object_store)
if report_object_store is not None
else None
),
)
if database_ready
else None
@@ -239,6 +258,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
company_reports.close()
if processing_input is not None:
processing_input.close()
if report_storage_runtime is not None:
report_storage_runtime.close()
return 0