Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/dbc22 1884 / dbc22 1885 combined for rest stop BE and FE implementation #345

Merged
merged 3 commits into from
Mar 28, 2024
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
46 changes: 45 additions & 1 deletion src/backend/apps/feed/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
REGIONAL_WEATHER,
REGIONAL_WEATHER_AREAS,
WEBCAM,
REST_STOP,
)
from apps.feed.serializers import (
CarsEventSerializer,
Expand All @@ -23,6 +24,7 @@
RegionalWeatherSerializer,
WebcamAPISerializer,
WebcamFeedSerializer,
RestStopSerializer,
)
from django.conf import settings
from rest_framework.exceptions import ValidationError
Expand Down Expand Up @@ -86,7 +88,10 @@
},
CURRENT_WEATHER_STATIONS: {
"base_url": settings.DRIVEBC_WEATHER_CURRENT_STATIONS_API_BASE_URL
}
},
REST_STOP: {
"base_url": settings.DRIVEBC_REST_STOP_API_BASE_URL,
},
}

def _get_auth_headers(self, resource_type):
Expand Down Expand Up @@ -468,3 +473,42 @@
CURRENT_WEATHER, 'currentweather', CurrentWeatherSerializer,
{"format": "json", "limit": 500}
)

# Rest Stop
def get_rest_stop_list_feed(self, resource_type, resource_name, serializer_cls, params=None):
"""Get data feed for list of objects."""
rest_stop_api_url = settings.DRIVEBC_REST_STOP_API_BASE_URL

try:
response = requests.get(rest_stop_api_url)
response.raise_for_status()
data = response.json()
json_response = data
json_objects = []
for entry in json_response["features"]:
rest_stop_id = entry['id']
geometry = entry["geometry"]
properties = entry["properties"]
bbox = entry["bbox"]
rest_stop_data = {
'rest_stop_id': rest_stop_id,
'geometry': geometry,
'properties': properties,
'bbox': bbox,
}

serializer = serializer_cls(data=rest_stop_data,
many=isinstance(rest_stop_data, list))
json_objects.append(rest_stop_data)

except requests.RequestException as e:
return Response({"error": f"Error fetching data from rest stop API: {str(e)}"}, status=500)

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

try:
serializer.is_valid(raise_exception=True)
return json_objects

except (KeyError, ValidationError):
field_errors = serializer.errors
for field, errors in field_errors.items():
print(f"Field: {field}, Errors: {errors}")
1 change: 1 addition & 0 deletions src/backend/apps/feed/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
REGIONAL_WEATHER_AREAS = "regional_weather_areas"
CURRENT_WEATHER = "current_weather"
CURRENT_WEATHER_STATIONS = "current_weather_stations"
REST_STOP = "rest_stop"

DIRECTIONS = {
'in both directions': 'BOTH',
Expand Down
12 changes: 12 additions & 0 deletions src/backend/apps/feed/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
WebcamRegionGroupField,
)
from apps.weather.models import CurrentWeather, RegionalWeather
from apps.rest.models import RestStop
from rest_framework import serializers


Expand Down Expand Up @@ -260,3 +261,14 @@ class Meta:
'datasets',
'issuedUtc',
)

# Rest Stop serializer
class RestStopSerializer(serializers.Serializer):
class Meta:
model = RestStop
fields = (
'id',
'geometry',
'properties',
'bbox',
)
7 changes: 6 additions & 1 deletion src/backend/apps/feed/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from apps.event.tasks import populate_all_event_data
from apps.webcam.tasks import populate_all_webcam_data, update_all_webcam_data
from apps.weather.tasks import populate_all_regional_weather_data
from apps.rest.tasks import populate_all_rest_stop_data
from django.core.management import call_command
from huey import crontab
from huey.contrib.djhuey import db_periodic_task
Expand Down Expand Up @@ -34,4 +35,8 @@ def publish_scheduled():

@db_periodic_task(crontab(minute="*/5"))
def populate_regional_weather_task():
populate_all_regional_weather_data()
populate_all_regional_weather_data()

@db_periodic_task(crontab(minute="*/5"))
def populate_rest_stop_task():
populate_all_rest_stop_data()
Empty file.
10 changes: 10 additions & 0 deletions src/backend/apps/rest/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from apps.rest.models import RestStop
from django.contrib import admin
from django.contrib.admin import ModelAdmin


class RestStopAdmin(ModelAdmin):
readonly_fields = ('id', )


admin.site.register(RestStop, RestStopAdmin)
6 changes: 6 additions & 0 deletions src/backend/apps/rest/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class RestConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.rest'
31 changes: 31 additions & 0 deletions src/backend/apps/rest/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 4.2.3 on 2024-03-15 20:15

import django.contrib.gis.db.models.fields
from django.db import migrations, models


class Migration(migrations.Migration):

initial = True

dependencies = [
]

