forked from City-of-Helsinki/parkkihubi
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
The dashboard will need also some bigger areas than the current "parking areas". Let's call these regions. Add a new model Region with name, geometry and capacity_estimate fields. Add a region field to Parking model so that it's fast to find parkings of a region. The capacity estimates of the regions will be calculated based on the capacity estimates of the parking areas. Add some methods to ParkingAreaQuerySet to help calculating these estimates.
- Loading branch information
1 parent
f47f4ac
commit 25b0cdb
Showing
9 changed files
with
345 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# -*- coding: utf-8 -*- | ||
# Generated by Django 1.11.4 on 2017-09-20 02:22 | ||
from __future__ import unicode_literals | ||
|
||
import uuid | ||
|
||
import django.contrib.gis.db.models.fields | ||
import django.db.models.deletion | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('parkings', '0016_normalized_reg_num_noblank'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='Region', | ||
fields=[ | ||
('created_at', models.DateTimeField( | ||
auto_now_add=True, verbose_name='time created')), | ||
('modified_at', models.DateTimeField( | ||
auto_now=True, verbose_name='time modified')), | ||
('id', models.UUIDField( | ||
default=uuid.uuid4, editable=False, | ||
primary_key=True, serialize=False)), | ||
('name', models.CharField( | ||
blank=True, max_length=200, verbose_name='name')), | ||
('geom', django.contrib.gis.db.models.fields.MultiPolygonField( | ||
srid=3879, verbose_name='geometry')), | ||
], | ||
options={ | ||
'verbose_name_plural': 'regions', | ||
'verbose_name': 'region', | ||
}, | ||
), | ||
migrations.AddField( | ||
model_name='parkingarea', | ||
name='region', | ||
field=models.ForeignKey( | ||
blank=True, null=True, | ||
on_delete=django.db.models.deletion.CASCADE, | ||
related_name='areas', | ||
to='parkings.Region', verbose_name='region'), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import django.db.models.deletion | ||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
dependencies = [ | ||
('parkings', '0017_region'), | ||
] | ||
|
||
operations = [ | ||
migrations.RemoveField( | ||
model_name='parkingarea', | ||
name='region', ), | ||
migrations.AddField( | ||
model_name='parking', | ||
name='region', | ||
field=models.ForeignKey( | ||
blank=True, | ||
null=True, | ||
on_delete=django.db.models.deletion.SET_NULL, | ||
related_name='parkings', | ||
to='parkings.Region', | ||
verbose_name='region')), | ||
migrations.AddField( | ||
model_name='region', | ||
name='capacity_estimate', | ||
field=models.PositiveIntegerField( | ||
blank=True, null=True, verbose_name='capacity estimate')), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
from django.contrib.gis.db import models as gis_models | ||
from django.contrib.gis.db.models.functions import Intersection | ||
from django.db import models | ||
from django.db.models import Case, Count, Q, When | ||
from django.utils import timezone | ||
from django.utils.translation import ugettext_lazy as _ | ||
|
||
from .mixins import TimestampedModelMixin, UUIDPrimaryKeyMixin | ||
from .parking_area import ParkingArea | ||
|
||
|
||
class RegionQuerySet(models.QuerySet): | ||
def with_parking_count(self, at_time=None): | ||
time = at_time if at_time else timezone.now() | ||
valid_parkings_q = ( | ||
Q(parkings__time_start__lte=time) & | ||
(Q(parkings__time_end__gte=time) | Q(parkings__time_end=None))) | ||
return self.annotate( | ||
parking_count=Count(Case(When(valid_parkings_q, then=1)))) | ||
|
||
|
||
class Region(TimestampedModelMixin, UUIDPrimaryKeyMixin): | ||
name = models.CharField(max_length=200, blank=True, verbose_name=_("name")) | ||
geom = gis_models.MultiPolygonField(srid=3879, verbose_name=_("geometry")) | ||
capacity_estimate = models.PositiveIntegerField( | ||
blank=True, null=True, | ||
verbose_name=_("capacity estimate"), | ||
) | ||
|
||
objects = RegionQuerySet.as_manager() | ||
|
||
class Meta: | ||
verbose_name = _("region") | ||
verbose_name_plural = _("regions") | ||
|
||
def __str__(self): | ||
return self.name or str(_("Unnamed region")) | ||
|
||
def save(self, *args, **kwargs): | ||
self.capacity_estimate = self.calculate_capacity_estimate() | ||
super().save(*args, **kwargs) | ||
|
||
def calculate_capacity_estimate(self): | ||
""" | ||
Calculate capacity estimate of this region from parking areas. | ||
:rtype: int | ||
""" | ||
areas = ParkingArea.objects.all() | ||
covered_areas = areas.inside_region(self) | ||
partial_areas = ( | ||
areas.intersecting_region(self).exclude(pk__in=covered_areas) | ||
.annotate(intsect=Intersection('geom', self.geom))) | ||
sum_covered = covered_areas.total_estimated_capacity | ||
sum_partials = sum( | ||
# Scale the estimated capacity according to ratio of area of | ||
# the intersection and total area | ||
int(round(x.estimated_capacity * x.intsect.area / x.geom.area)) | ||
for x in partial_areas) | ||
return sum_covered + sum_partials |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import pytest | ||
|
||
from parkings.models import ParkingArea | ||
|
||
|
||
def test_str(): | ||
assert str(ParkingArea(origin_id='TEST_ID')) == 'Parking Area TEST_ID' | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_estimated_capacity(parking_area): | ||
parking_area.capacity_estimate = 123 | ||
assert parking_area.estimated_capacity == 123 | ||
parking_area.capacity_estimate = None | ||
by_area = parking_area.estimate_capacity_by_area() | ||
assert parking_area.estimated_capacity == by_area | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_estimate_capacity_by_area(parking_area): | ||
assert parking_area.estimate_capacity_by_area() == int( | ||
round(parking_area.geom.area * 0.07328)) |
Oops, something went wrong.