File size: 3,276 Bytes
db4ba8d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
"""
TradeFlow AI — Vessel Validation Router (T-070)

GET /api/v1/vessel/validate?vessel_name=...&voyage=...
POST /api/v1/vessel/validate  (full validation with batch context)
"""
from __future__ import annotations

from typing import Annotated

from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel

from ..auth.dependencies import CurrentUser, RequireOperator, get_current_user

router = APIRouter(prefix="/api/v1", tags=["vessel"])


class VesselValidateRequest(BaseModel):
    vessel_name: str
    voyage_number: str | None = None
    bl_date: str | None = None
    arrival_port: str | None = None
    batch_id: str | None = None


@router.get("/vessel/validate")
async def validate_vessel_quick(
    vessel_name: str = Query(..., description="Vessel name to validate"),
    voyage: str | None = Query(None, description="Voyage number"),
    _: None = RequireOperator,
) -> dict:
    """Quick vessel lookup — checks AIS database for vessel confirmation."""
    import asyncpg

    from ..config import settings
    from ..services.ceisa_auth import CEISAAuthClient  # noqa: F401 (unused — intentional stub)

    db_url = settings.DATABASE_URL.replace("postgresql+asyncpg://", "postgresql://")
    try:
        conn = await asyncpg.connect(db_url)
        row = await conn.fetchrow(
            """
            SELECT imo, vessel_name, latitude, longitude, speed_knots, timestamp
            FROM ais_vessel_positions
            WHERE LOWER(vessel_name) LIKE LOWER($1)
            ORDER BY timestamp DESC LIMIT 1
            """,
            f"%{vessel_name}%",
        )
        await conn.close()
        if row:
            return {
                "found": True,
                "vessel_name": row["vessel_name"],
                "imo": row["imo"],
                "last_position": {
                    "lat": row["latitude"],
                    "lon": row["longitude"],
                    "speed_knots": row["speed_knots"],
                    "timestamp": row["timestamp"].isoformat() if row["timestamp"] else None,
                },
            }
        return {"found": False, "vessel_name": vessel_name}
    except Exception as e:
        return {"found": False, "error": str(e)}


@router.post("/vessel/validate")
async def validate_vessel_full(
    body: VesselValidateRequest,
    user: Annotated[CurrentUser, Depends(get_current_user)],
) -> dict:
    """Full vessel validation with AIS + port lineup checks."""
    from packages.agents.src.nodes.vessel_validation_agent import (
        vessel_validate_node,  # type: ignore
    )


    # Build minimal state for the vessel_validate_node
    mock_state = {
        "batch_id": body.batch_id or "adhoc",
        "reconciled_fields": [{
            "vessel_name": {"value": body.vessel_name, "confidence": 1.0},
            "voyage_number": {"value": body.voyage_number, "confidence": 1.0} if body.voyage_number else None,
            "bl_date": {"value": body.bl_date, "confidence": 1.0} if body.bl_date else None,
            "port_discharge_code": {"value": body.arrival_port, "confidence": 1.0} if body.arrival_port else None,
        }],
        "preprocessed": [],
    }

    result = await vessel_validate_node(mock_state)
    return result.get("vessel_validation", {})