feat: sync latest ARR implementation
This commit is contained in:
133
arr_web/run.py
133
arr_web/run.py
@@ -3,24 +3,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, Optional, Sequence
|
||||
from typing import Any, Callable, Optional, Sequence
|
||||
|
||||
from arr_database import controlled_connect
|
||||
from arr_web.app import PortalApplication, RuntimeHealth
|
||||
from arr_web.agent_writeback_runtime import (
|
||||
AgentWritebackRuntime,
|
||||
compose_oss_agent_writeback,
|
||||
)
|
||||
from arr_web.auth import LoginCredentials
|
||||
from arr_web.booking_uploads import ProgramBookingSourceCoordinator
|
||||
from arr_web.company_jobs import (
|
||||
PersistentCompanyReportCoordinator,
|
||||
ProgramCompanyReportExecutor,
|
||||
)
|
||||
from arr_web.downloads import ControlledProjectArtifactReader
|
||||
from arr_web.downloads import (
|
||||
ControlledProjectArtifactReader,
|
||||
ManagedObjectArtifactReader,
|
||||
RoutedArtifactReader,
|
||||
)
|
||||
from arr_web.repository import PostgresPortalRepository, UnavailablePortalRepository
|
||||
from arr_web.processing_runtime import (
|
||||
ProcessingInputRuntime,
|
||||
compose_oss_processing_input,
|
||||
compose_programmatic_processing,
|
||||
)
|
||||
from arr_web.server import serve
|
||||
from arr_web.services import ProgramMonthlyCoordinator
|
||||
@@ -36,6 +38,10 @@ from company_reports.repository import (
|
||||
PostgresReportRepository as PostgresCompanyReportRepository,
|
||||
)
|
||||
from company_reports.service import CompanyReportService
|
||||
from booking_ingestion.excel_postgres import (
|
||||
DatabaseConfig as BookingExcelDatabaseConfig,
|
||||
)
|
||||
from booking_ingestion.excel_review_postgres import PostgresBookingReviewRepository
|
||||
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
||||
@@ -65,15 +71,10 @@ def _parser() -> argparse.ArgumentParser:
|
||||
action="store_true",
|
||||
help="enable Bangkok-gated five-company channel-detail jobs",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enable-agent-writeback",
|
||||
action="store_true",
|
||||
help="enable signed Super Agent result callbacks backed by private OSS",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--enable-processing",
|
||||
action="store_true",
|
||||
help="enable private OSS upload and Open Agent dispatch",
|
||||
help="enable ARR-owned deterministic XML processing and ingestion",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--secure-cookies",
|
||||
@@ -85,52 +86,20 @@ def _parser() -> argparse.ArgumentParser:
|
||||
return parser
|
||||
|
||||
|
||||
def _read_controlled_database(path: Path) -> Dict[str, object]:
|
||||
values: Dict[str, str] = {}
|
||||
for raw in path.read_text(encoding="utf-8").splitlines():
|
||||
line = raw.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
values[key.strip()] = value.strip().strip('"').strip("'")
|
||||
required = ("ARR_DB_HOST", "ARR_DB_PORT", "ARR_DB_USER", "ARR_DB_PASSWORD", "ARR_DB_NAME")
|
||||
if any(not values.get(key) for key in required):
|
||||
raise ValueError("controlled database configuration is incomplete")
|
||||
return {
|
||||
"host": values["ARR_DB_HOST"],
|
||||
"port": int(values["ARR_DB_PORT"]),
|
||||
"user": values["ARR_DB_USER"],
|
||||
"password": values["ARR_DB_PASSWORD"],
|
||||
"dbname": values["ARR_DB_NAME"],
|
||||
}
|
||||
|
||||
|
||||
def _controlled_connect(
|
||||
config_path: Path,
|
||||
driver_path: Optional[Path],
|
||||
) -> Callable[[str], Any]:
|
||||
if driver_path is not None:
|
||||
resolved = driver_path.expanduser().resolve()
|
||||
if not resolved.is_dir():
|
||||
raise ValueError("database driver path is unavailable")
|
||||
sys.path.insert(0, str(resolved))
|
||||
import psycopg # type: ignore[import-not-found]
|
||||
|
||||
parameters = _read_controlled_database(config_path.expanduser().resolve())
|
||||
|
||||
def connect(_dsn: str) -> Any:
|
||||
return psycopg.connect(**parameters, autocommit=False)
|
||||
|
||||
return connect
|
||||
|
||||
|
||||
def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
args = _parser().parse_args(argv)
|
||||
try:
|
||||
login_credentials = LoginCredentials.from_environment()
|
||||
except ValueError as error:
|
||||
raise SystemExit(str(error)) from None
|
||||
connect: Optional[Callable[[str], Any]] = None
|
||||
try:
|
||||
if args.db_config:
|
||||
connect = _controlled_connect(args.db_config, args.driver_path)
|
||||
repository = PostgresPortalRepository("controlled", connect=connect)
|
||||
connect = controlled_connect(args.db_config, args.driver_path)
|
||||
repository = PostgresPortalRepository(
|
||||
"controlled",
|
||||
connect=connect,
|
||||
)
|
||||
else:
|
||||
repository = PostgresPortalRepository.from_environment()
|
||||
repository.list_months()
|
||||
@@ -200,23 +169,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
except Exception:
|
||||
company_reports = None
|
||||
company_reports_ready = False
|
||||
agent_writeback: Optional[AgentWritebackRuntime] = None
|
||||
agent_writeback_ready = False
|
||||
if args.enable_agent_writeback and database_ready:
|
||||
try:
|
||||
agent_writeback = compose_oss_agent_writeback(
|
||||
project_root=PROJECT_ROOT,
|
||||
connect=connect,
|
||||
)
|
||||
agent_writeback_ready = True
|
||||
except Exception:
|
||||
agent_writeback = None
|
||||
agent_writeback_ready = False
|
||||
processing_input: Optional[ProcessingInputRuntime] = None
|
||||
processing_ready = False
|
||||
if args.enable_processing and database_ready:
|
||||
try:
|
||||
processing_input = compose_oss_processing_input(
|
||||
processing_input = compose_programmatic_processing(
|
||||
project_root=PROJECT_ROOT,
|
||||
connect=connect,
|
||||
)
|
||||
@@ -224,18 +181,45 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
except Exception:
|
||||
processing_input = None
|
||||
processing_ready = False
|
||||
booking_sources = None
|
||||
company_source_upload_ready = False
|
||||
if args.enable_company_reports and database_ready and processing_input is not None:
|
||||
try:
|
||||
booking_repository = PostgresBookingReviewRepository(
|
||||
BookingExcelDatabaseConfig("controlled"),
|
||||
connect=connect,
|
||||
) if connect is not None else PostgresBookingReviewRepository(
|
||||
BookingExcelDatabaseConfig.from_environment()
|
||||
)
|
||||
booking_sources = ProgramBookingSourceCoordinator(
|
||||
processing_input.object_store,
|
||||
booking_repository,
|
||||
)
|
||||
booking_sources.current()
|
||||
booking_sources.draft()
|
||||
company_source_upload_ready = True
|
||||
except Exception:
|
||||
booking_sources = None
|
||||
company_source_upload_ready = False
|
||||
application = PortalApplication(
|
||||
repository=repository,
|
||||
upload=(
|
||||
processing_input.coordinator if processing_input is not None else None
|
||||
),
|
||||
agent_results=(
|
||||
agent_writeback.coordinator if agent_writeback is not None else None
|
||||
),
|
||||
monthly=monthly,
|
||||
company_reports=company_reports,
|
||||
booking_sources=booking_sources,
|
||||
artifact_reader=(
|
||||
ControlledProjectArtifactReader(PROJECT_ROOT) if database_ready else None
|
||||
RoutedArtifactReader(
|
||||
daily_reader=(
|
||||
ManagedObjectArtifactReader(processing_input.object_store)
|
||||
if processing_input is not None
|
||||
else None
|
||||
),
|
||||
local_reader=ControlledProjectArtifactReader(PROJECT_ROOT),
|
||||
)
|
||||
if database_ready
|
||||
else None
|
||||
),
|
||||
health=RuntimeHealth(
|
||||
database_ready=database_ready,
|
||||
@@ -243,8 +227,9 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
monthly_ready=monthly_ready,
|
||||
download_ready=database_ready,
|
||||
company_reports_ready=company_reports_ready,
|
||||
agent_writeback_ready=agent_writeback_ready,
|
||||
company_source_upload_ready=company_source_upload_ready,
|
||||
),
|
||||
credentials=login_credentials,
|
||||
secure_cookies=args.secure_cookies,
|
||||
)
|
||||
try:
|
||||
@@ -252,8 +237,6 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
finally:
|
||||
if company_reports is not None:
|
||||
company_reports.close()
|
||||
if agent_writeback is not None:
|
||||
agent_writeback.close()
|
||||
if processing_input is not None:
|
||||
processing_input.close()
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user