Skip to content
This repository has been archived by the owner on Aug 28, 2023. It is now read-only.

Commit

Permalink
Move dynamic checking of which datasets a City supports to a DB field
Browse files Browse the repository at this point in the history
The current dynamic check is not working very well. It checks for the
presence of map cells and reports their dataset names, but doesn't check
the values for those cells.
In practice, this means that a location without LOCA data will be reported
as supporting LOCA data, as the ingest will create LOCA cells, but fill
them with NaN (not a number) values.

Since checking the values in each cell would be computationally expensive,
we've instead moved the list of supported datasets to be stored in the
database. A future enhancement (#823) will ensure that this is properly
set for every existing city.

Closes #822
  • Loading branch information
maurizi authored and ddohler committed Aug 21, 2023
1 parent d9fd3b2 commit 8eff5eb
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 10 deletions.
1 change: 1 addition & 0 deletions django/climate_change_api/climate_change_api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.gis',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
Expand Down
10 changes: 10 additions & 0 deletions django/climate_change_api/climate_data/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from django.contrib import admin

from climate_data.models import City


class CityAdmin(admin.ModelAdmin):
exclude = ('_geog',)


admin.site.register(City, CityAdmin)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.8 on 2018-06-27 20:16
from __future__ import unicode_literals

import climate_data.models
import django.contrib.postgres.fields
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('climate_data', '0065_scenario_alias'),
]

operations = [
migrations.AlterModelOptions(
name='city',
options={'verbose_name_plural': 'cities'},
),
migrations.AddField(
model_name='city',
name='datasets',
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(choices=[('LOCA', 'LOCA'), ('NEX-GDDP', 'NEX-GDDP')], max_length=48), default=climate_data.models.get_datasets, size=2),
),
migrations.AlterField(
model_name='climatedataset',
name='name',
field=models.CharField(choices=[('LOCA', 'LOCA'), ('NEX-GDDP', 'NEX-GDDP')], max_length=48, unique=True),
),
]
29 changes: 27 additions & 2 deletions django/climate_change_api/climate_data/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib.gis.db import models
from django.contrib.postgres.fields.array import ArrayField
from django.core.exceptions import ObjectDoesNotExist
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db.models import CASCADE, SET_NULL

from climate_data.geo_boundary import census
Expand All @@ -16,10 +16,23 @@ def db_type(self, connection):
return models.SmallIntegerField().db_type(connection=connection)


def get_datasets():
return [ClimateDataset.Datasets.LOCA, ClimateDataset.Datasets.NEX_GDDP]


class ClimateDataset(models.Model):
"""Model representing a particular climate projection dataset."""

name = models.CharField(max_length=48, unique=True)
class Datasets:
LOCA = 'LOCA'
NEX_GDDP = 'NEX-GDDP'

CHOICES = (
(LOCA, LOCA),
(NEX_GDDP, NEX_GDDP),
)

name = models.CharField(max_length=48, unique=True, choices=Datasets.CHOICES)
label = models.CharField(max_length=128, blank=True, null=True)
description = models.CharField(max_length=4096, blank=True, null=True)
url = models.URLField(blank=True, null=True)
Expand Down Expand Up @@ -286,6 +299,12 @@ class City(models.Model):
is_coastal = models.BooleanField(default=False)
population = models.IntegerField(null=True)

datasets = ArrayField(
models.CharField(max_length=48, choices=ClimateDataset.Datasets.CHOICES),
size=2,
default=get_datasets
)

region = models.ForeignKey(Region, on_delete=SET_NULL, null=True)

objects = CityManager()
Expand All @@ -296,6 +315,7 @@ def __str__(self):

class Meta:
unique_together = ('name', 'admin')
verbose_name_plural = 'cities'

def get_map_cell(self, dataset):
"""Get the map cell for a given dataset for a given city.
Expand All @@ -309,6 +329,11 @@ def get_map_cell(self, dataset):
def natural_key(self):
return (self.name, self.admin)

def clean(self):
super().clean()
if len(self.datasets) != len(set(self.datasets)):
raise ValidationError({'datasets': 'Cannot contain duplicate datasets'})

def save(self, *args, **kwargs):
"""Override save to keep the geography field up to date."""
self._geog = self.geom
Expand Down
5 changes: 0 additions & 5 deletions django/climate_change_api/climate_data/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,6 @@ class Meta:

class CitySerializer(GeoFeatureModelSerializer):

datasets = serializers.SerializerMethodField()

def get_datasets(self, obj):
return [map_cell.dataset.name for map_cell in obj.map_cell_set.select_related('dataset')]

proximity = serializers.SerializerMethodField()

def get_proximity(self, obj):
Expand Down
4 changes: 1 addition & 3 deletions django/climate_change_api/climate_data/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,7 @@ def datasets(self, request, pk=None):
Returns 404 if the city object has no valid map cells.
"""
city = self.get_object()
map_cells = ClimateDataCityCell.objects.filter(city=city)
response = [map_cell.dataset.name for map_cell in map_cells]
return Response(response, status=status.HTTP_200_OK)
return Response(city.datasets, status=status.HTTP_200_OK)


class CityMapCellListView(APIView):
Expand Down

0 comments on commit 8eff5eb

Please sign in to comment.