Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions src/backend/apps/consumer/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ async def on_message(message: aio_pika.IncomingMessage):
filename = message.headers.get("filename", "unknown.jpg")
camera_id = filename.split("_")[0].split(".")[0]
timestamp_utc = message.headers.get("timestamp", "unknown")
camera_status = calculate_camera_status(timestamp_utc)
camera_status = calculate_camera_status(camera_id, timestamp_utc)

try:
timestamp_local = await sync_to_async(safe_db_call)(
Expand Down Expand Up @@ -254,7 +254,7 @@ async def process_message(message: aio_pika.IncomingMessage):
filename = message.headers.get("filename", "unknown.jpg")
camera_id = filename.split("_")[0].split(".")[0]
timestamp_utc = message.headers.get("timestamp", "unknown")
camera_status = calculate_camera_status(timestamp_utc)
camera_status = await sync_to_async(calculate_camera_status)(camera_id, timestamp_utc)

timestamp_local = await sync_to_async(safe_db_call)(
generate_local_timestamp, db_data, camera_id, timestamp_utc
Expand Down Expand Up @@ -550,7 +550,11 @@ def update_webcam(camera_id, timestamp, webcam):
region_id=region_id
).first().seq if region_id else None

camera, created = Webcam.objects.get_or_create(
time_now_utc = timestamp.strftime("%Y%m%d%H%M%S%f")[:-3]
camera_status = calculate_camera_status(camera_id, time_now_utc)


camera, created = Webcam.objects.update_or_create(
id=camera_id,
defaults={
"name": f"{webcam.get('cam_internet_name', '')}",
Expand All @@ -565,15 +569,16 @@ def update_webcam(camera_id, timestamp, webcam):
"orientation": webcam.get('cam_locations_orientation', ''),
"elevation": elevation,
"dbc_mark": webcam.get('dbc_mark', ''),
"update_period_mean": webcam.get('update_period_mean', 300),
"update_period_stddev": webcam.get('update_period_stddev', 60),
"update_period_mean": camera_status["mean_interval"],
"update_period_stddev": camera_status["stddev_interval"],
"marked_stale": camera_status["stale"],
"marked_delayed": camera_status["delayed"],
"https_cam": True,
"is_on": webcam.get('is_on'),
"last_update_modified": timestamp,
"last_update_attempt": timestamp,
}
)

camera.https_cam = True
camera.is_on = webcam.get('is_on')
camera.last_update_modified = timestamp
camera.last_update_attempt = timestamp
group_id = Webcam.objects.filter(location=camera.location).order_by('id').first().id
camera.group_id = group_id
camera.save()
Expand Down
6 changes: 4 additions & 2 deletions src/backend/apps/shared/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
timestamp_list: deque[Union[str, float]] = deque(maxlen=max_samples)

# @sync_to_async
def get_recent_timestamps(camera_id: int):
def get_recent_timestamps(camera_id: str):
latest_50 = ImageIndex.objects.filter(camera_id=str(camera_id)).order_by('-timestamp')[:50]
timestamp_list.clear()

Expand All @@ -43,10 +43,11 @@ def migrate_timestamp_list():
def calculate_stddev(values: List[float]) -> float:
return stdev(values) if len(values) > 1 else 0.0

def calculate_camera_status(timestamp_str: str) -> tuple[float, float]:
def calculate_camera_status(camera_id: str, timestamp_str: str) -> tuple[float, float]:
"""
Add new timestamp and calculate median/stddev of update periods.
Args:
camera_id: Camera ID
timestamp_str: Timestamp string in format 'YYYYMMDDHHMMSSFFF'
Returns:
timestamp: Current timestamp in milliseconds
Expand All @@ -57,6 +58,7 @@ def calculate_camera_status(timestamp_str: str) -> tuple[float, float]:
"""
# Step 1: parse latest timestamp
new_ts = parse_timestamp(timestamp_str)
get_recent_timestamps(camera_id) # Ensure timestamp_list is populated with latest data

# Step 2: ensure all timestamps are float
migrate_timestamp_list()
Expand Down
5 changes: 5 additions & 0 deletions src/backend/apps/webcam/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@
'caption',
'is_on',
'should_appear',
'marked_stale',
'marked_delayed',
]

CAMERA_FIELD_MAPPING = {
'name': 'cam_internetname',
'caption': 'cam_internetcaption',
'is_on': 'isOn',
'should_appear': 'should_appear',
'marked_stale': 'stale',
'marked_delayed': 'delayed',

}
25 changes: 18 additions & 7 deletions src/backend/apps/webcam/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,10 @@ def populate_webcam_from_data(webcam_data):
return


def update_webcam_db_stale_delayed(camera: Webcam):
async def update_webcam_db_stale_delayed(camera: Webcam):
time_now_utc = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%d%H%M%S%f")[:-3]
camera_status = calculate_camera_status(time_now_utc)
get_recent_timestamps(camera.id) # Ensure timestamp_list is populated with latest data
camera_status = calculate_camera_status(camera.id, time_now_utc)
ts_seconds = int(camera_status["timestamp"])
dt_utc = datetime.datetime.fromtimestamp(ts_seconds, tz=ZoneInfo("UTC"))

Expand Down Expand Up @@ -161,7 +162,7 @@ def format_region_name(region_name):
def update_webcam_db(cam_id: int, cam_data: dict):
is_updated = False
time_now_utc = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%d%H%M%S%f")[:-3]
camera_status = calculate_camera_status(time_now_utc)
camera_status = calculate_camera_status(cam_id, time_now_utc)
ts_seconds = int(camera_status["timestamp"])
dt_utc = datetime.datetime.fromtimestamp(ts_seconds, tz=ZoneInfo("UTC"))

Expand All @@ -172,7 +173,10 @@ def update_webcam_db(cam_id: int, cam_data: dict):
for field in CAMERA_DIFF_FIELDS:
source_field = CAMERA_FIELD_MAPPING.get(field, field)
webcam_value = getattr(existing_webcam, field)
source_value = cam_data.get(source_field)
if field in ['marked_stale', 'marked_delayed']:
source_value = camera_status[source_field]
else:
source_value = cam_data.get(source_field)
if webcam_value != source_value:
Webcam.objects.filter(id=cam_id).update(
is_on=True if cam_data.get("isOn") == 1 else False,
Expand All @@ -182,8 +186,12 @@ def update_webcam_db(cam_id: int, cam_data: dict):
is_new=cam_data.get("isNew"),
is_on_demand=cam_data.get("isOnDemand"),
last_update_attempt=dt_utc,
last_update_modified=dt_utc
)
last_update_modified=dt_utc,
update_period_mean=camera_status["mean_interval"],
update_period_stddev=camera_status["stddev_interval"],
marked_stale=camera_status["stale"],
marked_delayed=camera_status["delayed"],
)
is_updated = True
return is_updated

