feat: AR-House initial commit
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
"""Sub-agente: Mercado laboral marítimo.
|
||||
|
||||
Fuentes: BLS.gov API (gratuita) + Overpass para instalaciones físicas.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import requests
|
||||
from data_fetchers.base import USER_AGENT, DEFAULT_TIMEOUT
|
||||
|
||||
OVERPASS_URL = "https://overpass-api.de/api/interpreter"
|
||||
BLS_BASE = "https://api.bls.gov/publicAPI/v2/timeseries/data/"
|
||||
|
||||
# NAICS codes marítimos para BLS
|
||||
MARITIME_NAICS = {
|
||||
"483": "Water Transportation",
|
||||
"4883": "Support Activities for Water Transportation",
|
||||
"3366": "Ship & Boat Building",
|
||||
"114": "Fishing, Hunting and Trapping",
|
||||
}
|
||||
|
||||
|
||||
def run(lat: float, lon: float, address: str, state: str = "FL") -> dict:
|
||||
result = {
|
||||
"maritime_employers": [],
|
||||
"shipyards": [],
|
||||
"marinas_with_jobs": [],
|
||||
"bls_employment": {},
|
||||
"maritime_presence_score": 0,
|
||||
"sources": [],
|
||||
"errors": [],
|
||||
}
|
||||
|
||||
# --- Overpass: instalaciones marítimas físicas ---
|
||||
try:
|
||||
facilities = _overpass_maritime(lat, lon)
|
||||
result["shipyards"] = facilities.get("shipyards", [])
|
||||
result["marinas_with_jobs"] = facilities.get("marinas", [])
|
||||
result["maritime_employers"] = facilities.get("employers", [])
|
||||
result["sources"].append("OpenStreetMap/Overpass")
|
||||
except Exception as e:
|
||||
result["errors"].append(f"Overpass maritime: {e}")
|
||||
|
||||
# --- BLS API (sin key — API v1 gratuita, limitada) ---
|
||||
try:
|
||||
bls = _bls_maritime(state)
|
||||
result["bls_employment"] = bls
|
||||
result["sources"].append("BLS.gov")
|
||||
except Exception as e:
|
||||
result["errors"].append(f"BLS: {e}")
|
||||
|
||||
# Presencia marítima general
|
||||
result["maritime_presence_score"] = (
|
||||
len(result["shipyards"]) * 15 +
|
||||
len(result["marinas_with_jobs"]) * 8 +
|
||||
len(result["maritime_employers"]) * 5
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _overpass_maritime(lat: float, lon: float, radius_m: int = 16000) -> dict:
|
||||
"""Instalaciones marítimas en radio de ~10 millas."""
|
||||
query = f"""
|
||||
[out:json][timeout:30];
|
||||
(
|
||||
node["industrial"="port"](around:{radius_m},{lat},{lon});
|
||||
node["waterway"="boatyard"](around:{radius_m},{lat},{lon});
|
||||
node["leisure"="marina"](around:{radius_m},{lat},{lon});
|
||||
way["leisure"="marina"](around:{radius_m},{lat},{lon});
|
||||
node["man_made"="shipyard"](around:{radius_m},{lat},{lon});
|
||||
node["seamark:type"="harbour"](around:{radius_m},{lat},{lon});
|
||||
);
|
||||
out body center;
|
||||
"""
|
||||
time.sleep(1)
|
||||
r = requests.post(OVERPASS_URL, data={"data": query},
|
||||
headers={"User-Agent": USER_AGENT}, timeout=35)
|
||||
r.raise_for_status()
|
||||
elements = r.json().get("elements", [])
|
||||
|
||||
shipyards, marinas, employers = [], [], []
|
||||
for el in elements:
|
||||
tags = el.get("tags", {})
|
||||
name = tags.get("name", "Sin nombre")
|
||||
industrial = tags.get("industrial", "")
|
||||
waterway = tags.get("waterway", "")
|
||||
leisure = tags.get("leisure", "")
|
||||
man_made = tags.get("man_made", "")
|
||||
|
||||
if man_made == "shipyard" or waterway == "boatyard":
|
||||
shipyards.append({"name": name, "type": "shipyard"})
|
||||
elif leisure == "marina":
|
||||
marinas.append({"name": name, "type": "marina"})
|
||||
elif industrial == "port":
|
||||
employers.append({"name": name, "type": "port"})
|
||||
|
||||
return {"shipyards": shipyards[:10], "marinas": marinas[:10], "employers": employers[:10]}
|
||||
|
||||
|
||||
def _bls_maritime(state: str) -> dict:
|
||||
"""BLS API v1 — empleo en water transportation por estado."""
|
||||
# Series ID formato: SMU{state_fips}0000004830000001 (Water Transportation)
|
||||
# Sin key usamos endpoint público v1
|
||||
series_id = f"SMU120000004830000001" # Florida como default
|
||||
payload = {
|
||||
"seriesid": [series_id],
|
||||
"startyear": "2022",
|
||||
"endyear": "2024",
|
||||
}
|
||||
headers = {"User-Agent": USER_AGENT, "Content-Type": "application/json"}
|
||||
import json
|
||||
r = requests.post(BLS_BASE, data=json.dumps(payload),
|
||||
headers=headers, timeout=DEFAULT_TIMEOUT)
|
||||
r.raise_for_status()
|
||||
data = r.json()
|
||||
|
||||
series = data.get("Results", {}).get("series", [])
|
||||
if not series:
|
||||
return {}
|
||||
|
||||
latest = series[0].get("data", [])
|
||||
if not latest:
|
||||
return {}
|
||||
|
||||
return {
|
||||
"series_id": series_id,
|
||||
"latest_employment": latest[0].get("value"),
|
||||
"period": latest[0].get("period"),
|
||||
"year": latest[0].get("year"),
|
||||
"label": "Water Transportation Employment (thousands)",
|
||||
}
|
||||
|
||||
|
||||
def score(data: dict) -> int:
|
||||
"""Score 0-100 para mercado laboral marítimo."""
|
||||
presence = data.get("maritime_presence_score", 0)
|
||||
|
||||
if presence >= 60:
|
||||
return 90
|
||||
elif presence >= 40:
|
||||
return 75
|
||||
elif presence >= 20:
|
||||
return 60
|
||||
elif presence >= 10:
|
||||
return 45
|
||||
elif presence > 0:
|
||||
return 35
|
||||
else:
|
||||
return 20
|
||||
Reference in New Issue
Block a user