satupeta-main/api/routers/datasets_router.py

309 lines
8.5 KiB
Python
Raw Normal View History

2026-01-27 02:52:02 +00:00
import asyncio
from uuid import uuid4
from fastapi import APIRouter, HTTPException
2026-01-29 03:16:34 +00:00
import httpx
2026-01-27 02:52:02 +00:00
import requests
from sqlalchemy import text
from sqlalchemy.exc import SQLAlchemyError
from database.connection import engine
from services.datasets.delete import delete_dataset_from_partition # import fungsi di atas
from response import successRes, errorRes
from services.datasets.publish_geonetwork import publish_metadata
from services.datasets.publish_geoserver import publish_layer_to_geoserver
from services.datasets.metadata import update_job_status
2026-01-29 03:16:34 +00:00
from core.config import GEOSERVER_URL, GEOSERVER_USER, GEOSERVER_PASS, QGIS_URL, MAIN_API_URL, SERVICE_KEY
2026-01-27 02:52:02 +00:00
router = APIRouter()
def serialize_row(row_dict):
new_dict = {}
for key, value in row_dict.items():
if hasattr(value, "isoformat"):
new_dict[key] = value.isoformat()
else:
new_dict[key] = value
return new_dict
@router.get("/metadata")
async def get_author_metadata(
# user = Depends(get_current_user)
):
"""
Mengambil data author_metadata:
- Admin semua data
- User hanya data miliknya
"""
try:
async with engine.begin() as conn:
query = text("""
SELECT *
FROM backend.author_metadata
ORDER BY CASE process
WHEN 'CLEANSING' THEN 1
WHEN 'ERROR' THEN 2
WHEN 'FINISHED' THEN 3
WHEN 'TESTING' THEN 4
END;
""")
result = await conn.execute(query)
rows = result.fetchall()
# data = [dict(row._mapping) for row in rows]
data = [serialize_row(dict(row._mapping)) for row in rows]
return successRes(
message="Berhasil mengambil data author metadata",
data=data
)
except Exception as e:
print(f"[ERROR] Gagal ambil author_metadata: {e}")
raise errorRes(
status_code=500,
message="Gagal mengambil data author_metadata",
details=str(e)
)
@router.delete("/delete/{user_id}/{metadata_id}")
async def delete_dataset(user_id: int, metadata_id: int, title: str):
"""
Hapus dataset tertentu (berdasarkan user_id dan metadata_id)
"""
try:
async with engine.begin() as conn:
await delete_dataset_from_partition(conn, user_id, metadata_id, title)
return successRes(message=f"Dataset {title} berhasil dihapus.", data="")
except Exception as e:
print(f"[ERROR] Gagal hapus dataset: {e}")
raise errorRes(status_code=500, details=str(e), message="Gagal hapus dataset")
# @router.post("/cleansing/{table_name}")
def cleansing_data(table_name: str, job_id: str):
payload = {
"table_name": table_name,
"job_id": job_id
}
print("cleansing_data runn")
# response = requests.post(
# f"{QGIS_URL}/process/{table_name}",
# )
response = requests.post(
f"{QGIS_URL}/process",
json=payload,
)
return response
@router.post("/jobs/callback")
async def job_callback(payload: dict):
table = payload["table"]
job_id = payload["job_id"]
geos_link = publish_layer_to_geoserver(table, job_id)
2026-02-10 01:54:35 +00:00
uuid = await publish_metadata(table_name=table, geoserver_links=geos_link)
2026-01-27 02:52:02 +00:00
2026-02-10 01:54:35 +00:00
await update_job_status(table, "FINISHED", job_id)
2026-01-27 02:52:02 +00:00
return {
"ok": True,
"uuid": uuid
}
@router.get("/styles")
def get_style_list(workspace: str = None):
"""
Mengambil daftar style yang ada di GeoServer.
- Jika workspace = None ambil style global
- Jika workspace diisi ambil style milik workspace tersebut
"""
# Tentukan URL sesuai workspace
if workspace:
url = f"{GEOSERVER_URL}/rest/workspaces/{workspace}/styles"
else:
url = f"{GEOSERVER_URL}/rest/styles"
headers = {"Accept": "application/json"}
try:
response = requests.get(
url,
auth=(GEOSERVER_USER, GEOSERVER_PASS),
headers=headers,
timeout=15
)
if response.status_code == 200:
data = response.json()
styles = data.get("styles", {}).get("style", [])
return {
"status": "success",
"workspace": workspace,
"count": len(styles),
"styles": styles
}
else:
raise HTTPException(status_code=response.status_code, detail=response.text)
except requests.exceptions.RequestException as e:
raise HTTPException(status_code=500, detail=f"Request error: {str(e)}")
@router.get("/styles/{style_name}")
def get_style(style_name: str, workspace: str = None):
"""
Mengambil file SLD style dari GeoServer.
- Jika workspace tidak diisi ambil style global
- Jika workspace diisi ambil dari workspace
"""
# Tentukan endpoint sesuai workspace
url = f"{GEOSERVER_URL}/rest/styles/{style_name}.sld"
try:
response = requests.get(
url,
auth=(GEOSERVER_USER, GEOSERVER_PASS),
timeout=15
)
if response.status_code == 200:
# Return isi SLD sebagai text
return {
"status": "success",
"style_name": style_name,
"workspace": workspace,
"sld": response.text
}
elif response.status_code == 404:
raise HTTPException(status_code=404, detail="Style tidak ditemukan di GeoServer")
else:
raise HTTPException(status_code=500, detail=f"GeoServer error: {response.text}")
except requests.exceptions.RequestException as e:
raise HTTPException(status_code=500, detail=f"Request error: {str(e)}")
# =============================================================
# cleansing query
# =============================================================
2026-01-29 03:16:34 +00:00
# async def query_cleansing_data_fn(
# table_name: str
# ):
# try:
# async with engine.begin() as conn:
# await conn.execute(
# text("SELECT public.fn_cleansing_satupeta_polygon(:table_name)"),
# {"table_name": table_name}
# )
# return "done"
# except SQLAlchemyError as e:
# raise RuntimeError(f"Fix geometry failed: {str(e)}")
2026-01-27 02:52:02 +00:00
async def query_cleansing_data(
table_name: str
):
try:
2026-02-10 01:54:35 +00:00
print("clean")
2026-01-27 02:52:02 +00:00
async with engine.begin() as conn:
await conn.execute(
2026-01-29 03:16:34 +00:00
text("CALL pr_cleansing_satupeta_polygon(:table_name, NULL);"),
2026-01-27 02:52:02 +00:00
{"table_name": table_name}
)
2026-02-10 01:54:35 +00:00
print("clean-done")
2026-01-27 02:52:02 +00:00
return "done"
except SQLAlchemyError as e:
2026-02-10 01:54:35 +00:00
print("clean-error", e)
2026-01-27 02:52:02 +00:00
raise RuntimeError(f"Fix geometry failed: {str(e)}")
async def publish_layer(table_name: str, job_id: str):
try:
geos_link = publish_layer_to_geoserver(table_name, job_id)
2026-02-10 01:54:35 +00:00
# uuid = await publish_metadata(
# table_name=table_name,
# geoserver_links=geos_link
# )
2026-01-27 02:52:02 +00:00
2026-02-10 01:54:35 +00:00
# await update_job_status(table_name, "FINISHED", job_id)
2026-01-29 03:16:34 +00:00
# return uuid
return {
"geos_link": geos_link["layer_url"],
2026-02-10 01:54:35 +00:00
# "uuid": uuid
"uuid": "123123"
2026-01-29 03:16:34 +00:00
}
2026-01-27 02:52:02 +00:00
except Exception as e:
2026-02-10 01:54:35 +00:00
await update_job_status(table_name, "FAILED", job_id)
2026-01-27 02:52:02 +00:00
raise RuntimeError(f"Publish layer gagal: {e}") from e
2026-01-29 03:16:34 +00:00
async def upload_to_main(payload: dict):
try:
async with httpx.AsyncClient(timeout=10) as client:
response = await client.post(
MAIN_API_URL+"/api/internal/mapsets",
json=payload,
headers={
"X-SERVICE-KEY": SERVICE_KEY
}
)
response.raise_for_status()
print("GOOOOO")
return {
"status": "success",
"data": response.json()
}
except httpx.RequestError as e:
# error koneksi, DNS, timeout, dll
raise HTTPException(
status_code=500,
detail=f"Gagal connect ke MAIN_API: {str(e)}"
)
except httpx.HTTPStatusError as e:
# API tujuan balas tapi error (4xx / 5xx)
raise HTTPException(
status_code=e.response.status_code,
detail=f"MAIN_API error: {e.response.text}"
)
except Exception as e:
# fallback kalau ada error lain
raise HTTPException(
status_code=500,
detail=f"Unexpected error: {str(e)}"
)