Spaces:
Running
Running
deploy: ban fields on Positions
Browse files- backend/models.py +18 -2
- backend/services/maps_service.py +21 -7
backend/models.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
-
from typing import Optional, List, Union
|
| 3 |
|
| 4 |
|
| 5 |
class CategoryBase(BaseModel):
|
|
@@ -93,6 +93,8 @@ class Street(BaseModel):
|
|
| 93 |
district_id: Optional[int] = None
|
| 94 |
city_id: Optional[int] = None
|
| 95 |
positions: Optional[list] = None
|
|
|
|
|
|
|
| 96 |
created_at: Optional[str] = None
|
| 97 |
|
| 98 |
class Config:
|
|
@@ -121,6 +123,9 @@ class StreetUpdate(BaseModel):
|
|
| 121 |
type: Optional[str] = None
|
| 122 |
district_id: Optional[int] = None
|
| 123 |
city_id: Optional[int] = None
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
|
| 126 |
# ==================== POSITIONS MODEL ====================
|
|
@@ -137,7 +142,10 @@ class Position(BaseModel):
|
|
| 137 |
lane: Optional[int] = None
|
| 138 |
tool: Optional[int] = None
|
| 139 |
flooding: Optional[bool] = None
|
| 140 |
-
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
class Config:
|
| 143 |
from_attributes = True
|
|
@@ -153,6 +161,10 @@ class PositionCreate(BaseModel):
|
|
| 153 |
lane: Optional[int] = None
|
| 154 |
tool: Optional[int] = None
|
| 155 |
flooding: Optional[bool] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 156 |
|
| 157 |
|
| 158 |
class PositionUpdate(BaseModel):
|
|
@@ -165,6 +177,10 @@ class PositionUpdate(BaseModel):
|
|
| 165 |
lane: Optional[int] = None
|
| 166 |
tool: Optional[int] = None
|
| 167 |
flooding: Optional[bool] = None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
|
| 169 |
|
| 170 |
# ==================== GROUPS MODEL ====================
|
|
|
|
| 1 |
from pydantic import BaseModel
|
| 2 |
+
from typing import Optional, List, Union, Any
|
| 3 |
|
| 4 |
|
| 5 |
class CategoryBase(BaseModel):
|
|
|
|
| 93 |
district_id: Optional[int] = None
|
| 94 |
city_id: Optional[int] = None
|
| 95 |
positions: Optional[list] = None
|
| 96 |
+
toll: Optional[bool] = None
|
| 97 |
+
flooding: Optional[bool] = None
|
| 98 |
created_at: Optional[str] = None
|
| 99 |
|
| 100 |
class Config:
|
|
|
|
| 123 |
type: Optional[str] = None
|
| 124 |
district_id: Optional[int] = None
|
| 125 |
city_id: Optional[int] = None
|
| 126 |
+
positions: Optional[list] = None
|
| 127 |
+
toll: Optional[bool] = None
|
| 128 |
+
flooding: Optional[bool] = None
|
| 129 |
|
| 130 |
|
| 131 |
# ==================== POSITIONS MODEL ====================
|
|
|
|
| 142 |
lane: Optional[int] = None
|
| 143 |
tool: Optional[int] = None
|
| 144 |
flooding: Optional[bool] = None
|
| 145 |
+
ban_turn: Optional[Any] = None # cαΊ₯m rαΊ½
|
| 146 |
+
ban_vehicle: Optional[Any] = None # cαΊ₯m phΖ°Ζ‘ng tiα»n
|
| 147 |
+
ban_hours: Optional[Any] = None # cαΊ₯m giα»
|
| 148 |
+
ban_weight: Optional[Any] = None # cαΊ₯m tαΊ£i trα»ng
|
| 149 |
|
| 150 |
class Config:
|
| 151 |
from_attributes = True
|
|
|
|
| 161 |
lane: Optional[int] = None
|
| 162 |
tool: Optional[int] = None
|
| 163 |
flooding: Optional[bool] = None
|
| 164 |
+
ban_turn: Optional[Any] = None
|
| 165 |
+
ban_vehicle: Optional[Any] = None
|
| 166 |
+
ban_hours: Optional[Any] = None
|
| 167 |
+
ban_weight: Optional[Any] = None
|
| 168 |
|
| 169 |
|
| 170 |
class PositionUpdate(BaseModel):
|
|
|
|
| 177 |
lane: Optional[int] = None
|
| 178 |
tool: Optional[int] = None
|
| 179 |
flooding: Optional[bool] = None
|
| 180 |
+
ban_turn: Optional[Any] = None
|
| 181 |
+
ban_vehicle: Optional[Any] = None
|
| 182 |
+
ban_hours: Optional[Any] = None
|
| 183 |
+
ban_weight: Optional[Any] = None
|
| 184 |
|
| 185 |
|
| 186 |
# ==================== GROUPS MODEL ====================
|
backend/services/maps_service.py
CHANGED
|
@@ -33,7 +33,10 @@ class MapsService:
|
|
| 33 |
'tool': item.get('tool'),
|
| 34 |
'flooding': item.get('flooding'),
|
| 35 |
'streets': item.get('streets'),
|
| 36 |
-
'
|
|
|
|
|
|
|
|
|
|
| 37 |
}
|
| 38 |
@staticmethod
|
| 39 |
async def get_all_cities() -> List[City]:
|
|
@@ -208,7 +211,7 @@ class MapsService:
|
|
| 208 |
@staticmethod
|
| 209 |
async def get_street_by_id(street_id: int) -> Optional[Street]:
|
| 210 |
try:
|
| 211 |
-
result = supabase.table("Streets").select("
|
| 212 |
return Street(**result.data[0]) if result.data else None
|
| 213 |
except Exception as e:
|
| 214 |
raise Exception(f"Failed to fetch street: {str(e)}")
|
|
@@ -216,8 +219,16 @@ class MapsService:
|
|
| 216 |
@staticmethod
|
| 217 |
async def update_street(street_id: int, data: StreetUpdate) -> Street:
|
| 218 |
try:
|
| 219 |
-
|
| 220 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
return Street(**result.data[0])
|
| 222 |
except Exception as e:
|
| 223 |
raise Exception(f"Failed to update street: {str(e)}")
|
|
@@ -415,7 +426,8 @@ class MapsService:
|
|
| 415 |
# include streets JSON if provided on model
|
| 416 |
if getattr(data, 'street_id', None):
|
| 417 |
payload['streets'] = [data.street_id]
|
| 418 |
-
for field in ("ban", "speed", "park", "lane", "tool", "flooding"
|
|
|
|
| 419 |
val = getattr(data, field)
|
| 420 |
if val is not None:
|
| 421 |
payload[field] = val
|
|
@@ -473,6 +485,8 @@ class MapsService:
|
|
| 473 |
lon = float(pt.get('lon'))
|
| 474 |
streets = [int(s) for s in (pt.get('streets') or [])]
|
| 475 |
pos_id = pt.get('pos_id')
|
|
|
|
|
|
|
| 476 |
|
| 477 |
if pos_id:
|
| 478 |
# ββ UPDATE existing position ββββββββββββββββββββββββββββββββββ
|
|
@@ -481,7 +495,7 @@ class MapsService:
|
|
| 481 |
existing_pos = supabase.table("Positions").select("streets").eq("id", pos_id).limit(1).execute()
|
| 482 |
old_streets = list((existing_pos.data[0].get('streets') or []) if existing_pos.data else [])
|
| 483 |
|
| 484 |
-
result = supabase.table("Positions").update({"long": lon, "lat": lat, "streets": streets}).eq("id", pos_id).execute()
|
| 485 |
if result.data:
|
| 486 |
created.append(result.data[0])
|
| 487 |
|
|
@@ -510,7 +524,7 @@ class MapsService:
|
|
| 510 |
else:
|
| 511 |
# ββ INSERT new position βββββββββββββββββββββββββββββββββββββββ
|
| 512 |
next_id = _next_id("Positions")
|
| 513 |
-
payload = {"id": next_id, "long": lon, "lat": lat, "streets": streets}
|
| 514 |
result = supabase.table("Positions").insert(payload).execute()
|
| 515 |
if not result.data:
|
| 516 |
raise Exception("Failed to insert position")
|
|
|
|
| 33 |
'tool': item.get('tool'),
|
| 34 |
'flooding': item.get('flooding'),
|
| 35 |
'streets': item.get('streets'),
|
| 36 |
+
'ban_turn': item.get('ban_turn'),
|
| 37 |
+
'ban_vehicle': item.get('ban_vehicle'),
|
| 38 |
+
'ban_hours': item.get('ban_hours'),
|
| 39 |
+
'ban_weight': item.get('ban_weight'),
|
| 40 |
}
|
| 41 |
@staticmethod
|
| 42 |
async def get_all_cities() -> List[City]:
|
|
|
|
| 211 |
@staticmethod
|
| 212 |
async def get_street_by_id(street_id: int) -> Optional[Street]:
|
| 213 |
try:
|
| 214 |
+
result = supabase.table("Streets").select("*").eq("id", street_id).execute()
|
| 215 |
return Street(**result.data[0]) if result.data else None
|
| 216 |
except Exception as e:
|
| 217 |
raise Exception(f"Failed to fetch street: {str(e)}")
|
|
|
|
| 219 |
@staticmethod
|
| 220 |
async def update_street(street_id: int, data: StreetUpdate) -> Street:
|
| 221 |
try:
|
| 222 |
+
raw = {k: v for k, v in data.model_dump().items() if v is not None}
|
| 223 |
+
optional = {"toll", "flooding"}
|
| 224 |
+
core = {k: v for k, v in raw.items() if k not in optional}
|
| 225 |
+
try:
|
| 226 |
+
result = supabase.table("Streets").update(raw).eq("id", street_id).execute()
|
| 227 |
+
except Exception:
|
| 228 |
+
# toll/flooding columns may not exist yet β save core fields only
|
| 229 |
+
result = supabase.table("Streets").update(core or raw).eq("id", street_id).execute()
|
| 230 |
+
if not result.data:
|
| 231 |
+
raise Exception(f"Street #{street_id} not found or update failed")
|
| 232 |
return Street(**result.data[0])
|
| 233 |
except Exception as e:
|
| 234 |
raise Exception(f"Failed to update street: {str(e)}")
|
|
|
|
| 426 |
# include streets JSON if provided on model
|
| 427 |
if getattr(data, 'street_id', None):
|
| 428 |
payload['streets'] = [data.street_id]
|
| 429 |
+
for field in ("ban", "speed", "park", "lane", "tool", "flooding",
|
| 430 |
+
"ban_turn", "ban_vehicle", "ban_hours", "ban_weight"):
|
| 431 |
val = getattr(data, field)
|
| 432 |
if val is not None:
|
| 433 |
payload[field] = val
|
|
|
|
| 485 |
lon = float(pt.get('lon'))
|
| 486 |
streets = [int(s) for s in (pt.get('streets') or [])]
|
| 487 |
pos_id = pt.get('pos_id')
|
| 488 |
+
# Optional ban fields (only written when the key is present in payload)
|
| 489 |
+
ban_fields = {k: pt[k] for k in ("ban_turn", "ban_vehicle", "ban_hours", "ban_weight") if k in pt}
|
| 490 |
|
| 491 |
if pos_id:
|
| 492 |
# ββ UPDATE existing position ββββββββββββββββββββββββββββββββββ
|
|
|
|
| 495 |
existing_pos = supabase.table("Positions").select("streets").eq("id", pos_id).limit(1).execute()
|
| 496 |
old_streets = list((existing_pos.data[0].get('streets') or []) if existing_pos.data else [])
|
| 497 |
|
| 498 |
+
result = supabase.table("Positions").update({"long": lon, "lat": lat, "streets": streets, **ban_fields}).eq("id", pos_id).execute()
|
| 499 |
if result.data:
|
| 500 |
created.append(result.data[0])
|
| 501 |
|
|
|
|
| 524 |
else:
|
| 525 |
# ββ INSERT new position βββββββββββββββββββββββββββββββββββββββ
|
| 526 |
next_id = _next_id("Positions")
|
| 527 |
+
payload = {"id": next_id, "long": lon, "lat": lat, "streets": streets, **ban_fields}
|
| 528 |
result = supabase.table("Positions").insert(payload).execute()
|
| 529 |
if not result.data:
|
| 530 |
raise Exception("Failed to insert position")
|