Persist station CRUD across restarts via JSON config
Station CRUD via the API previously mutated the scraper's in-memory station_mapping only, so changes were lost on restart (and the systemd service auto-restarts). - Extract the 130-line hardcoded station_mapping into bundled defaults at src/data/stations.json; the scraper loads from a runtime-writable config file (STATION_CONFIG_PATH, default stations.json) and falls back to the bundled defaults to seed it. - Add scraper.save_stations() with an atomic temp-file + os.replace write. - create/update/delete station endpoints now persist and roll back the in-memory change if the write fails; re-raise HTTPException so persistence errors surface as real 500s instead of being swallowed. - Backend-agnostic (works for the VictoriaMetrics deployment, which has no relational stations table). Runtime stations.json is gitignored. Also clears pre-existing flake8 debt in water_scraper_v3.py (unused imports, long lines, duplicate logging import) and dedupes the User-Agent to Config.USER_AGENT.
This commit is contained in:
+22
-2
@@ -353,8 +353,9 @@ async def create_station(station: StationCreateModel):
|
||||
existing_ids = [int(sid) for sid in scraper.station_mapping.keys()]
|
||||
new_station_id = max(existing_ids) + 1 if existing_ids else 1
|
||||
|
||||
# Add to station mapping
|
||||
scraper.station_mapping[str(new_station_id)] = {
|
||||
# Add to station mapping and persist
|
||||
new_key = str(new_station_id)
|
||||
scraper.station_mapping[new_key] = {
|
||||
"code": station.station_code,
|
||||
"thai_name": station.thai_name,
|
||||
"english_name": station.english_name,
|
||||
@@ -362,6 +363,9 @@ async def create_station(station: StationCreateModel):
|
||||
"longitude": station.longitude,
|
||||
"geohash": station.geohash,
|
||||
}
|
||||
if not scraper.save_stations():
|
||||
scraper.station_mapping.pop(new_key, None)
|
||||
raise HTTPException(status_code=500, detail="Failed to persist new station")
|
||||
|
||||
logger.info(f"Created new station: {station.station_code} ({station.english_name})")
|
||||
|
||||
@@ -376,6 +380,8 @@ async def create_station(station: StationCreateModel):
|
||||
status=station.status,
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating station: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -396,6 +402,7 @@ async def update_station(station_id: int, updates: StationUpdateModel):
|
||||
|
||||
try:
|
||||
station_info = scraper.station_mapping[station_key]
|
||||
original = dict(station_info) # snapshot for rollback if persistence fails
|
||||
|
||||
# Update fields if provided
|
||||
if updates.thai_name is not None:
|
||||
@@ -409,6 +416,10 @@ async def update_station(station_id: int, updates: StationUpdateModel):
|
||||
if updates.geohash is not None:
|
||||
station_info["geohash"] = updates.geohash
|
||||
|
||||
if not scraper.save_stations():
|
||||
scraper.station_mapping[station_key] = original
|
||||
raise HTTPException(status_code=500, detail="Failed to persist station update")
|
||||
|
||||
logger.info(f"Updated station {station_id}: {station_info['code']}")
|
||||
|
||||
return StationResponse(
|
||||
@@ -422,6 +433,8 @@ async def update_station(station_id: int, updates: StationUpdateModel):
|
||||
status=updates.status or "active",
|
||||
)
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating station {station_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
@@ -442,10 +455,17 @@ async def delete_station(station_id: int):
|
||||
|
||||
try:
|
||||
station_info = scraper.station_mapping.pop(station_key)
|
||||
|
||||
if not scraper.save_stations():
|
||||
scraper.station_mapping[station_key] = station_info # restore
|
||||
raise HTTPException(status_code=500, detail="Failed to persist station deletion")
|
||||
|
||||
logger.info(f"Deleted station {station_id}: {station_info['code']}")
|
||||
|
||||
return {"message": f"Station {station_info['code']} deleted successfully"}
|
||||
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting station {station_id}: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
Reference in New Issue
Block a user