919 lines
31 KiB
Python
919 lines
31 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""市場情報頁面與 API 路由。"""
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from flask import Blueprint, jsonify, render_template, request
|
|
|
|
from auth import login_required
|
|
from config import SYSTEM_VERSION
|
|
from services.market_intel import MarketIntelService
|
|
from services.market_intel.candidate_queue_writer_cli import build_candidate_queue_writer_cli_plan
|
|
from services.market_intel.candidate_queue_writer_preflight import build_candidate_queue_writer_preflight
|
|
from services.market_intel.candidate_queue_writer_postwrite_smoke import build_candidate_queue_writer_postwrite_smoke
|
|
from services.market_intel.candidate_queue_writer_operator_drill import build_candidate_queue_writer_operator_drill
|
|
from services.market_intel.candidate_queue_writer_run_package import build_candidate_queue_writer_run_package
|
|
from services.market_intel.candidate_queue_writer_run_readiness import build_candidate_queue_writer_run_readiness
|
|
from services.market_intel.candidate_queue_writer_run_receipt import build_candidate_queue_writer_run_receipt
|
|
from services.market_intel.candidate_queue_writer_run_closeout import build_candidate_queue_writer_run_closeout
|
|
from services.market_intel.candidate_queue_review_handoff import build_candidate_queue_review_handoff
|
|
from services.market_intel.mcp_activation_evidence import build_mcp_activation_evidence_preview
|
|
from services.market_intel.mcp_fetch_target_review import (
|
|
build_mcp_fetch_target_review_preview,
|
|
)
|
|
from services.market_intel.mcp_manual_fetch_handoff import (
|
|
build_mcp_manual_fetch_handoff_preview,
|
|
)
|
|
from services.market_intel.mcp_runtime_promotion import build_mcp_runtime_promotion_preview
|
|
from services.market_intel.mcp_runtime_smoke_receipt import build_mcp_runtime_smoke_receipt_preview
|
|
|
|
|
|
TAIPEI_TZ = timezone(timedelta(hours=8))
|
|
market_intel_bp = Blueprint("market_intel", __name__)
|
|
|
|
|
|
def _service():
|
|
return MarketIntelService()
|
|
|
|
|
|
@market_intel_bp.route("/market_intel")
|
|
@market_intel_bp.route("/market_intel/campaigns")
|
|
@login_required
|
|
def campaigns():
|
|
service = _service()
|
|
status = service.get_runtime_status()
|
|
return render_template(
|
|
"market_intel/disabled.html",
|
|
active_page="market_intel",
|
|
datetime_now=datetime.now(TAIPEI_TZ).strftime("%Y-%m-%d %H:%M:%S"),
|
|
system_version=SYSTEM_VERSION,
|
|
status=status,
|
|
adapter_count=len(service.get_adapter_summaries()),
|
|
manual_fetch_allowed=service.manual_fetch_allowed(),
|
|
current_section="campaigns",
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/market_intel/products")
|
|
@market_intel_bp.route("/market_intel/matches")
|
|
@market_intel_bp.route("/market_intel/opportunities")
|
|
@login_required
|
|
def disabled_section():
|
|
service = _service()
|
|
status = service.get_runtime_status()
|
|
return render_template(
|
|
"market_intel/disabled.html",
|
|
active_page="market_intel",
|
|
datetime_now=datetime.now(TAIPEI_TZ).strftime("%Y-%m-%d %H:%M:%S"),
|
|
system_version=SYSTEM_VERSION,
|
|
status=status,
|
|
adapter_count=len(service.get_adapter_summaries()),
|
|
manual_fetch_allowed=service.manual_fetch_allowed(),
|
|
current_section="disabled",
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/status")
|
|
@login_required
|
|
def market_intel_status():
|
|
return jsonify(_service().get_runtime_status().to_dict())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/schema")
|
|
@login_required
|
|
def market_intel_schema():
|
|
return jsonify({"tables": _service().get_schema_tables()})
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/schema_smoke")
|
|
@login_required
|
|
def market_intel_schema_smoke():
|
|
return jsonify(_service().build_schema_smoke())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/schema_db_probe")
|
|
@login_required
|
|
def market_intel_schema_db_probe():
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(_service().build_schema_db_probe(execute_requested=execute_requested))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/platform_seed_db_diff")
|
|
@login_required
|
|
def market_intel_platform_seed_db_diff():
|
|
platform_code = request.args.get("platform", "all")
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(
|
|
_service().build_platform_seed_db_diff(
|
|
platform_code=platform_code,
|
|
execute_requested=execute_requested,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/legacy_source_bridge")
|
|
@login_required
|
|
def market_intel_legacy_source_bridge():
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
sample_limit = request.args.get("limit", default=5, type=int)
|
|
return jsonify(
|
|
_service().build_legacy_source_bridge(
|
|
execute_requested=execute_requested,
|
|
sample_limit=sample_limit,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_readiness")
|
|
@login_required
|
|
def market_intel_mcp_readiness():
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
timeout_sec = request.args.get("timeout", default=3, type=int)
|
|
return jsonify(
|
|
_service().build_mcp_readiness(
|
|
execute_requested=execute_requested,
|
|
timeout_sec=timeout_sec,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_tool_contract")
|
|
@login_required
|
|
def market_intel_mcp_tool_contract():
|
|
return jsonify(_service().build_mcp_tool_contract())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_deploy_preflight")
|
|
@login_required
|
|
def market_intel_mcp_deploy_preflight():
|
|
return jsonify(_service().build_mcp_deploy_preflight())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_activation_runbook")
|
|
@login_required
|
|
def market_intel_mcp_activation_runbook():
|
|
return jsonify(_service().build_mcp_activation_runbook())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_fetch_gate")
|
|
@login_required
|
|
def market_intel_mcp_fetch_gate():
|
|
fetch_requested = request.args.get("fetch", "false").lower() == "true"
|
|
execute_readiness = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(
|
|
_service().build_mcp_fetch_gate(
|
|
fetch_requested=fetch_requested,
|
|
execute_readiness=execute_readiness,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_completion_audit")
|
|
@login_required
|
|
def market_intel_mcp_completion_audit():
|
|
return jsonify(_service().build_mcp_completion_audit())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_activation_evidence", methods=["GET", "POST"])
|
|
@login_required
|
|
def market_intel_mcp_activation_evidence():
|
|
evidence = {}
|
|
if request.method == "POST":
|
|
payload = request.get_json(silent=True) or {}
|
|
evidence = payload.get("evidence", payload)
|
|
return jsonify(
|
|
build_mcp_activation_evidence_preview(
|
|
evidence=evidence,
|
|
phase=_service().phase,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_runtime_smoke_receipt", methods=["GET", "POST"])
|
|
@login_required
|
|
def market_intel_mcp_runtime_smoke_receipt():
|
|
receipt = {}
|
|
if request.method == "POST":
|
|
payload = request.get_json(silent=True) or {}
|
|
receipt = payload.get("receipt", payload)
|
|
return jsonify(
|
|
build_mcp_runtime_smoke_receipt_preview(
|
|
receipt=receipt,
|
|
phase=_service().phase,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_runtime_promotion", methods=["GET", "POST"])
|
|
@login_required
|
|
def market_intel_mcp_runtime_promotion():
|
|
activation_evidence = {}
|
|
runtime_receipt = {}
|
|
if request.method == "POST":
|
|
payload = request.get_json(silent=True) or {}
|
|
package = payload.get("promotion_package") or payload
|
|
activation_evidence = package.get("activation_evidence", {})
|
|
runtime_receipt = package.get("runtime_receipt", {})
|
|
return jsonify(
|
|
build_mcp_runtime_promotion_preview(
|
|
activation_evidence=activation_evidence,
|
|
runtime_receipt=runtime_receipt,
|
|
phase=_service().phase,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_manual_fetch_handoff", methods=["GET", "POST"])
|
|
@login_required
|
|
def market_intel_mcp_manual_fetch_handoff():
|
|
promotion_package = {}
|
|
promotion_review = None
|
|
operator_acknowledgements = {}
|
|
if request.method == "POST":
|
|
payload = request.get_json(silent=True) or {}
|
|
handoff_package = payload.get("handoff_package") or payload
|
|
promotion_package = handoff_package.get("promotion_package", {})
|
|
promotion_review = handoff_package.get("promotion_review")
|
|
operator_acknowledgements = handoff_package.get(
|
|
"operator_acknowledgements",
|
|
{},
|
|
)
|
|
|
|
service = _service()
|
|
return jsonify(
|
|
build_mcp_manual_fetch_handoff_preview(
|
|
promotion_package=promotion_package,
|
|
promotion_review=promotion_review,
|
|
operator_acknowledgements=operator_acknowledgements,
|
|
runtime_status=service.get_runtime_status(),
|
|
phase=service.phase,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/mcp_fetch_target_review", methods=["GET", "POST"])
|
|
@login_required
|
|
def market_intel_mcp_fetch_target_review():
|
|
handoff_package = {}
|
|
handoff_review = None
|
|
target_review = {}
|
|
if request.method == "POST":
|
|
payload = request.get_json(silent=True) or {}
|
|
package = payload.get("target_review_package") or payload
|
|
handoff_package = package.get("handoff_package", {})
|
|
handoff_review = package.get("handoff_review")
|
|
target_review = (
|
|
package
|
|
if "platform_targets" in package
|
|
else package.get("target_review", {})
|
|
)
|
|
|
|
return jsonify(
|
|
build_mcp_fetch_target_review_preview(
|
|
handoff_package=handoff_package,
|
|
handoff_review=handoff_review,
|
|
target_review=target_review,
|
|
phase=_service().phase,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/scheduler_plan")
|
|
@login_required
|
|
def market_intel_scheduler_plan():
|
|
return jsonify(_service().build_scheduler_plan())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_sample_plan")
|
|
@login_required
|
|
def market_intel_manual_sample_plan():
|
|
return jsonify(_service().build_manual_sample_plan())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_sample_acceptance")
|
|
@login_required
|
|
def market_intel_manual_sample_acceptance():
|
|
return jsonify(_service().build_manual_sample_acceptance())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_sample_review")
|
|
@login_required
|
|
def market_intel_manual_sample_review():
|
|
return jsonify(_service().build_manual_sample_review())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_sample_review/evaluate", methods=["POST"])
|
|
@login_required
|
|
def market_intel_manual_sample_review_evaluate():
|
|
payload = request.get_json(silent=True)
|
|
if not isinstance(payload, dict):
|
|
data = _service().build_manual_sample_review_evaluation(
|
|
sample_result=None,
|
|
payload_error="invalid_json_object",
|
|
)
|
|
return jsonify(data), 400
|
|
|
|
sample_result = payload.get("sample_result", payload)
|
|
return jsonify(
|
|
_service().build_manual_sample_review_evaluation(sample_result=sample_result)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_handoff",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_handoff():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
if not isinstance(payload, dict):
|
|
data = _service().build_manual_sample_candidate_handoff(
|
|
sample_result=None,
|
|
payload_error="invalid_json_object",
|
|
limit=limit,
|
|
)
|
|
return jsonify(data), 400
|
|
|
|
sample_result = payload.get("sample_result", payload)
|
|
return jsonify(
|
|
_service().build_manual_sample_candidate_handoff(
|
|
sample_result=sample_result,
|
|
limit=limit,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_draft",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_draft():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
if not isinstance(payload, dict):
|
|
data = _service().build_manual_sample_candidate_queue_draft(
|
|
sample_result=None,
|
|
payload_error="invalid_json_object",
|
|
limit=limit,
|
|
)
|
|
return jsonify(data), 400
|
|
|
|
sample_result = payload.get("sample_result", payload)
|
|
return jsonify(
|
|
_service().build_manual_sample_candidate_queue_draft(
|
|
sample_result=sample_result,
|
|
limit=limit,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_approval",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_approval():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
if not isinstance(payload, dict):
|
|
data = _service().build_manual_sample_candidate_queue_approval(
|
|
sample_result=None,
|
|
payload_error="invalid_json_object",
|
|
limit=limit,
|
|
)
|
|
return jsonify(data), 400
|
|
|
|
sample_result = payload.get("sample_result", payload)
|
|
return jsonify(
|
|
_service().build_manual_sample_candidate_queue_approval(
|
|
sample_result=sample_result,
|
|
limit=limit,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_transaction",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_transaction():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
if not isinstance(payload, dict):
|
|
data = _service().build_manual_sample_candidate_queue_transaction(
|
|
sample_result=None,
|
|
payload_error="invalid_json_object",
|
|
limit=limit,
|
|
)
|
|
return jsonify(data), 400
|
|
|
|
sample_result = payload.get("sample_result", payload)
|
|
return jsonify(
|
|
_service().build_manual_sample_candidate_queue_transaction(
|
|
sample_result=sample_result,
|
|
limit=limit,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_writer_status",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_status():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
apply_real_write = request.args.get("apply_real_write", "false").lower() == "true"
|
|
service = _service()
|
|
payload_error = None
|
|
if not isinstance(payload, dict):
|
|
sample_result = None
|
|
payload_error = "invalid_json_object"
|
|
else:
|
|
sample_result = payload.get("sample_result", payload)
|
|
|
|
transaction_preview = service.build_manual_sample_candidate_queue_transaction(
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
)
|
|
writer_preflight = build_candidate_queue_writer_preflight(
|
|
transaction_preview=transaction_preview,
|
|
execute_requested=False,
|
|
)
|
|
data = build_candidate_queue_writer_cli_plan(
|
|
transaction_preview=transaction_preview,
|
|
writer_preflight=writer_preflight,
|
|
execute_requested=execute_requested,
|
|
apply_real_write=apply_real_write,
|
|
approval_token=None,
|
|
approval_token_secret=None,
|
|
)
|
|
data["phase"] = service.phase
|
|
status_code = 400 if payload_error else 200
|
|
return jsonify(data), status_code
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_writer_preflight",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_preflight():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
service = _service()
|
|
payload_error = None
|
|
if not isinstance(payload, dict):
|
|
sample_result = None
|
|
payload_error = "invalid_json_object"
|
|
else:
|
|
sample_result = payload.get("sample_result", payload)
|
|
|
|
transaction_preview = service.build_manual_sample_candidate_queue_transaction(
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
)
|
|
data = build_candidate_queue_writer_preflight(
|
|
transaction_preview=transaction_preview,
|
|
execute_requested=execute_requested,
|
|
)
|
|
data["phase"] = service.phase
|
|
status_code = 400 if payload_error else 200
|
|
return jsonify(data), status_code
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_writer_postwrite_smoke",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_postwrite_smoke():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
service = _service()
|
|
payload_error = None
|
|
if not isinstance(payload, dict):
|
|
sample_result = None
|
|
payload_error = "invalid_json_object"
|
|
else:
|
|
sample_result = payload.get("sample_result", payload)
|
|
|
|
transaction_preview = service.build_manual_sample_candidate_queue_transaction(
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
)
|
|
data = build_candidate_queue_writer_postwrite_smoke(
|
|
transaction_preview=transaction_preview,
|
|
execute_requested=execute_requested,
|
|
)
|
|
data["phase"] = service.phase
|
|
status_code = 400 if payload_error else 200
|
|
return jsonify(data), status_code
|
|
|
|
|
|
def _build_candidate_queue_writer_stack(
|
|
*,
|
|
service,
|
|
sample_result,
|
|
payload_error,
|
|
limit,
|
|
operator_evidence=None,
|
|
):
|
|
transaction_preview = service.build_manual_sample_candidate_queue_transaction(
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
)
|
|
writer_preflight = build_candidate_queue_writer_preflight(
|
|
transaction_preview=transaction_preview,
|
|
execute_requested=False,
|
|
)
|
|
writer_status = build_candidate_queue_writer_cli_plan(
|
|
transaction_preview=transaction_preview,
|
|
writer_preflight=writer_preflight,
|
|
approval_token=None,
|
|
approval_token_secret=None,
|
|
)
|
|
postwrite_smoke = build_candidate_queue_writer_postwrite_smoke(
|
|
transaction_preview=transaction_preview,
|
|
execute_requested=False,
|
|
)
|
|
operator_drill = build_candidate_queue_writer_operator_drill(
|
|
transaction_preview=transaction_preview,
|
|
writer_preflight=writer_preflight,
|
|
writer_status=writer_status,
|
|
postwrite_smoke=postwrite_smoke,
|
|
)
|
|
run_package = build_candidate_queue_writer_run_package(
|
|
transaction_preview=transaction_preview,
|
|
writer_preflight=writer_preflight,
|
|
writer_status=writer_status,
|
|
postwrite_smoke=postwrite_smoke,
|
|
operator_drill=operator_drill,
|
|
)
|
|
run_readiness = build_candidate_queue_writer_run_readiness(
|
|
transaction_preview=transaction_preview,
|
|
writer_preflight=writer_preflight,
|
|
writer_status=writer_status,
|
|
postwrite_smoke=postwrite_smoke,
|
|
operator_drill=operator_drill,
|
|
run_package=run_package,
|
|
operator_evidence=operator_evidence or {},
|
|
)
|
|
return {
|
|
"transaction_preview": transaction_preview,
|
|
"writer_preflight": writer_preflight,
|
|
"writer_status": writer_status,
|
|
"postwrite_smoke": postwrite_smoke,
|
|
"operator_drill": operator_drill,
|
|
"run_package": run_package,
|
|
"run_readiness": run_readiness,
|
|
}
|
|
|
|
|
|
def _extract_candidate_queue_writer_run_payload():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
if not isinstance(payload, dict):
|
|
return None, {}, {}, {}, "invalid_json_object", limit
|
|
return (
|
|
payload.get("sample_result", payload),
|
|
payload.get("operator_evidence", {}),
|
|
payload.get("writer_output", {}),
|
|
payload.get("postwrite_smoke_result", {}),
|
|
None,
|
|
limit,
|
|
)
|
|
|
|
|
|
def _build_candidate_queue_writer_run_response(builder):
|
|
service = _service()
|
|
sample_result, operator_evidence, writer_output, postwrite_smoke_result, payload_error, limit = (
|
|
_extract_candidate_queue_writer_run_payload()
|
|
)
|
|
stack = _build_candidate_queue_writer_stack(
|
|
service=service,
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
operator_evidence=operator_evidence,
|
|
)
|
|
receipt = build_candidate_queue_writer_run_receipt(
|
|
transaction_preview=stack["transaction_preview"],
|
|
run_readiness=stack["run_readiness"],
|
|
writer_output=writer_output,
|
|
postwrite_smoke_result=postwrite_smoke_result,
|
|
operator_evidence=operator_evidence,
|
|
)
|
|
closeout = build_candidate_queue_writer_run_closeout(
|
|
transaction_preview=stack["transaction_preview"],
|
|
run_receipt=receipt,
|
|
operator_evidence=operator_evidence,
|
|
)
|
|
data = builder(stack, receipt, closeout, operator_evidence)
|
|
data["phase"] = service.phase
|
|
return jsonify(data), 400 if payload_error else 200
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_writer_operator_drill",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_operator_drill():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
service = _service()
|
|
payload_error = None
|
|
if not isinstance(payload, dict):
|
|
sample_result = None
|
|
payload_error = "invalid_json_object"
|
|
else:
|
|
sample_result = payload.get("sample_result", payload)
|
|
|
|
stack = _build_candidate_queue_writer_stack(
|
|
service=service,
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
)
|
|
data = stack["operator_drill"]
|
|
data["phase"] = service.phase
|
|
status_code = 400 if payload_error else 200
|
|
return jsonify(data), status_code
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_writer_run_package",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_run_package():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
service = _service()
|
|
payload_error = None
|
|
if not isinstance(payload, dict):
|
|
sample_result = None
|
|
payload_error = "invalid_json_object"
|
|
else:
|
|
sample_result = payload.get("sample_result", payload)
|
|
|
|
stack = _build_candidate_queue_writer_stack(
|
|
service=service,
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
)
|
|
data = stack["run_package"]
|
|
data["phase"] = service.phase
|
|
status_code = 400 if payload_error else 200
|
|
return jsonify(data), status_code
|
|
|
|
|
|
@market_intel_bp.route(
|
|
"/api/market_intel/manual_sample_review/candidate_queue_writer_run_readiness",
|
|
methods=["POST"],
|
|
)
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_run_readiness():
|
|
payload = request.get_json(silent=True)
|
|
limit = request.args.get("limit", default=20, type=int)
|
|
service = _service()
|
|
payload_error = None
|
|
operator_evidence = {}
|
|
if not isinstance(payload, dict):
|
|
sample_result = None
|
|
payload_error = "invalid_json_object"
|
|
else:
|
|
sample_result = payload.get("sample_result", payload)
|
|
operator_evidence = payload.get("operator_evidence", {})
|
|
|
|
stack = _build_candidate_queue_writer_stack(
|
|
service=service,
|
|
sample_result=sample_result,
|
|
payload_error=payload_error,
|
|
limit=limit,
|
|
operator_evidence=operator_evidence,
|
|
)
|
|
data = stack["run_readiness"]
|
|
data["phase"] = service.phase
|
|
status_code = 400 if payload_error else 200
|
|
return jsonify(data), status_code
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_sample_review/candidate_queue_writer_run_receipt", methods=["POST"])
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_run_receipt():
|
|
return _build_candidate_queue_writer_run_response(
|
|
lambda stack, receipt, closeout, operator_evidence: receipt
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_sample_review/candidate_queue_writer_run_closeout", methods=["POST"])
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_writer_run_closeout():
|
|
return _build_candidate_queue_writer_run_response(
|
|
lambda stack, receipt, closeout, operator_evidence: closeout
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_sample_review/candidate_queue_review_handoff", methods=["POST"])
|
|
@login_required
|
|
def market_intel_manual_sample_candidate_queue_review_handoff():
|
|
return _build_candidate_queue_writer_run_response(
|
|
lambda stack, receipt, closeout, operator_evidence: build_candidate_queue_review_handoff(
|
|
transaction_preview=stack["transaction_preview"],
|
|
run_closeout=closeout,
|
|
operator_evidence=operator_evidence,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/match_review_plan")
|
|
@login_required
|
|
def market_intel_match_review_plan():
|
|
return jsonify(_service().build_match_review_plan())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/opportunity_plan")
|
|
@login_required
|
|
def market_intel_opportunity_plan():
|
|
return jsonify(_service().build_opportunity_plan())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/opportunity_scoring_plan")
|
|
@login_required
|
|
def market_intel_opportunity_scoring_plan():
|
|
return jsonify(_service().build_opportunity_scoring_plan())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/opportunity_evidence_plan")
|
|
@login_required
|
|
def market_intel_opportunity_evidence_plan():
|
|
return jsonify(_service().build_opportunity_evidence_plan())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/opportunity_alert_plan")
|
|
@login_required
|
|
def market_intel_opportunity_alert_plan():
|
|
return jsonify(_service().build_opportunity_alert_plan())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/adapters")
|
|
@login_required
|
|
def market_intel_adapters():
|
|
return jsonify({"adapters": _service().get_adapter_summaries()})
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/dry_run_plan")
|
|
@login_required
|
|
def market_intel_dry_run_plan():
|
|
platform_code = request.args.get("platform", "all")
|
|
return jsonify(_service().build_dry_run_plan(platform_code=platform_code))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/discovery_plan")
|
|
@login_required
|
|
def market_intel_discovery_plan():
|
|
platform_code = request.args.get("platform", "all")
|
|
return jsonify(_service().build_discovery_plan(platform_code=platform_code))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/manual_discovery")
|
|
@login_required
|
|
def market_intel_manual_discovery():
|
|
platform_code = request.args.get("platform", "all")
|
|
fetch = request.args.get("fetch", "false").lower() == "true"
|
|
return jsonify(_service().run_manual_discovery(platform_code=platform_code, fetch=fetch))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/candidate_preview")
|
|
@login_required
|
|
def market_intel_candidate_preview():
|
|
platform_code = request.args.get("platform", "all")
|
|
fetch = request.args.get("fetch", "false").lower() == "true"
|
|
min_band = request.args.get("min_band", "all")
|
|
limit = request.args.get("limit", default=50, type=int)
|
|
limit = max(1, min(limit or 50, 200))
|
|
return jsonify(
|
|
_service().build_candidate_preview(
|
|
platform_code=platform_code,
|
|
fetch=fetch,
|
|
min_band=min_band,
|
|
limit=limit,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/platform_seed_plan")
|
|
@login_required
|
|
def market_intel_platform_seed_plan():
|
|
platform_code = request.args.get("platform", "all")
|
|
return jsonify(_service().build_platform_seed_plan(platform_code=platform_code))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/platform_seed_write_guard")
|
|
@login_required
|
|
def market_intel_platform_seed_write_guard():
|
|
platform_code = request.args.get("platform", "all")
|
|
return jsonify(_service().build_platform_seed_write_guard(platform_code=platform_code))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/platform_seed_writer_plan")
|
|
@login_required
|
|
def market_intel_platform_seed_writer_plan():
|
|
platform_code = request.args.get("platform", "all")
|
|
return jsonify(_service().build_platform_seed_writer_plan(platform_code=platform_code))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/migration_blueprint")
|
|
@login_required
|
|
def market_intel_migration_blueprint():
|
|
return jsonify(_service().build_migration_blueprint())
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/migration_apply_drill")
|
|
@login_required
|
|
def market_intel_migration_apply_drill():
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(
|
|
_service().build_migration_apply_drill(
|
|
execute_requested=execute_requested,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/migration_catalog_review")
|
|
@login_required
|
|
def market_intel_migration_catalog_review():
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(
|
|
_service().build_migration_catalog_review(
|
|
execute_requested=execute_requested,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/migration_live_smoke")
|
|
@login_required
|
|
def market_intel_migration_live_smoke():
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(
|
|
_service().build_migration_live_smoke(
|
|
execute_requested=execute_requested,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/live_db_inventory")
|
|
@login_required
|
|
def market_intel_live_db_inventory():
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(
|
|
_service().build_live_db_inventory(
|
|
execute_requested=execute_requested,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/seed_writer_cli_status")
|
|
@login_required
|
|
def market_intel_seed_writer_cli_status():
|
|
platform_code = request.args.get("platform", "all")
|
|
execute_requested = request.args.get("execute", "false").lower() == "true"
|
|
return jsonify(
|
|
_service().build_seed_writer_cli_status(
|
|
platform_code=platform_code,
|
|
execute_requested=execute_requested,
|
|
)
|
|
)
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/write_approval_runbook")
|
|
@login_required
|
|
def market_intel_write_approval_runbook():
|
|
platform_code = request.args.get("platform", "all")
|
|
return jsonify(_service().build_write_approval_runbook(platform_code=platform_code))
|
|
|
|
|
|
@market_intel_bp.route("/api/market_intel/deployment_readiness")
|
|
@login_required
|
|
def market_intel_deployment_readiness():
|
|
return jsonify(_service().build_deployment_readiness())
|
|
|
|
|
|
from routes import market_intel_mcp_run_routes # noqa: E402,F401
|
|
from routes import market_intel_mcp_review_routes # noqa: E402,F401
|