Skip to content

Commit

Permalink
unicef-locations
Browse files Browse the repository at this point in the history
  • Loading branch information
domdinicola committed Mar 14, 2022
1 parent 96b1661 commit 7709496
Show file tree
Hide file tree
Showing 10 changed files with 683 additions and 639 deletions.
7 changes: 4 additions & 3 deletions django_api/Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,10 @@ python-social-auth = "<=0.3.6"
pyrestcli = "<=0.6.11"
requests = "<=2.26"
sentry-sdk = "<=1.3.1"
social-auth-app-django = "<=4.0"
social-auth-core = "<=4.1"
unicef-notification = "<=0.2.1"
social-auth-app-django = "<=5.0"
social-auth-core = "<=5.0"
unicef-notification = "<=1.1"
unicef-locations = "<=4.0"
uWSGI = "<=2.0.19.1"
weasyprint = "<=53.0"

Expand Down
1,025 changes: 585 additions & 440 deletions django_api/Pipfile.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions django_api/etools_prp/apps/core/admin.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from django.contrib.gis import admin

from leaflet.admin import LeafletGeoAdmin
from unicef_locations.models import CartoDBTable

from .cartodb import update_sites_from_cartodb
from .forms import CartoDBTableForm
from .models import CartoDBTable, Location, PRPRole, ResponsePlan, Workspace
from .models import Location, PRPRole, ResponsePlan, Workspace


class LocationAdmin(LeafletGeoAdmin, admin.ModelAdmin):
Expand Down Expand Up @@ -57,7 +58,6 @@ def import_sites(self, request, queryset):
class WorkspaceAdmin(admin.ModelAdmin):
list_display = ('title', 'workspace_code', 'business_area_code',
'external_id')
list_filter = ('countries',)
search_fields = ('title', 'workspace_code', 'business_area_code',
'external_id')

Expand All @@ -78,5 +78,6 @@ class PRPRoleAdmin(admin.ModelAdmin):
admin.site.register(Workspace, WorkspaceAdmin)
admin.site.register(Location, LocationAdmin)
admin.site.register(ResponsePlan, ResponsePlanAdmin)
admin.site.unregister(CartoDBTable)
admin.site.register(CartoDBTable, CartoDBTableAdmin)
admin.site.register(PRPRole, PRPRoleAdmin)
3 changes: 2 additions & 1 deletion django_api/etools_prp/apps/core/cartodb.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
from celery import shared_task
from celery.utils.log import get_task_logger
from pyrestcli.auth import BaseAuthClient
from unicef_locations.models import CartoDBTable

from etools_prp.apps.core.models import CartoDBTable, Location
from etools_prp.apps.core.models import Location

logger = get_task_logger('core.cartodb')

Expand Down
2 changes: 1 addition & 1 deletion django_api/etools_prp/apps/core/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

from carto.exceptions import CartoException
from carto.sql import SQLClient
from unicef_locations.models import CartoDBTable

from etools_prp.apps.core.cartodb import EtoolsCartoNoAuthClient
from etools_prp.apps.core.models import CartoDBTable

logger = logging.getLogger('locations.models')

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Generated by Django 3.2.6 on 2022-02-23 23:23

import django.contrib.gis.db.models.fields
from django.db import migrations, models
import django.utils.timezone
import model_utils.fields


class Migration(migrations.Migration):

dependencies = [
('core', '0017_alter_location_unique_together'),
]

operations = [
migrations.AlterUniqueTogether(
name='gatewaytype',
unique_together=None,
),
migrations.RemoveField(
model_name='gatewaytype',
name='country',
),
migrations.RemoveField(
model_name='workspace',
name='countries',
),
migrations.AddField(
model_name='location',
name='created',
field=model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created'),
),
migrations.AddField(
model_name='location',
name='is_active',
field=models.BooleanField(blank=True, default=True, verbose_name='Active'),
),
migrations.AddField(
model_name='location',
name='modified',
field=model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified'),
),
migrations.AlterField(
model_name='location',
name='geom',
field=django.contrib.gis.db.models.fields.MultiPolygonField(blank=True, null=True, srid=4326, verbose_name='Geo Point'),
),
migrations.AlterField(
model_name='location',
name='point',
field=django.contrib.gis.db.models.fields.PointField(blank=True, null=True, srid=4326, verbose_name='Point'),
),
migrations.DeleteModel(
name='Country',
),
migrations.DeleteModel(
name='GatewayType',
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 3.2.6 on 2022-03-01 00:39

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('core', '0018_auto_20220223_2323'),
]

operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.DeleteModel(name='CartoDBTable'),
],
database_operations=[
]
)
]
192 changes: 2 additions & 190 deletions django_api/etools_prp/apps/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,11 @@
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db.models import Q
from django.utils.functional import cached_property
from django.utils.translation import gettext as _