Expand All @@ -192,11 +200,14 @@ def create_webcam_db(cam_data: dict):

try:
time_now_utc = datetime.datetime.now(datetime.timezone.utc).strftime("%Y%m%d%H%M%S%f")[:-3]
camera_status = calculate_camera_status(time_now_utc)
camera_status = calculate_camera_status(cam_id, time_now_utc)
ts_seconds = int(camera_status["timestamp"])
dt_utc = datetime.datetime.fromtimestamp(ts_seconds, tz=ZoneInfo("UTC"))

region_obj = Region.objects.using("mssql").filter(id=cam_data.cam_locationsregion).first()
if not region_obj:
logger.error(f"Region not found for camera {cam_id}, skipping webcam creation.")
return None, False
region_id = region_obj.seq if region_obj else None
raw_hw = cam_data.cam_locationshighway
highway_group = RegionHighway.objects.using("mssql").filter(highway_id=raw_hw).first().seq
Expand Down
2 changes: 1 addition & 1 deletion src/backend/apps/webcam/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def _timelapse_impl(self, request, pk):
timestamps = get_image_list(pk, "TIMELAPSE_HOURS")

# If no filter params provided, return full list unfiltered
if not any([from_date_str, to_date_str, from_time_str, to_time_str, timezone_str]):
if not any([from_date_str, to_date_str, from_time_str, to_time_str]):
return Response(timestamps, status=status.HTTP_200_OK)

tz = ZoneInfo(timezone_str)
Expand Down
Loading