operations = [
migrations.CreateModel(
name='RestStop',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('modified_at', models.DateTimeField(auto_now=True)),
('rest_stop_id', models.CharField(max_length=100, null=True)),
('location', django.contrib.gis.db.models.fields.PointField(null=True, srid=4326)),
('geometry', models.JSONField(null=True)),
('properties', models.JSONField(null=True)),
('bbox', models.JSONField(null=True)),
],
options={
'abstract': False,
},
),
]
Empty file.
19 changes: 19 additions & 0 deletions src/backend/apps/rest/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from apps.shared.models import BaseModel
from django.contrib.gis.db import models
from django.contrib.gis.geos import Point


class RestStop(BaseModel):
rest_stop_id = models.CharField(max_length=100, null=True)
location = models.PointField(null=True)
geometry = models.JSONField(null=True)
properties = models.JSONField(null=True)
bbox = models.JSONField(null=True)

def __str__(self):
return f"Rest Stop for {self.pk}"

def save(self, *args, **kwargs):
latitude, longitude = self.geometry.get("coordinates")[0], self.geometry.get("coordinates")[1]
self.location = Point(longitude, latitude)
super().save(*args, **kwargs)
8 changes: 8 additions & 0 deletions src/backend/apps/rest/serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from apps.rest.models import RestStop
from rest_framework import serializers


class RestStopSerializer(serializers.ModelSerializer):
class Meta:
model = RestStop
exclude = ['geometry']
38 changes: 38 additions & 0 deletions src/backend/apps/rest/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import logging

from apps.feed.client import FeedClient
from apps.shared.enums import CacheKey
from apps.rest.models import RestStop
from django.core.cache import cache

logger = logging.getLogger(__name__)


def populate_rest_stop_from_data(new_rest_stop_data):
rest_stop_id = new_rest_stop_data.get('rest_stop_id')
geometry = new_rest_stop_data.get('geometry')

existing_record = RestStop.objects.filter(rest_stop_id=rest_stop_id).first()
data = {
'rest_stop_id': rest_stop_id,
'geometry': geometry,
'properties': new_rest_stop_data.get('properties'),
'bbox': new_rest_stop_data.get('bbox'),
}

if existing_record:
existing_record.__dict__.update(data)
existing_record.save()
else:
RestStop.objects.create(**data)


def populate_all_rest_stop_data():
client = FeedClient()
feed_data = client.get_rest_stop_list()

for rest_stop_data in feed_data:
populate_rest_stop_from_data(rest_stop_data)

# Rebuild cache
cache.delete(CacheKey.REGIONAL_WEATHER_LIST)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[
{
"id": 2,
"created_at": "2024-03-15T11:10:29.518958-07:00",
"modified_at": "2024-03-15T11:10:29.518958-07:00",
"rest_stop_id": "DBC_RIM_REST_AREA_V.fid-59dfb4f6_18e433c4f15_-52d9",
"geometry": {
"type": "Point",
"coordinates": [
54.66828166,
-126.99686259
]
},
"properties": {
"WI_FI": "No",
"OBJECTID": 4381,
"OPEN_DATE": null,
"CLOSE_DATE": null,
"POWER_TYPE": "No Power",
"TOILET_TYPE": "Pit",
"DIRECT_ACCESS": "Yes",
"EVENT_LOCATION": 2.129,
"HIGHWAY_NUMBER": "16",
"REST_AREA_NAME": "BULKLEY VIEW",
"ADMIN_UNIT_CODE": "425",
"ADMIN_UNIT_NAME": "Bulkley Nass SA",
"OPEN_YEAR_ROUND": "Yes",
"REST_AREA_CLASS": "RAM Class C",
"NUMBER_OF_TABLES": 4,
"REST_AREA_NUMBER": "R0146",
"ACCELERATION_LANE": "No",
"DECELERATION_LANE": "Yes",
"NUMBER_OF_TOILETS": 1,
"ACCESS_RESTRICTION": "Westbound",
"CHRIS_REST_AREA_ID": "1532673",
"DIRECTION_OF_TRAFFIC": "Eastbound",
"POWER_RESPONSIBILITY": "Not Applicable",
"EV_STATION_25_KW_DCFC": 0,
"EV_STATION_50_KW_DCFC": 0,
"CROSS_SECTION_POSITION": "Right of Way - Right",
"ACCOM_COMMERCIAL_TRUCKS": "Yes",
"CHRIS_ANCHOR_SECTION_ID": 1313674,
"EV_STATION_LEVEL_2_J1772": 0,
"WHEELCHAIR_ACCESS_TOILET": "Yes",
"ASSOCIATED_NUMBERED_ROUTE": "16",
"DISTANCE_FROM_MUNICIPALITY": "4.5 KM EAST OF TELKWA",
"NUMBER_OF_STANDARD_BARRELS": 0,
"NUMBER_OF_BEAR_PROOF_BARRELS": 6
},
"bbox": [
-126.99686259,
54.66828166,
-126.99686259,
54.66828166
]
}
]
Loading
Loading