import mptt
import pycountry
from model_utils.models import TimeStampedModel
from mptt.managers import TreeManager
from mptt.models import MPTTModel, TreeForeignKey
from unicef_locations.models import AbstractLocation

from etools_prp.apps.core.countries import COUNTRY_NAME_TO_ALPHA2_CODE
from etools_prp.apps.utils.emails import send_email_from_template

from .common import EXTERNAL_DATA_SOURCES, INDICATOR_REPORT_STATUS, OVERALL_STATUS, PRP_ROLE_TYPES, RESPONSE_PLAN_TYPE
Expand Down Expand Up @@ -69,56 +65,6 @@ class Meta:
unique_together = ('external_id', 'external_source')


# TODO remove me
class Country(TimeStampedModel):
"""
Represents a country which has many offices and sections.
Taken from https://github.com/unicef/etools/blob/master/EquiTrack/users/models.py
on Sep. 14, 2017.
"""
name = models.CharField(max_length=100)
iso3_code = models.CharField(
max_length=10,
blank=True,
default='',
verbose_name=_("ISO3 Code"),
)
country_short_code = models.CharField(
max_length=10,
null=True,
blank=True
)
long_name = models.CharField(max_length=255, null=True, blank=True)

class Meta:
ordering = ['name']
verbose_name_plural = 'Countries'

def __str__(self):
return self.name

@property
def details(self):
"""
Tries to retrieve a usable country reference
:return: pycountry Country object or None
"""
lookup = None

if not self.country_short_code:
lookup = {'alpha_2': COUNTRY_NAME_TO_ALPHA2_CODE.get(self.name, None)}
elif len(self.country_short_code) == 3:
lookup = {'alpha_3': self.country_short_code}
elif len(self.country_short_code) == 2:
lookup = {'alpha_2': self.country_short_code}

if lookup:
try:
return pycountry.countries.get(**lookup)
except KeyError:
pass


class WorkspaceManager(models.Manager):
def user_workspaces(self, user, role_list=None):
if user.is_unicef:
Expand Down Expand Up @@ -152,7 +98,6 @@ class Workspace(TimeStampedExternalSourceModel):
max_length=8,
unique=True
)
countries = models.ManyToManyField(Country, related_name='workspaces')
business_area_code = models.CharField(
max_length=10,
null=True, blank=True
Expand Down Expand Up @@ -515,49 +460,7 @@ def partner_activities(self, partner, clusters=None, limit=None):
return qset


# TODO Remove me
class GatewayType(TimeStampedModel):
"""
Represents an Admin Type in location-related models.
"""

name = models.CharField(max_length=64, unique=True, verbose_name=_('Name'))
admin_level = models.PositiveSmallIntegerField(verbose_name=_('Admin Level'))

country = models.ForeignKey(
Country,
related_name="gateway_types",
on_delete=models.CASCADE,
)

class Meta:
ordering = ['name']
verbose_name = 'Location type'
unique_together = ('country', 'admin_level')

def __str__(self):
return '{} - {}'.format(self.name, self.admin_level)


class LocationManager(TreeManager):

def get_queryset(self):
return super().get_queryset()


class Location(MPTTModel):
"""
Location model define place where agents are working.
The background of the location can be:
Country > Region > City > District/Point.
Either a point or geospatial object.
pcode should be unique.
related models:
indicator.Reportable (ForeignKey): "reportable"
core.Location (ForeignKey): "self"
"""
class Location(AbstractLocation):
external_id = models.CharField(
help_text='An ID representing this instance in an external system',
blank=True,
Expand All @@ -567,98 +470,7 @@ class Location(MPTTModel):

external_source = models.TextField(choices=EXTERNAL_DATA_SOURCES, blank=True, null=True)

name = models.CharField(verbose_name=_("Name"), max_length=254)

admin_level_name = models.CharField(max_length=64, verbose_name=_('Admin Level Name'), blank=True, null=True)
admin_level = models.SmallIntegerField(verbose_name=_('Admin Level'), blank=True, null=True)
workspaces = models.ManyToManyField(Workspace, related_name='locations')

latitude = models.FloatField(
verbose_name=_("Latitude"),
null=True,
blank=True,
)
longitude = models.FloatField(
verbose_name=_("Longitude"),
null=True,
blank=True,
)
p_code = models.CharField(
verbose_name=_("P Code"),
max_length=32,
blank=True,
default='',
)

parent = TreeForeignKey(
'self',
verbose_name=_("Parent"),
null=True,
blank=True,
related_name='children',
db_index=True,
on_delete=models.CASCADE
)

geom = models.MultiPolygonField(null=True, blank=True)
point = models.PointField(null=True, blank=True)
objects = LocationManager()

class Meta:
unique_together = ('name', 'p_code', 'admin_level')
ordering = ['name']

def __str__(self):
if self.p_code:
return '{} ({} {})'.format(
self.name,
self.admin_level_name,
"{}: {}".format(self.admin_level, self.p_code or '')
)

return self.name

@property
def geo_point(self):
return self.point if self.point else self.geom.point_on_surface if self.geom else ""

@property
def point_lat_long(self):
return "Lat: {}, Long: {}".format(
self.point.y,
self.point.x
)


class CartoDBTable(TimeStampedModel, MPTTModel):
"""
Represents a table in CartoDB, it is used to import locations
"""

domain = models.CharField(max_length=254, verbose_name=_('Domain'))
api_key = models.CharField(max_length=254, verbose_name=_('API Key'))
table_name = models.CharField(max_length=254, verbose_name=_('Table Name'))
display_name = models.CharField(max_length=254, default='', blank=True, verbose_name=_('Display Name'))
admin_level_name = models.CharField(max_length=64, verbose_name=_('Admin level name'))
admin_level = models.SmallIntegerField(verbose_name=_('Admin Level'))

name_col = models.CharField(max_length=254, default='name', verbose_name=_('Name Column'))
pcode_col = models.CharField(max_length=254, default='pcode', verbose_name=_('Pcode Column'))
remap_table_name = models.CharField(max_length=254, verbose_name=_('Remap Table Name'), blank=True, null=True)
parent_code_col = models.CharField(max_length=254, default='', blank=True, verbose_name=_('Parent Code Column'))
parent = TreeForeignKey(
'self', null=True, blank=True, related_name='children', db_index=True,
verbose_name=_('Parent'),
on_delete=models.CASCADE,
)
color = models.CharField(blank=True, default=get_random_color, max_length=7, verbose_name=_('Color'))

def __str__(self):
return self.table_name

class Meta:
verbose_name_plural = 'CartoDB tables'


mptt.register(Location, order_insertion_by=['name'])
mptt.register(CartoDBTable, order_insertion_by=['table_name'])
3 changes: 2 additions & 1 deletion django_api/etools_prp/apps/core/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from dateutil.relativedelta import relativedelta
from factory import fuzzy
from faker import Faker
from unicef_locations.models import CartoDBTable

from etools_prp.apps.account.models import User, UserProfile
from etools_prp.apps.cluster.models import Cluster, ClusterActivity, ClusterObjective
Expand All @@ -35,7 +36,7 @@
SHARED_PARTNER_TYPE,
)
from etools_prp.apps.core.countries import COUNTRIES_ALPHA2_CODE, COUNTRIES_ALPHA2_CODE_DICT
from etools_prp.apps.core.models import CartoDBTable, Location, PRPRole, ResponsePlan, Workspace
from etools_prp.apps.core.models import Location, PRPRole, ResponsePlan, Workspace
from etools_prp.apps.indicator.models import (
Disaggregation,
DisaggregationValue,
Expand Down
Loading

0 comments on commit 7709496

Please sign in to comment.