class MultiFields(NamedModel): city = models.ForeignKey(City) point = models.PointField() poly = models.PolygonField()
class Locazione(ConMarcaTemporale, models.Model): class Meta: verbose_name = "Locazione Geografica" verbose_name_plural = "Locazioni Geografiche" permissions = (("view_locazione", "Can view locazione"), ) indirizzo = models.CharField("Indirizzo", max_length=255, db_index=True) objects = models.GeoManager() geo = models.PointField(blank=True, default='POINT(0.0 0.0)', db_index=True, spatial_index=True, srid=4326, geography=True) via = models.CharField("Via", max_length=64, blank=True) civico = models.CharField("Civico", max_length=16, blank=True) comune = models.CharField("Comune", max_length=64, blank=True, db_index=True) provincia = models.CharField("Provincia", max_length=64, blank=True, db_index=True) provincia_breve = models.CharField("Provincia (breve)", max_length=64, blank=True, db_index=True) regione = models.CharField("Regione", max_length=64, blank=True, db_index=True) cap = models.CharField("CAP", max_length=32, blank=True, db_index=True) stato = CountryField("Stato", default="IT") def __str__(self): return self.indirizzo @classmethod def scomponi_indirizzo(cls, oggetto): INDIRIZZO_PARTI = (('civico', ('street_number', 'long_name')), ('via', ('route', 'long_name')), ('comune', ('locality', 'long_name')), ('provincia', ('administrative_area_level_2', 'long_name')), ('provincia_breve', ('administrative_area_level_2', 'short_name')), ('regione', ('administrative_area_level_1', 'long_name')), ('stato', ('country', 'short_name')), ('cap', ('postal_code', 'long_name'))) parti = {a[0]: None for a in INDIRIZZO_PARTI} for (parte, nomenclatura) in INDIRIZZO_PARTI: (a, b) = nomenclatura for p in oggetto['address_components']: if not a in p['types']: continue parti[parte] = p[b].replace('Provincia di ', '').replace('Province of ', '')\ .replace('Provincia della ', 'La ').replace('Provincia dell\'', 'L\'') return parti @classmethod def controlla_posizione(cls, coordinate): """ Valida le coordinate. L'unico controllo possibile è che le coordinate non devono essere entrambe zero :param coordinate: vettore lat / lng ritornato da google geocoding :return: coordinate inalterate o '0' """ try: if coordinate['lat'] and coordinate['lng']: return coordinate except (KeyError, ValueError, TypeError): return '0' return '0' @classmethod def cerca(cls, indirizzo): """ Usa le API di Google (Geocode) per cercare l'indirizzo, ritorna una stringa (indirizzo formattato) per ogni risultato. :param indirizzo: Indirizzo da cercare :return: Lista di risultati. """ gmaps = googlemaps.Client(key=GOOGLE_KEY) risultati = gmaps.geocode(indirizzo, region="it", language="it") return [(x['formatted_address'], cls.controlla_posizione(x['geometry']['location']), cls.scomponi_indirizzo(x)) for x in risultati] @classmethod def oggetto(self, indirizzo): """ Cerca un oggetto per indirizzo. Se esistente, ritorna la referenza. Altrimenti, interroga Google, scompone l'indirizzo e ritorna una nuova istanza di Locazione (salvata). :param indirizzo: Una stringa da ricercare. :return: Oggetto Locazione o None se non trovato. """ if not indirizzo: return None j = Locazione.objects.filter(indirizzo=indirizzo).first() if j: return j risultati = Locazione.cerca(indirizzo) if not len(risultati): return None risultato = risultati[0] j = Locazione.objects.filter(indirizzo=risultato[0]).first() if j: return j l = Locazione(indirizzo=risultato[0], geo=Point(risultato[1]['lng'], risultato[1]['lat']), **{ k: risultato[2][k] if risultato[2][k] else '' for k in risultato[2] }) l.save() return l def cerca_e_aggiorna(self): """ Cerca indirizzo in 'indirizzo' e aggiorna indirizzo e coordinate. """ risultati = Locazione.cerca(self.indirizzo) if not len(risultati): return False risultato = risultati[0] self.indirizzo = risultato[0] self.geo = Point(risultato[1]['lng'], risultato[1]['lat']) valori = { k: risultato[2][k] if risultato[2][k] else '' for k in risultato[2] } for k in valori: setattr(self, k, valori[k]) return self.save() @property def formattato(self): return self.indirizzo.replace("Italy", "Italia")
class RegMun(LayerModel): name = 'reg_mun' ags = models.IntegerField(primary_key=True) geom = geomodels.MultiPolygonField(srid=3035) geom_centroid = geomodels.PointField(srid=3035, null=True) gen = models.CharField(max_length=254)
class DummyModel(gismodels.Model): geom = gismodels.PointField()
class PlanetOsmPoint(models.Model): osm_id = models.BigIntegerField(primary_key=True) access = models.TextField(blank=True, null=True) addr_housename = models.TextField( db_column='addr:housename', blank=True, null=True) # Field renamed to remove unsuitable characters. addr_housenumber = models.TextField( db_column='addr:housenumber', blank=True, null=True) # Field renamed to remove unsuitable characters. addr_interpolation = models.TextField( db_column='addr:interpolation', blank=True, null=True) # Field renamed to remove unsuitable characters. admin_level = models.TextField(blank=True, null=True) aerialway = models.TextField(blank=True, null=True) aeroway = models.TextField(blank=True, null=True) amenity = models.TextField(blank=True, null=True) area = models.TextField(blank=True, null=True) barrier = models.TextField(blank=True, null=True) bicycle = models.TextField(blank=True, null=True) brand = models.TextField(blank=True, null=True) bridge = models.TextField(blank=True, null=True) boundary = models.TextField(blank=True, null=True) building = models.TextField(blank=True, null=True) capital = models.TextField(blank=True, null=True) construction = models.TextField(blank=True, null=True) covered = models.TextField(blank=True, null=True) culvert = models.TextField(blank=True, null=True) cutting = models.TextField(blank=True, null=True) denomination = models.TextField(blank=True, null=True) disused = models.TextField(blank=True, null=True) ele = models.TextField(blank=True, null=True) embankment = models.TextField(blank=True, null=True) foot = models.TextField(blank=True, null=True) generator_source = models.TextField( db_column='generator:source', blank=True, null=True) # Field renamed to remove unsuitable characters. harbour = models.TextField(blank=True, null=True) highway = models.TextField(blank=True, null=True) historic = models.TextField(blank=True, null=True) horse = models.TextField(blank=True, null=True) intermittent = models.TextField(blank=True, null=True) junction = models.TextField(blank=True, null=True) landuse = models.TextField(blank=True, null=True) layer = models.TextField(blank=True, null=True) leisure = models.TextField(blank=True, null=True) lock = models.TextField(blank=True, null=True) man_made = models.TextField(blank=True, null=True) military = models.TextField(blank=True, null=True) motorcar = models.TextField(blank=True, null=True) name = models.TextField(blank=True, null=True) natural = models.TextField(blank=True, null=True) office = models.TextField(blank=True, null=True) oneway = models.TextField(blank=True, null=True) operator = models.TextField(blank=True, null=True) place = models.TextField(blank=True, null=True) population = models.TextField(blank=True, null=True) power = models.TextField(blank=True, null=True) power_source = models.TextField(blank=True, null=True) public_transport = models.TextField(blank=True, null=True) railway = models.TextField(blank=True, null=True) ref = models.TextField(blank=True, null=True) religion = models.TextField(blank=True, null=True) route = models.TextField(blank=True, null=True) service = models.TextField(blank=True, null=True) shop = models.TextField(blank=True, null=True) sport = models.TextField(blank=True, null=True) surface = models.TextField(blank=True, null=True) toll = models.TextField(blank=True, null=True) tourism = models.TextField(blank=True, null=True) tower_type = models.TextField( db_column='tower:type', blank=True, null=True) # Field renamed to remove unsuitable characters. tunnel = models.TextField(blank=True, null=True) water = models.TextField(blank=True, null=True) waterway = models.TextField(blank=True, null=True) wetland = models.TextField(blank=True, null=True) width = models.TextField(blank=True, null=True) wood = models.TextField(blank=True, null=True) z_order = models.IntegerField(blank=True, null=True) way = models.PointField(srid=4326, blank=True, null=True) class Meta: managed = False db_table = 'planet_osm_point'
class Device(BaseAccessLevel): """ Device Model Represents a network device eg: an outdoor point-to-point wifi device, a BGP router, a server, and so on """ name = models.CharField(_('name'), max_length=50) node = models.ForeignKey('nodes.Node', verbose_name=_('node')) type = models.CharField(_('type'), max_length=50, choices=DEVICE_TYPES_CHOICES) status = models.SmallIntegerField(_('status'), choices=DEVICE_STATUS_CHOICES, default=DEVICE_STATUS.get('unknown')) # geographic data location = models.PointField( _('location'), blank=True, null=True, help_text=_("""specify device coordinates (if different from node); defaults to node coordinates if node is a point, otherwise if node is a geometry it will default to che centroid of the geometry""" )) elev = models.FloatField(_('elevation'), blank=True, null=True) # device specific routing_protocols = models.ManyToManyField('net.RoutingProtocol', blank=True) os = models.CharField(_('operating system'), max_length=128, blank=True, null=True) os_version = models.CharField(_('operating system version'), max_length=128, blank=True, null=True) first_seen = models.DateTimeField(_('first time seen on'), blank=True, null=True, default=None) last_seen = models.DateTimeField(_('last time seen on'), blank=True, null=True, default=None) # text description = models.CharField(_('description'), max_length=255, blank=True, null=True) notes = models.TextField(_('notes'), blank=True, null=True) # extra data data = DictionaryField( _('extra data'), null=True, blank=True, help_text=_('store extra attributes in JSON string')) shortcuts = ReferencesField(null=True, blank=True) objects = DeviceManager() # list indicating if any other module has extended this model extended_by = [] class Meta: app_label = 'net' def __unicode__(self): return '%s' % self.name def save(self, *args, **kwargs): """ Custom save method does the following: * automatically inherit node coordinates and elevation * save shortcuts if HSTORE is enabled """ custom_checks = kwargs.pop('custom_checks', True) super(Device, self).save(*args, **kwargs) if custom_checks is False: return changed = False if not self.location: self.location = self.node.point changed = True if not self.elev and self.node.elev: self.elev = self.node.elev changed = True original_user = self.shortcuts.get('user') if self.node.user: self.shortcuts['user'] = self.node.user if original_user != self.shortcuts.get('user'): changed = True if 'nodeshot.core.layers' in settings.INSTALLED_APPS: original_layer = self.shortcuts.get('layer') self.shortcuts['layer'] = self.node.layer if original_layer != self.shortcuts.get('layer'): changed = True if changed: self.save(custom_checks=False) @property def owner(self): if 'user' not in self.shortcuts: if self.node or self.node_id: self.save() else: raise Exception('Instance does not have a node set yet') return self.shortcuts['user'] @property def layer(self): if 'nodeshot.core.layers' not in settings.INSTALLED_APPS: return False if 'layer' not in self.shortcuts: if self.node or self.node_id: self.save() else: raise Exception('Instance does not have a node set yet') return self.shortcuts['layer'] if 'grappelli' in settings.INSTALLED_APPS: @staticmethod def autocomplete_search_fields(): return ('name__icontains', )
class Comercio(models.Model): nombre = models.CharField(max_length=100) latlng = models.PointField() ciudad = models.ForeignKey(Ciudad) objects = models.GeoManager()
class VD(models.Model): authOwner = models.ForeignKey(User, on_delete=models.SET_DEFAULT, null=True, default=None, related_name='vds') realOwner = models.ForeignKey(RealUser, on_delete=models.SET_DEFAULT, null=True, default=None, related_name='vds') lastLonLat = models.PointField(blank=True, null=True, default=None, geography=True) data = JSONField(blank=True, null=True, default=None, db_index=False) deviceName = models.CharField(max_length=254, blank=True, null=True, default=None) deviceTypeName = models.CharField(max_length=36, blank=True, null=True, default=None) country = models.CharField(max_length=2, blank=True, null=True, default=None) language = models.CharField(max_length=2, blank=True, null=True, default=None) timezone = models.CharField(max_length=5, blank=True, null=True, default=None) parent = models.ForeignKey('self', on_delete=models.SET_DEFAULT, null=True, default=None, related_name='childs') mask = models.SmallIntegerField(blank=True, null=True, default=None) accessHistory = JSONField(blank=True, null=True, default=None, db_index=False) def __init__(self, *args, **kwargs): super(VD, self).__init__(*args, **kwargs) def __unicode__(self): email = (self.realOwner and self.realOwner.email) or ( self.authOwner and self.authOwner.email) or 'unknown@email' deviceTypeName = self.deviceTypeName or 'unknown device' deviceNameCaption = (self.deviceName and ' (%s)' % self.deviceName) or '' return '%s\'s %s%s' % (email, deviceTypeName, deviceNameCaption) @property def aid(self): encrypter = Fernet(self.authOwner.crypto_key) result = encrypter.encrypt(unicode(self.id).encode(encoding='utf-8')) return result def aid2id(self, aid): return self.authOwner.aid2id(aid) @property def realOwner_vd_ids(self): if self.realOwner: result, is_created = cache_get_or_create( self, 'realOwner_vd_ids', None, lambda: self.realOwner.vd_ids) return result return [self.id] @property def realOwner_publisher_ids(self): def helper(vd_ids): from importer.models import Importer importers = Importer.objects.filter(subscriber_id__in=vd_ids) if importers: return sum( [importer.publisher.vd_ids for importer in importers], []) else: return [] result, is_created = cache_get_or_create(self, 'realOwner_publisher_ids', None, helper, self.realOwner_vd_ids) return result @property def realOwner_place_ids(self): def helper(vd_ids): from place.models import Place return [ place.id for place in Place.objects.filter(uplaces__vd_id__in=vd_ids) ] result, is_created = cache_get_or_create(self, 'realOwner_place_ids', None, helper, self.realOwner_vd_ids) return result @property def realOwner_duplicated_uplace_ids(self): def helper(vd_ids): from place.libs import get_valid_uplaces_qs base = get_valid_uplaces_qs().exclude(place_id=None).filter( vd_id__in=vd_ids) group_by = base.values('place_id').annotate(cnt=Count(1)).filter( cnt__gte=2) result = list() before = None for uplace in base.filter( place_id__in=[row['place_id'] for row in group_by]).order_by( 'place_id', '-id'): if uplace.place_id == (before and before.place_id): result.append(uplace.id) before = uplace return result result, is_created = cache_get_or_create( self, 'realOwner_duplicated_uplace_ids', None, helper, self.realOwner_vd_ids) return result @property def realOwner_duplicated_iplace_ids(self): def helper(vd_ids): from importer.models import ImportedPlace base = ImportedPlace.objects.all().exclude(place_id=None).filter( vd_id__in=vd_ids) group_by = base.values('place_id').annotate(cnt=Count(1)).filter( cnt__gte=2) result = list() before = None for iplace in base.filter( place_id__in=[row['place_id'] for row in group_by]).order_by( 'place_id', '-id'): if iplace.place_id == (before and before.place_id): result.append(iplace.id) before = iplace return result result, is_created = cache_get_or_create( self, 'realOwner_duplicated_iplace_ids', None, helper, self.realOwner_publisher_ids) return result @property def is_private(self): return (self.mask or 0) & 1 != 0 @is_private.setter def is_private(self, value): if value: self.mask = (self.mask or 0) | 1 else: self.mask = (self.mask or 0) & (~1) @property def is_public(self): return (self.mask or 0) & 2 != 0 @is_public.setter def is_public(self, value): if value: self.mask = (self.mask or 0) | 2 else: self.mask = (self.mask or 0) & (~2) def save(self, *args, **kwargs): if not self.mask: self.mask = 0 self.data = convert_to_json(self.data) self.accessHistory = convert_to_json(self.accessHistory) cache_expire_ru(self, 'realOwner_vd_ids') super(VD, self).save(*args, **kwargs) # TODO : 제대로 구현하기 @property def error_tagging_ratio(self): # P(D|~H) / P(D|H) return 0.2 def getToken(self): raw_token = '%s|%s' % (self.id, self.authOwner_id) encrypter = Fernet(self.authOwner.crypto_key) return encrypter.encrypt(raw_token.encode(encoding='utf-8')) def getEmailConfirmToken(self, email): from pks.settings import VD_ENC_KEY raw_token = '%s|%s' % (self.id, email) encrypter = Fernet(VD_ENC_KEY) return encrypter.encrypt(raw_token.encode(encoding='utf-8')) def add_access_history(self, uplaces): if not self.accessHistory: self.accessHistory = dict(uplaces=[]) if not type(uplaces) is list: uplaces = [uplaces] for uplace in uplaces: uuid = uplace.uuid timestamp = get_timestamp() self.accessHistory['uplaces'].insert( 0, dict(uuid=uuid, timestamp=timestamp)) super(VD, self).save() @property def accessUplaces(self): from place.models import UserPlace result = [] if self.accessHistory and 'uplaces' in self.accessHistory: for d in self.accessHistory['uplaces']: uplace = UserPlace.get_from_uuid(d['uuid']) uplace.accessed = d['timestamp'] result.append(uplace) return result def send_confirm_email(self, email): # send email for confirm # TODO : 관련 테스트 보강 from account.task_wrappers import EmailTaskWrapper from pks.settings import SERVER_HOST task = EmailTaskWrapper() to = email title = '사진 공유 서비스 포플(PHOPL) 이메일 인증' confirm_link = '%s/vds/confirm/?email_confirm_token=%s' % ( SERVER_HOST, self.getEmailConfirmToken(to)) msg = '안녕하세요. 사진 공유 서비스 포플(PHOPL) 입니다.\n이메일 인증을 위해 하기 링크를 터치해 주세요.\n\n%s' % confirm_link r = task.delay(to, title, msg) return not r.failed()
class SignpostPlan(UpdatePlanLocationMixin, SourceControlModel, SoftDeleteModel, UserControlModel): id = models.UUIDField(primary_key=True, unique=True, editable=False, default=uuid.uuid4) location = models.PointField(_("Location (2D)"), srid=settings.SRID) height = models.DecimalField(_("Height"), max_digits=5, decimal_places=2, blank=True, null=True) direction = models.IntegerField(_("Direction"), default=0) device_type = models.ForeignKey( TrafficControlDeviceType, verbose_name=_("Device type"), on_delete=models.PROTECT, limit_choices_to=Q( Q(target_model=None) | Q(target_model=DeviceTypeTargetModel.SIGNPOST)), ) value = models.IntegerField(_("Signpost value"), blank=True, null=True) txt = models.CharField(_("Signpost txt"), max_length=254, blank=True, null=True) parent = models.ForeignKey( "self", verbose_name=_("Parent Signpost Plan"), on_delete=models.PROTECT, blank=True, null=True, ) mount_plan = models.ForeignKey( MountPlan, verbose_name=_("Mount Plan"), on_delete=models.PROTECT, blank=True, null=True, ) mount_type = models.ForeignKey( MountType, verbose_name=_("Mount type"), blank=True, null=True, on_delete=models.SET_NULL, ) decision_date = models.DateField(_("Decision date")) decision_id = models.CharField(_("Decision id"), max_length=254, blank=True, null=True) validity_period_start = models.DateField(_("Validity period start"), blank=True, null=True) validity_period_end = models.DateField(_("Validity period end"), blank=True, null=True) plan = models.ForeignKey( Plan, verbose_name=_("Plan"), on_delete=models.PROTECT, related_name="signpost_plans", blank=True, null=True, ) owner = models.ForeignKey( "traffic_control.Owner", verbose_name=_("Owner"), blank=False, null=False, on_delete=models.PROTECT, ) size = EnumField( Size, verbose_name=_("Size"), max_length=1, default=Size.MEDIUM, blank=True, null=True, ) reflection_class = EnumField( Reflection, verbose_name=_("Reflection"), max_length=2, default=Reflection.R1, blank=True, null=True, ) seasonal_validity_period_start = models.DateField( _("Seasonal validity period start"), blank=True, null=True) seasonal_validity_period_end = models.DateField( _("Seasonal validity period end"), blank=True, null=True) attachment_class = models.CharField(_("Attachment class"), max_length=254, blank=True, null=True) target_id = models.CharField(_("Target ID"), max_length=254, blank=True, null=True) target_txt = models.CharField(_("Target txt"), max_length=254, blank=True, null=True) responsible_entity = models.CharField(_("Responsible entity"), max_length=254, blank=True, null=True) electric_maintainer = models.CharField(_("Electric maintainer"), max_length=254, blank=True, null=True) lifecycle = EnumIntegerField(Lifecycle, verbose_name=_("Lifecycle"), default=Lifecycle.ACTIVE) road_name = models.CharField(_("Road name"), max_length=254, blank=True, null=True) lane_number = EnumField(LaneNumber, verbose_name=_("Lane number"), default=LaneNumber.MAIN_1, blank=True) lane_type = EnumField( LaneType, verbose_name=_("Lane type"), default=LaneType.MAIN, blank=True, ) location_specifier = EnumIntegerField( LocationSpecifier, verbose_name=_("Location specifier"), default=LocationSpecifier.RIGHT, blank=True, null=True, ) objects = SoftDeleteQuerySet.as_manager() class Meta: db_table = "signpost_plan" verbose_name = _("Signpost Plan") verbose_name_plural = _("Signpost Plans") unique_together = ["source_name", "source_id"] def __str__(self): return f"{self.id} {self.device_type} {self.txt}" def save(self, *args, **kwargs): if not self.device_type.validate_relation( DeviceTypeTargetModel.SIGNPOST): raise ValidationError( f'Device type "{self.device_type}" is not allowed for signposts' ) super().save(*args, **kwargs)
class FakePoint(models.Model): fake_id = models.IntegerField(primary_key=True) name = models.TextField() point = models.PointField(srid=4326)
class SprayDay(models.Model): """ SprayDay model - IRS HH submission data model. """ submission_id = models.PositiveIntegerField(unique=True) osmid = models.BigIntegerField(null=True, db_index=True) spray_date = models.DateField(db_index=True) geom = models.PointField(srid=4326, db_index=True, null=True) bgeom = models.GeometryField(srid=4326, db_index=True, null=True) data = JSONField(default=dict) location = models.ForeignKey("Location", db_index=True, null=True, on_delete=models.CASCADE) rhc = models.ForeignKey( "Location", db_index=True, null=True, related_name="visited_rhc", on_delete=models.CASCADE, ) district = models.ForeignKey( "Location", db_index=True, null=True, related_name="visited_district", on_delete=models.CASCADE, ) team_leader = models.ForeignKey("TeamLeader", db_index=True, null=True, on_delete=models.CASCADE) team_leader_assistant = models.ForeignKey( "TeamLeaderAssistant", db_index=True, null=True, on_delete=models.CASCADE, ) spray_operator = models.ForeignKey("SprayOperator", db_index=True, null=True, on_delete=models.CASCADE) household = models.ForeignKey("Household", null=True, on_delete=models.SET_NULL) start_time = models.DateTimeField(null=True) end_time = models.DateTimeField(null=True) was_sprayed = models.BooleanField(default=False) sprayable = models.BooleanField(default=False) created_on = models.DateTimeField(auto_now_add=True) modified_on = models.DateTimeField(auto_now=True) class Meta: app_label = "main" def __str__(self): return self.spray_date.isoformat() def _set_sprayed_status(self): # pylint: disable=no-member was_sprayed_field = getattr(settings, "MSPRAY_WAS_SPRAYED_FIELD", WAS_SPRAYED_FIELD) sprayed_values = getattr(settings, "SPRAYED_VALUES", []) if self.data.get(was_sprayed_field): if sprayed_values: self.was_sprayed = (self.data.get(was_sprayed_field) in sprayed_values) else: self.was_sprayed = ( self.data.get(was_sprayed_field) == SPRAYED_VALUE) elif self.data.get(NEW_WAS_SPRAYED_FIELD): self.was_sprayed = ( self.data.get(NEW_WAS_SPRAYED_FIELD) == SPRAYED_VALUE) def _set_sprayable_status(self): # pylint: disable=no-member sprayable_field = getattr(settings, "SPRAYABLE_FIELD", SPRAYABLE_FIELD) new_sprayable_field = getattr(settings, "NEW_STRUCTURE_SPRAYABLE_FIELD", NEW_STRUCTURE_GPS_FIELD) not_sprayable_value = getattr(settings, "NOT_SPRAYABLE_VALUE", NOT_SPRAYABLE_VALUE) if self.data.get(sprayable_field): self.sprayable = (self.data.get(sprayable_field) != not_sprayable_value) elif self.data.get(new_sprayable_field): self.sprayable = (self.data.get(new_sprayable_field) != not_sprayable_value) def _set_parent_locations(self): if self.location: if self.rhc != self.location.parent: # pylint: disable=no-member self.rhc = self.location.parent # pylint: disable=no-member if self.district != self.rhc.parent: # pylint: disable=no-member self.district = self.rhc.parent # pylint: disable=no-member def has_osm_data(self): """ Check if OSM data has been received for this record we assume that if settings.MSPRAY_OSM_PRESENCE_FIELD is present then this record has received OSM data """ return settings.MSPRAY_OSM_PRESENCE_FIELD in self.data # pylint: disable=arguments-differ def save(self, *args, **kwargs): from mspray.apps.main.utils import get_formid # noqa self._set_sprayed_status() self._set_sprayable_status() self._set_parent_locations() if "sprayformid" and self.spray_operator: self.data.update({ # pylint: disable=no-member "sprayformid": get_formid(self.spray_operator, self.spray_date) }) osmid = get_osmid(self.data) try: if osmid and int(osmid) != self.osmid and not self.location: self.osmid = osmid except ValueError: pass return super(SprayDay, self).save(*args, **kwargs)
class FakePoint(models.Model): name = models.CharField(max_length=30) point = models.PointField() objects = models.GeoManager()
class Map(NamedModel): """ A single thematical map. """ ANONYMOUS = 1 EDITORS = 2 OWNER = 3 PUBLIC = 1 OPEN = 2 PRIVATE = 3 EDIT_STATUS = ( (ANONYMOUS, _('Everyone can edit')), (EDITORS, _('Only editors can edit')), (OWNER, _('Only owner can edit')), ) SHARE_STATUS = ( (PUBLIC, _('everyone (public)')), (OPEN, _('anyone with link')), (PRIVATE, _('editors only')), ) slug = models.SlugField(db_index=True) description = models.TextField(blank=True, null=True, verbose_name=_("description")) center = models.PointField(geography=True, verbose_name=_("center")) zoom = models.IntegerField(default=7, verbose_name=_("zoom")) locate = models.BooleanField(default=False, verbose_name=_("locate"), help_text=_("Locate user on load?")) licence = models.ForeignKey(Licence, help_text=_("Choose the map licence."), verbose_name=_('licence'), on_delete=models.SET_DEFAULT, default=get_default_licence) modified_at = models.DateTimeField(auto_now=True) tilelayer = models.ForeignKey(TileLayer, blank=True, null=True, related_name="maps", verbose_name=_("background")) owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="owned_maps", verbose_name=_("owner")) editors = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, verbose_name=_("editors")) edit_status = models.SmallIntegerField(choices=EDIT_STATUS, default=OWNER, verbose_name=_("edit status")) share_status = models.SmallIntegerField(choices=SHARE_STATUS, default=PUBLIC, verbose_name=_("share status")) settings = DictField(blank=True, null=True, verbose_name=_("settings")) objects = models.GeoManager() public = PublicManager() def get_absolute_url(self): return reverse("map", kwargs={ 'slug': self.slug or "map", 'pk': self.pk }) def get_anonymous_edit_url(self): signer = Signer() signature = signer.sign(self.pk) return reverse('map_anonymous_edit_url', kwargs={'signature': signature}) def is_anonymous_owner(self, request): if self.owner: # edit cookies are only valid while map hasn't owner return False key, value = self.signed_cookie_elements try: has_anonymous_cookie = int(request.get_signed_cookie( key, False)) == value except ValueError: has_anonymous_cookie = False return has_anonymous_cookie def can_edit(self, user=None, request=None): """ Define if a user can edit or not the instance, according to his account or the request. """ can = False if request and not self.owner: if (getattr(settings, "LEAFLET_STORAGE_ALLOW_ANONYMOUS", False) and self.is_anonymous_owner(request)): can = True if user and user.is_authenticated(): # if user is authenticated, attach as owner self.owner = user self.save() msg = _( "Your anonymous map has been attached to your account %s" % user) messages.info(request, msg) if self.edit_status == self.ANONYMOUS: can = True elif not user.is_authenticated(): pass elif user == self.owner: can = True elif self.edit_status == self.EDITORS and user in self.editors.all(): can = True return can def can_view(self, request): if self.owner is None: can = True elif self.share_status in [self.PUBLIC, self.OPEN]: can = True elif request.user == self.owner: can = True else: can = not (self.share_status == self.PRIVATE and request.user not in self.editors.all()) return can @property def signed_cookie_elements(self): return ('anonymous_owner|%s' % self.pk, self.pk) def get_tilelayer(self): return self.tilelayer or TileLayer.get_default() def clone(self, **kwargs): new = self.__class__.objects.get(pk=self.pk) new.pk = None new.name = u"%s %s" % (_("Clone of"), self.name) if "owner" in kwargs: # can be None in case of anonymous cloning new.owner = kwargs["owner"] new.save() for editor in self.editors.all(): new.editors.add(editor) for datalayer in self.datalayer_set.all(): datalayer.clone(map_inst=new) return new
class NonConcreteModel(NamedModel): non_concrete = NonConcreteField() point = models.PointField(geography=True)
class TrafficSignPlan(UpdatePlanLocationMixin, SourceControlModel, SoftDeleteModel, UserControlModel): id = models.UUIDField(primary_key=True, unique=True, editable=False, default=uuid.uuid4) location = models.PointField(_("Location (3D)"), dim=3, srid=settings.SRID) height = models.IntegerField(_("Height"), blank=True, null=True) direction = models.IntegerField(_("Direction"), default=0) device_type = models.ForeignKey( TrafficControlDeviceType, verbose_name=_("Device type"), on_delete=models.PROTECT, limit_choices_to=Q( Q(target_model=None) | Q(target_model=DeviceTypeTargetModel.TRAFFIC_SIGN)), ) value = models.IntegerField(_("Traffic Sign Code value"), blank=True, null=True) txt = models.CharField(_("Txt"), max_length=254, blank=True, null=True) mount_plan = models.ForeignKey( MountPlan, verbose_name=_("Mount Plan"), on_delete=models.PROTECT, blank=True, null=True, ) mount_type = models.ForeignKey( MountType, verbose_name=_("Mount type"), blank=True, null=True, on_delete=models.SET_NULL, ) decision_date = models.DateField(_("Decision date")) decision_id = models.CharField(_("Decision id"), max_length=254, blank=True, null=True) validity_period_start = models.DateField(_("Validity period start"), blank=True, null=True) validity_period_end = models.DateField(_("Validity period end"), blank=True, null=True) affect_area = models.PolygonField(_("Affect area (2D)"), srid=settings.SRID, blank=True, null=True) plan = models.ForeignKey( Plan, verbose_name=_("Plan"), on_delete=models.PROTECT, related_name="traffic_sign_plans", blank=True, null=True, ) size = EnumField( Size, verbose_name=_("Size"), max_length=1, default=Size.MEDIUM, blank=True, null=True, ) reflection_class = EnumField( Reflection, verbose_name=_("Reflection"), max_length=2, default=Reflection.R1, blank=True, null=True, ) surface_class = EnumField( Surface, verbose_name=_("Surface"), max_length=6, default=Surface.FLAT, blank=True, null=True, ) seasonal_validity_period_start = models.DateField( _("Seasonal validity period start"), blank=True, null=True) seasonal_validity_period_end = models.DateField( _("Seasonal validity period end"), blank=True, null=True) owner = models.ForeignKey( "traffic_control.Owner", verbose_name=_("Owner"), blank=False, null=False, on_delete=models.PROTECT, ) lifecycle = EnumIntegerField(Lifecycle, verbose_name=_("Lifecycle"), default=Lifecycle.ACTIVE) road_name = models.CharField(_("Road name"), max_length=254, blank=True, null=True) lane_number = EnumField(LaneNumber, verbose_name=_("Lane number"), default=LaneNumber.MAIN_1, blank=True) lane_type = EnumField( LaneType, verbose_name=_("Lane type"), default=LaneType.MAIN, blank=True, ) location_specifier = EnumIntegerField( LocationSpecifier, verbose_name=_("Location specifier"), default=LocationSpecifier.RIGHT, blank=True, null=True, ) objects = TrafficSignPlanQuerySet.as_manager() class Meta: db_table = "traffic_sign_plan" verbose_name = _("Traffic Sign Plan") verbose_name_plural = _("Traffic Sign Plans") unique_together = ["source_name", "source_id"] def __str__(self): return f"{self.id} {self.device_type}" def save(self, *args, **kwargs): if not self.device_type.validate_relation( DeviceTypeTargetModel.TRAFFIC_SIGN): raise ValidationError( f'Device type "{self.device_type}" is not allowed for traffic signs' ) super().save(*args, **kwargs) def has_additional_signs(self): return self.additional_signs.active().exists() @transaction.atomic def soft_delete(self, user): super().soft_delete(user) self.additional_signs.soft_delete(user)
class Leaflet(geo_model.Model): def __init__(self, *args, **kwargs): super(Leaflet, self).__init__(*args, **kwargs) self._initial = model_to_dict( self, fields=[field.name for field in self._meta.fields] ) title = models.CharField(blank=True, max_length=765) description = models.TextField(blank=True, null=True) publisher_party = models.ForeignKey( Party, blank=True, null=True, on_delete=models.CASCADE ) ynr_party_id = models.CharField( blank=True, null=True, max_length=255, db_index=True ) ynr_party_name = models.CharField(blank=True, null=True, max_length=255) publisher_person = models.ForeignKey( Person, blank=True, null=True, on_delete=models.CASCADE ) ynr_person_id = models.IntegerField(blank=True, null=True, db_index=True) ynr_person_name = models.CharField(blank=True, null=True, max_length=255) ballot_id = models.CharField( blank=True, null=True, max_length=255, db_index=True ) election = models.ForeignKey(Election, null=True, on_delete=models.CASCADE) constituency = models.ForeignKey( Constituency, blank=True, null=True, on_delete=models.CASCADE ) imprint = models.TextField(blank=True, null=True) postcode = models.CharField(max_length=150, blank=True) location = geo_model.PointField(null=True, blank=True) name = models.CharField(blank=True, max_length=300) email = models.CharField(blank=True, max_length=300) date_uploaded = models.DateTimeField(auto_now_add=True) date_delivered = models.DateTimeField(blank=True, null=True) status = models.CharField( choices=constants.LEAFLET_STATUSES, null=True, blank=True, max_length=255, ) reviewed = models.BooleanField(default=False) objects = models.Manager() def __unicode__(self): return self.title class Meta: ordering = ("-date_uploaded",) def get_absolute_url(self): from django.contrib.sites.models import Site return "http://%s/leaflets/%s/" % ( Site.objects.get_current().domain, self.id, ) def get_first_image(self): try: return self.images.all()[0] except IndexError: # TODO: Set image_key to value for 'empty' image return None def get_title(self): if self.title and len(self.title): return self.title elif self.publisher_party: return "%s leaflet" % self.publisher_party.party_name else: "Untitled leaflet" def get_person(self): if ( self.ynr_person_id and self.ynr_person_name and not self.publisher_person ): return { "link": reverse( "person", kwargs={"remote_id": self.ynr_person_id} ), "name": self.ynr_person_name, } elif self.publisher_person: return { "link": reverse( "person", kwargs={"remote_id": self.publisher_person.remote_id}, ), "name": self.publisher_person.name, } def get_party(self): if self.ynr_party_id and self.ynr_party_name: pp_id = "PP{}".format(re.sub(r"party:", "", self.ynr_party_id)) return { "link": reverse("party-view", kwargs={"pk": pp_id}), "name": self.ynr_party_name, } elif self.publisher_party: return { "link": reverse( "party-view", kwargs={"pk": self.publisher_party.pk} ), "name": self.publisher_party.party_name, } def get_ballot(self): if self.ballot_id: r = devs_dc_helper.ballot_request(self.ballot_id) if r.status_code == 200: ballot = r.json() return { "name": "{} in {}".format( ballot["election_title"], ballot["election_type"]["name"], ), "link": "https://whocanivotefor.co.uk/elections/{}".format( self.ballot_id ), } def geocode(self, postcode): data = geocode(postcode) if data: self.constituency = data["constituency"] self.location = Point(data["wgs84_lon"], data["wgs84_lat"],) def save(self, *args, **kwargs): if self.postcode: if self.postcode != self._initial["postcode"]: self.geocode(self.postcode) super(Leaflet, self).save(*args, **kwargs)
class DummyInlineModel(gismodels.Model): geom = gismodels.PointField() class Meta: app_label = "leaflet"
class PlanetOsmPoint(models.Model): """ osm point model """ osm_fields: dict = { "access": str, "addr:housename": str, "addr:housenumber": str, "admin_level": str, "aerialway": str, "aeroway": str, "amenity": str, "barrier": str, "boundary": str, "building": str, "highway": str, "historic": str, "junction": str, "landuse": str, "layer": int, "leisure": str, "lock": str, "man_made": str, "military": str, "name": str, "natural": str, "oneway": str, "place": str, "power": str, "railway": str, "ref": str, "religion": str, "shop": str, "tourism": str, "water": str, "waterway": str, } id = models.BigAutoField(primary_key=True) osm_id = models.BigIntegerField(blank=True, null=True) version = models.IntegerField(blank=True, null=True) visible = models.BooleanField(blank=True, null=True) geoobject = models.BigIntegerField(blank=True, null=True) access = models.TextField(blank=True, null=True) addr_housename = models.TextField(db_column="addr:housename", blank=True, null=True) addr_housenumber = models.TextField(db_column="addr:housenumber", blank=True, null=True) admin_level = models.TextField(blank=True, null=True) aerialway = models.TextField(blank=True, null=True) aeroway = models.TextField(blank=True, null=True) amenity = models.TextField(blank=True, null=True) barrier = models.TextField(blank=True, null=True) boundary = models.TextField(blank=True, null=True) building = models.TextField(blank=True, null=True) highway = models.TextField(blank=True, null=True) historic = models.TextField(blank=True, null=True) junction = models.TextField(blank=True, null=True) landuse = models.TextField(blank=True, null=True) layer = models.IntegerField(blank=True, null=True) leisure = models.TextField(blank=True, null=True) lock = models.TextField(blank=True, null=True) man_made = models.TextField(blank=True, null=True) military = models.TextField(blank=True, null=True) name = models.TextField(blank=True, null=True) natural = models.TextField(blank=True, null=True) oneway = models.TextField(blank=True, null=True) place = models.TextField(blank=True, null=True) power = models.TextField(blank=True, null=True) railway = models.TextField(blank=True, null=True) ref = models.TextField(blank=True, null=True) religion = models.TextField(blank=True, null=True) shop = models.TextField(blank=True, null=True) tourism = models.TextField(blank=True, null=True) water = models.TextField(blank=True, null=True) waterway = models.TextField(blank=True, null=True) tags = HStoreField(default=dict) way = models.PointField(srid=3857, blank=True, null=True) valid_since = models.DateField(blank=True, null=True) valid_until = models.DateField(blank=True, null=True) class Meta: db_table = "planet_osm_point"
class PimpUser(AbstractEmailUser): MARKETER = 0 CAUSE = 1 ADMIN = 2 USER_TYPE_CHOICES = ((MARKETER, "Marketer"), (CAUSE, "Cause"), (ADMIN, "Admin")) REQUIRED_FIELDS = ['usertype'] # basic info name = models.CharField(max_length=240, blank=True, null=True) surname = models.CharField(max_length=240, blank=True, null=True) phone = models.CharField(max_length=250, blank=True, null=True) image = S3DirectField(dest='user-profile-images', blank=True, null=True) # location info country = CountryField(blank=True, null=True, blank_label='Select country') city = models.CharField(max_length=1000, blank=True, null=True) postcode = models.CharField(max_length=120, blank=True, null=True) location = models.PointField(blank=True, null=True) # professional info position = models.CharField(max_length=1000, blank=True, null=True) usertype = models.IntegerField( choices=USER_TYPE_CHOICES, default=MARKETER, ) bio = models.TextField(max_length=10000, blank=True, null=True) cause_name = models.CharField(max_length=1000, blank=True, null=True) # social accounts twitter = models.URLField(max_length=1000, blank=True, null=True) linkedin = models.URLField(max_length=1000, blank=True, null=True) website = models.URLField(max_length=1000, blank=True, null=True) featured = models.BooleanField(default=False) # storing old geo data geo_data = models.TextField(max_length=300, blank=True, null=True) has_reactivated = models.BooleanField(default=False) def __str__(self): if (self.usertype == self.CAUSE): return '%s' % (self.cause_name) else: return '%s %s' % (self.surname, self.name) def save(self, **kwargs): if (self.usertype == self.CAUSE and not self.cause_name): raise ValidationError("Invalid value: Cause name is required", code='invalid') super(PimpUser, self).save(**kwargs) @property def is_admin(self): return self.usertype in [PimpUser.ADMIN] @property def is_marketer(self): return self.usertype in [PimpUser.MARKETER] @property def is_cause(self): return self.usertype in [PimpUser.CAUSE] @property def is_geolocated(self): if (not self.location): return False else: return True
class Location(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) name = models.CharField(max_length=200) localization = models.PointField(srid=4326, ) elevation = models.FloatField(default=0.0)
class Earthquake(BaseEventModel): """Earthquake model.""" INITIAL_SOURCE_TYPE = 'initial' CORRECTED_SOURCE_TYPE = 'corrected' class Meta: """Meta class.""" app_label = 'realtime' unique_together = (('shake_id', 'source_type'), ) ordering = ['-time', '-shake_id', '-source_type'] # Shake ID of corrected shakemaps can go as far as: # 20180316015829_52_-6050_142170_20180316015829 shake_id = models.CharField( verbose_name=_('The Shake ID'), help_text=_('The Shake ID, which represents the time of the event.'), max_length=50, blank=False) shake_grid = models.FileField(verbose_name=_('Shake Grid XML File'), help_text=_('The Shake Grid to process'), upload_to='earthquake/grid', blank=True, null=True) shake_grid_xml = models.TextField( verbose_name=_('Shake Grid XML File Contents'), help_text=_('The content of shake grid file'), blank=True, null=True) mmi_output = models.FileField( verbose_name=_('MMI related file zipped'), help_text=_('MMI related file, layers, and data, zipped.'), upload_to='earthquake/mmi_output', blank=True, null=True) mmi_output_path = models.CharField( verbose_name=_('MMI related file path'), help_text=_('MMI related file path location'), max_length=255, blank=True, null=True, default=None) magnitude = models.FloatField(verbose_name=_('The magnitude'), help_text=_('The magnitude of the event.')) time = models.DateTimeField(verbose_name=_('Date and Time'), help_text=_('The time the shake happened.'), blank=False) generated_time = models.DateTimeField( verbose_name=_('Report Generated Date and Time'), help_text=_('The time the shake report generated.'), blank=True, null=True, default=None) depth = models.FloatField( verbose_name=_('The depth'), help_text=_('The depth of the event in km unit.')) location = models.PointField( verbose_name=_('Location'), help_text=_( 'The location of the shake event in longitude and latitude.'), srid=4326, max_length=255, null=False, blank=False) location_description = models.CharField( verbose_name=_('Location Description'), help_text=_('The description of the location e.g "Bali".'), max_length=255) felt = models.BooleanField( verbose_name=_('Felt Earthquake'), help_text=_("Set to True if this particular event showed up as felt " "Earthquake in BMKG's List"), default=False) source_type = models.CharField(verbose_name=_('Source Type'), help_text=_('Source type of shake grid'), max_length=30, default='initial') has_corrected = models.BooleanField(verbose_name=_( 'Cache flag to tell that this shakemap has a corrected version.'), default=False) mmi_layer_saved = models.BooleanField(verbose_name=_( 'Cache flag to tell that this shakemap already saved its ' 'contours.'), default=False) shake_grid_saved = models.BooleanField( verbose_name=_('Cache flag to tell that shakemap grid already saved.'), default=False) objects = EarthquakeManager() def __unicode__(self): shake_string = u'Shake event [{0}]'.format(self.event_id_formatted) if self.location_description.strip(): shake_string += u' in {0}'.format(self.location_description) return shake_string @property def reports_queryset(self): return self.reports @property def report_class(self): return EarthquakeReport def delete(self, using=None): # delete all report if self.shake_grid: self.shake_grid.delete() if self.mmi_output: self.mmi_output.delete() for report in self.reports.all(): report.delete(using=using) super(Earthquake, self).delete(using=using) def mark_shakemaps_has_corrected(self): """Mark cache flag of has_corrected. This is to tell that this shakemaps have shakemaps corrected. """ # Only for initial shakemaps if self.source_type == self.INITIAL_SOURCE_TYPE: has_corrected = bool(self.corrected_shakemaps) Earthquake.objects.filter(id=self.id).update( has_corrected=has_corrected) def mark_shakemaps_has_contours(self, layer_saved=False): """Mark cache flag of mmi_layer_saved. This is to tell that this shakemaps have contours saved in database. """ if layer_saved: mmi_layer_saved = layer_saved else: mmi_layer_saved = bool(self.contours.all().count() > 0) Earthquake.objects.filter(id=self.id).update( mmi_layer_saved=mmi_layer_saved) @property def shake_grid_exists(self): return bool(self.shake_grid or self.shake_grid_saved) @property def mmi_layer_exists(self): """Return bool to indicate existences of impact layers""" if self.impact_file_path: return os.path.exists(self.mmi_output_path) return False @property def analysis_zip_path(self): """Return analysis zip path for download.""" dirname = os.path.dirname(self.impact_file_path) basename = os.path.basename(self.impact_file_path) basename_without_ext = split_layer_ext(basename)[0] zip_path = os.path.join(dirname, basename_without_ext + '.zip') if os.path.exists(zip_path): return zip_path return None @property def shake_grid_download_url(self): return reverse('realtime:shake_grid', kwargs={ 'shake_id': self.shake_id, 'source_type': self.source_type }) @property def mmi_layer_download_url(self): return reverse('realtime:earthquake_mmi_contours_list', kwargs={ 'shake_id': self.shake_id, 'source_type': self.source_type }) @property def analysis_zip_download_url(self): return reverse('realtime:analysis_zip', kwargs={ 'shake_id': self.shake_id, 'source_type': self.source_type }) @property def event_id_formatted(self): return EARTHQUAKE_EVENT_ID_FORMAT.format(shake_id=self.shake_id, source_type=self.source_type) @property def grid_xml_filename(self): return '{event_id_formatted}-grid.xml'.format( event_id_formatted=self.event_id_formatted) @property def mmi_layer_filename(self): return '{event_id_formatted}-mmi.geojson'.format( event_id_formatted=self.event_id_formatted) def shakemaps_matching_queryset(self, source_type): """Return a proper queryset match to retrieve matching shakemaps.""" # return a combination of querysets # Find exact info match exact_combination_match = Earthquake.objects.filter( time=self.time, location=self.location, magnitude=self.magnitude, source_type=source_type) if exact_combination_match: return exact_combination_match # Find exact time match exact_time_match = Earthquake.objects.filter(time=self.time, source_type=source_type) return exact_time_match # The following section were commented out until we have a proper # algorithm # # find a range of shakemaps in a given range, sorted with: # # - least time diff # # - least magnitude diff # # - least location diff # # # it is difficult to have an absolute time diff using sql alone, so # # will try to find the match using date range # # Use a maximum of an hour difference # delta_time = datetime.timedelta(minutes=30) # end_time = self.time + delta_time # start_time = self.time - delta_time # # # A range of 1 MMI # magnitude_delta = 1 # # A range of 10 km # distance_delta = 10000 # # within_time_range_with_location_match = Earthquake.objects\ # .filter( # time__range=[start_time, end_time], # source_type=self.CORRECTED_SOURCE_TYPE)\ # .annotate( # # add magnitude diff # magnitude_diff=Func( # F('magnitude') - self.magnitude, # function='ABS'))\ # .distance(self.location)\ # .filter( # magnitude_diff__lte=magnitude_delta, # distance__lte=distance_delta # )\ # .order_by('-time', 'distance', 'magnitude_diff') # # return within_time_range_with_location_match def corrected_shakemaps_queryset(self): """Find a corrected shakemaps matching this one.""" return self.shakemaps_matching_queryset(self.CORRECTED_SOURCE_TYPE) def initial_shakemaps_queryset(self): """Find initial shakemaps matching this one.""" return self.shakemaps_matching_queryset(self.INITIAL_SOURCE_TYPE) @property def corrected_shakemaps(self): """Return the corrected version of the shakemaps if any.""" if self.source_type == self.CORRECTED_SOURCE_TYPE: # Not Applicable return None # Return only the latest one return self.corrected_shakemaps_queryset().order_by( '-shake_id').first() @property def initial_shakemaps(self): """Return the initial version of the shakemaps if any.""" if self.source_type == self.INITIAL_SOURCE_TYPE: # Not Applicable return None # There should be only one return self.initial_shakemaps_queryset().first()
class CampusLocation(models.Model): """ A general location of the campus """ street_address = models.CharField(max_length=240) additional_address = models.CharField( max_length=240, blank=True, help_text="Apartment or Suite Number") locality = models.CharField( help_text="The city or municipality of the campus", max_length=240) region = models.CharField( help_text="State or Province of the campus (Abbreviation preffered)", max_length=100) telephone = models.CharField(help_text="Phone number of the campus", max_length=50, blank=True) zip_code = models.CharField(help_text="Zip or Postal Code of the campus", max_length=10, blank=True) lat = models.FloatField(blank=True, null=True) lon = models.FloatField(blank=True, null=True) point = models.PointField(blank=True, null=True) objects = models.GeoManager() def __str__(self): return "{0} \n {1}, {2}".format(self.street_address, self.locality, self.region) def save(self, *args, **kwargs): if self.point is None: from haystack.utils.geo import Point if self.lon is not None and self.lat is not None: self.point = Point(self.lon, self.lat) elif self.full_address != "": try: from geopy.geocoders import GoogleV3 geolocator = GoogleV3() try: lat, lon = geolocator.geocode(self.full_address)[1] self.lat = lat self.lon = lon self.point = Point(lon, lat) except IndexError: pass except Exception: pass # TODO: else with the street address and geo encoder via google or something super(CampusLocation, self).save(*args, **kwargs) @property def full_address(self): ctx = { 'street_address': self.street_address, 'additional_address': self.additional_address, 'locality': self.locality, 'region': self.region, 'zip_code': self.zip_code, } full_address = """ {street_address} {additional_address}, {locality}, {region}, {zip_code} """.format(**ctx) return full_address
class TaxLotState(models.Model): # The state field names should match pretty close to the pdf, just # because these are the most 'public' fields in terms of # communicating with the cities. # Support finding the property by the import_file import_file = models.ForeignKey(ImportFile, on_delete=models.CASCADE, null=True, blank=True) # Add organization to the tax lot states organization = models.ForeignKey(Organization, on_delete=models.CASCADE) data_state = models.IntegerField(choices=DATA_STATE, default=DATA_STATE_UNKNOWN) merge_state = models.IntegerField(choices=MERGE_STATE, default=MERGE_STATE_UNKNOWN, null=True) custom_id_1 = models.CharField(max_length=255, null=True, blank=True) jurisdiction_tax_lot_id = models.CharField(max_length=2047, null=True, blank=True) block_number = models.CharField(max_length=255, null=True, blank=True) district = models.CharField(max_length=255, null=True, blank=True) address_line_1 = models.CharField(max_length=255, null=True, blank=True) address_line_2 = models.CharField(max_length=255, null=True, blank=True) normalized_address = models.CharField(max_length=255, null=True, blank=True, editable=False) city = models.CharField(max_length=255, null=True, blank=True) state = models.CharField(max_length=255, null=True, blank=True) postal_code = models.CharField(max_length=255, null=True, blank=True) number_properties = models.IntegerField(null=True, blank=True) extra_data = JSONField(default=dict, blank=True) hash_object = models.CharField(max_length=32, null=True, blank=True, default=None) # taxlots can now have lat/long and polygons, points. latitude = models.FloatField(null=True, blank=True) longitude = models.FloatField(null=True, blank=True) long_lat = geomodels.PointField(geography=True, null=True, blank=True) centroid = geomodels.PolygonField(geography=True, null=True, blank=True) bounding_box = geomodels.PolygonField(geography=True, null=True, blank=True) taxlot_footprint = geomodels.PolygonField(geography=True, null=True, blank=True) # A unique building identifier as defined by DOE's UBID project (https://buildingid.pnnl.gov/) # Note that ulid is not an actual project at the moment, but it is similar to UBID in that it # is a unique string that represents the bounding box of the Land (or Lot) ulid = models.CharField(max_length=255, null=True, blank=True) geocoding_confidence = models.CharField(max_length=32, null=True, blank=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) class Meta: index_together = [['hash_object'], ['import_file', 'data_state'], ['import_file', 'data_state', 'merge_state']] def __str__(self): return 'TaxLot State - %s' % self.pk def promote(self, cycle): """ Promote the TaxLotState to the view table for the given cycle Args: cycle: Cycle to assign the view Returns: The resulting TaxLotView (note that it is not returning the TaxLotState) """ # First check if the cycle and the PropertyState already have a view tlvs = TaxLotView.objects.filter(cycle=cycle, state=self) if len(tlvs) == 0: # _log.debug("Found 0 TaxLotViews, adding TaxLot, promoting") # There are no PropertyViews for this property state and cycle. # Most likely there is nothing to match right now, so just # promote it to the view # Need to create a property for this state if self.organization is None: _log.error("organization is None") taxlot = TaxLot.objects.create(organization=self.organization) tlv = TaxLotView.objects.create(taxlot=taxlot, cycle=cycle, state=self) # This is legacy but still needed here to have the tests pass. self.data_state = DATA_STATE_MATCHING self.save() return tlv elif len(tlvs) == 1: # _log.debug("Found 1 PropertyView... Nothing to do") # PropertyView already exists for cycle and state. Nothing to do. return tlvs[0] else: _log.error("Found %s PropertyView" % len(tlvs)) _log.error("This should never occur, famous last words?") return None def to_dict(self, fields=None, include_related_data=True): """ Returns a dict version of the TaxLotState, either with all fields or masked to just those requested. """ # TODO: make this a serializer and/or merge with PropertyState.to_dict if fields: model_fields, ed_fields = split_model_fields(self, fields) extra_data = self.extra_data ed_fields = list(filter(lambda f: f in extra_data, ed_fields)) result = {field: getattr(self, field) for field in model_fields} result['extra_data'] = { field: extra_data[field] for field in ed_fields } # always return id's result['id'] = result['pk'] = self.pk return result d = obj_to_dict(self, include_m2m=include_related_data) return d def save(self, *args, **kwargs): # Calculate and save the normalized address if self.address_line_1 is not None: self.normalized_address = normalize_address_str( self.address_line_1) else: self.normalized_address = None # save a hash of the object to the database for quick lookup from seed.data_importer.tasks import hash_state_object self.hash_object = hash_state_object(self) return super().save(*args, **kwargs) def history(self): """ Return the history of the taxlot state by parsing through the auditlog. Returns only the ids of the parent states and some descriptions. master / \ / \ parent1 parent2 In the records, parent2 is most recent, so make sure to navigate parent two first since we are returning the data in reverse over (that is most recent changes first) :return: list, history as a list, and the master record """ """Return history in reverse order.""" history = [] master = { 'state_id': self.id, 'state_data': self, 'date_edited': None, } def record_dict(log): filename = None if not log.import_filename else path.basename( log.import_filename) if filename: # Attempt to remove NamedTemporaryFile suffix name, ext = path.splitext(filename) pattern = re.compile('(.*?)(_[a-zA-Z0-9]{7})$') match = pattern.match(name) if match: filename = match.groups()[0] + ext return { 'state_id': log.state.id, 'state_data': log.state, 'date_edited': convert_to_js_timestamp(log.created), 'source': log.get_record_type_display(), 'filename': filename, # 'changed_fields': json.loads(log.description) if log.record_type == AUDIT_USER_EDIT else None } log = TaxLotAuditLog.objects.select_related( 'state', 'parent1', 'parent2').filter(state_id=self.id).order_by('-id').first() if log: master = { 'state_id': log.state.id, 'state_data': log.state, 'date_edited': convert_to_js_timestamp(log.created), } # Traverse parents and add to history if log.name in [ 'Manual Match', 'System Match', 'Merge current state in migration' ]: done_searching = False while not done_searching: # if there is no parents, then break out immediately if (log.parent1_id is None and log.parent2_id is None ) or log.name == 'Manual Edit': break # initalize the tree to None everytime. If not new tree is found, then we will not iterate tree = None # Check if parent2 has any other parents or is the original import creation. Start with parent2 # because parent2 will be the most recent import file. if log.parent2: if log.parent2.name in [ 'Import Creation', 'Manual Edit' ]: record = record_dict(log.parent2) history.append(record) elif log.parent2.name == 'System Match' and log.parent2.parent1.name == 'Import Creation' and log.parent2.parent2.name == 'Import Creation': # Handle case where an import file matches within itself, and proceeds to match with # existing records record = record_dict(log.parent2.parent2) history.append(record) record = record_dict(log.parent2.parent1) history.append(record) else: tree = log.parent2 if log.parent1: if log.parent1.name in [ 'Import Creation', 'Manual Edit' ]: record = record_dict(log.parent1) history.append(record) elif log.parent1.name == 'System Match' and log.parent1.parent1.name == 'Import Creation' and log.parent1.parent2.name == 'Import Creation': # Handle case where an import file matches within itself, and proceeds to match with # existing records record = record_dict(log.parent1.parent2) history.append(record) record = record_dict(log.parent1.parent1) history.append(record) else: tree = log.parent1 if not tree: done_searching = True else: log = tree elif log.name == 'Manual Edit': record = record_dict(log.parent1) history.append(record) elif log.name == 'Import Creation': record = record_dict(log) history.append(record) return history, master @classmethod def coparent(cls, state_id): """ Return the coparent of the TaxLotState. This will query the TaxLotAuditLog table to determine if there is a coparent and return it if it is found. The state_id needs to be the base ID of when the original record was imported :param state_id: integer, state id to find coparent. :return: dict """ coparents = list( TaxLotState.objects.raw( """ WITH creation_id AS ( SELECT pal.id, pal.state_id AS original_state_id FROM seed_taxlotauditlog pal WHERE pal.state_id = %s AND pal.name = 'Import Creation' AND pal.import_filename IS NOT NULL ), audit_id AS ( SELECT audit_log.id, audit_log.state_id, audit_log.parent1_id, audit_log.parent2_id, audit_log.parent_state1_id, audit_log.parent_state2_id, cid.original_state_id FROM creation_id cid, seed_taxlotauditlog audit_log WHERE audit_log.parent1_id = cid.id OR audit_log.parent2_id = cid.id ) SELECT ps.id, ps.custom_id_1, ps.block_number, ps.district, ps.address_line_1, ps.address_line_2, ps.city, ps.state, ps.postal_code, ps.extra_data, ps.number_properties, ps.jurisdiction_tax_lot_id, ps.geocoding_confidence, NULL FROM seed_taxlotstate ps, audit_id aid WHERE (ps.id = aid.parent_state1_id AND aid.parent_state1_id <> aid.original_state_id) OR (ps.id = aid.parent_state2_id AND aid.parent_state2_id <> aid.original_state_id);""", [int(state_id)])) # reduce this down to just the fields that were returns and convert to dict. This is # important because the fields that were not queried will be deferred and require a new # query to retrieve. keep_fields = [ 'id', 'custom_id_1', 'jurisdiction_tax_lot_id', 'block_number', 'district', 'address_line_1', 'address_line_2', 'city', 'state', 'postal_code', 'number_properties', 'extra_data' ] coparents = [{key: getattr(c, key) for key in keep_fields} for c in coparents] return coparents, len(coparents) @classmethod def merge_relationships(cls, merged_state, state1, state2): """Stub to implement if merging TaxLotState relationships is needed""" return None
class Place(models.Model): name = models.TextField() place_id = models.TextField(primary_key=True) lat = models.FloatField() lng = models.FloatField() user_rating = models.FloatField() num_ratings = models.FloatField() address = models.TextField() area = models.ForeignKey(to='Area', null=True, blank=True, on_delete=models.SET_NULL) email_contact = models.EmailField(null=True, blank=True) place_url = models.URLField(null=True, blank=True, max_length=1000) image_url = models.URLField(null=True, blank=True, max_length=1000) image_attribution = models.TextField(null=True, blank=True) gift_card_url = models.URLField(null=True, blank=True, max_length=1000) takeout_url = models.URLField(null=True, blank=True, max_length=1000) donation_url = models.URLField(null=True, blank=True, max_length=1000) geom = models.PointField(srid=4326, null=True, blank=True) place_types = models.TextField(null=True, blank=True) @classmethod def dump_names_for_site(cls, out_fl): all_places = cls.objects.all() output = [] for place in all_places: info = ( """{{key: "{place_id}", address: "{address}", name: "{name}"}},""" .format(name=place.name, address=place.get_short_address(), place_id=place.place_id)) output.append(info) with open(out_fl, 'w') as fl: fl.writelines(output) @classmethod def dump_places_missing_photos(cls, out_fl): missing_photo = cls.objects.filter(image_url=None) names = ['%s\n' % place.place_id for place in missing_photo] with open(out_fl, 'w') as fl: fl.writelines(names) @classmethod def dump_places_missing_website(cls, out_fl): missing_photo = cls.objects.filter(place_url=None) names = ['%s\n' % place.place_id for place in missing_photo] with open(out_fl, 'w') as fl: fl.writelines(names) def get_image_url(self): return self.image_url or "http://TODO/placeholder" def get_collage_picture(self): from places.helper import get_place_collage_picture filename = get_place_collage_picture(self.place_id) return filename def get_short_address(self): return self.address.split(', CA')[0] def to_json(self): return { 'name': self.name, 'address': self.get_short_address(), 'giftCardURL': self.gift_card_url, 'takeoutURL': self.takeout_url, 'donationURL': self.donation_url, 'placeURL': self.place_url, 'emailContact': self.email_contact, 'imageURL': self.get_image_url(), 'placeID': self.place_id, 'area': self.area.key if self.area else None } def to_typeahead_json(self): return { 'name': self.name, 'address': self.get_short_address(), 'key': self.place_id, 'image_attribution': self.image_attribution } def __str__(self): return '%s (%s)' % (self.name, self.address) def save(self, *args, **kwargs): from places.helper import check_link_against_blacklist if self.gift_card_url and not check_link_against_blacklist( self.gift_card_url): raise Exception("Bad Link Saved") if (self.lat and self.lng): self.geom = Point([float(x) for x in (self.lng, self.lat)], srid=4326) super(self.__class__, self).save(*args, **kwargs)
class MinusOneSRID(models.Model): geom = models.PointField(srid=-1) # Minus one SRID. objects = models.GeoManager()
class Neighborhood(models.Model): name = models.TextField() key = models.TextField(primary_key=True) places = models.ManyToManyField(through='NeighborhoodEntry', to='Place') lat = models.FloatField() lng = models.FloatField() geom = models.PointField(srid=4326, null=True) bounds = models.PolygonField(srid=4326, null=True, blank=True) photo_url = models.URLField(blank=True, null=True, max_length=1000) photo_attribution = models.TextField(blank=True, null=True) area = models.ForeignKey(to='Area', on_delete=models.SET_NULL, blank=True, null=True) rank = models.IntegerField(null=True, blank=True) def place_list(self, limit, offset): hardcoded = [ x.place for x in NeighborhoodEntry.objects.filter( neighborhood=self).order_by('rank') ] if offset == 0: to_fetch = (limit - len(hardcoded)) + 1 else: to_fetch = limit + 1 if self.bounds: close_by = Place.objects.filter( Q(geom__within=self.bounds) ).annotate(has_card=models.Count('gift_card_url')).annotate( has_donation=models.Count('donation_url')).annotate( has_takeout=models.Count('takeout_url')).exclude( place_id__in=[x.place_id for x in hardcoded]).order_by( '-has_card', '-has_takeout', '-has_donation', '-num_ratings')[offset:offset + to_fetch] else: close_by = Place.objects.filter( Q(geom__distance_lt=(self.geom, D(m=2500))) ).exclude(place_id__in=[x.place_id for x in hardcoded]).annotate( has_card=models.Count('gift_card_url')).annotate( has_donation=models.Count('donation_url')).annotate( has_takeout=models.Count('takeout_url')).annotate( distance=Distance('geom', self.geom)).order_by( '-has_card', '-has_takeout', '-has_donation', 'distance')[offset:offset + to_fetch] more_available = len(close_by) == to_fetch if offset == 0: joined = (hardcoded + list(close_by)) else: joined = list(close_by) end_list = -1 if more_available else len(joined) return joined[0:end_list], more_available def to_json(self): return {"name": self.name, "key": self.key, "image": self.photo_url} def save(self, *args, **kwargs): if (self.lat and self.lng): self.geom = Point([float(x) for x in (self.lng, self.lat)], srid=4326) super(self.__class__, self).save(*args, **kwargs)
class Alert(models.Model): timestamp = models.DateTimeField(default=timezone.now) geolocation = models.PointField(dim=2) active = models.BooleanField(default=True)
class TrafficSignReal(SourceControlModel, SoftDeleteModel, UserControlModel): id = models.UUIDField(primary_key=True, unique=True, editable=False, default=uuid.uuid4) traffic_sign_plan = models.ForeignKey( TrafficSignPlan, verbose_name=_("Traffic Sign Plan"), on_delete=models.PROTECT, blank=True, null=True, ) location = models.PointField(_("Location (3D)"), dim=3, srid=settings.SRID) height = models.IntegerField(_("Height"), blank=True, null=True) direction = models.IntegerField(_("Direction"), default=0) device_type = models.ForeignKey( TrafficControlDeviceType, verbose_name=_("Device type"), on_delete=models.PROTECT, limit_choices_to=Q( Q(target_model=None) | Q(target_model=DeviceTypeTargetModel.TRAFFIC_SIGN)), blank=False, null=True, ) value = models.IntegerField(_("Traffic Sign Code value"), blank=True, null=True) legacy_code = models.CharField(_("Legacy Traffic Sign Code"), max_length=32, blank=True, null=True) txt = models.CharField(_("Txt"), max_length=254, blank=True, null=True) mount_real = models.ForeignKey( MountReal, verbose_name=_("Mount Real"), on_delete=models.PROTECT, blank=True, null=True, ) mount_type = models.ForeignKey( MountType, verbose_name=_("Mount type"), blank=True, null=True, on_delete=models.SET_NULL, ) installation_date = models.DateField(_("Installation date"), blank=True, null=True) installation_status = EnumField( InstallationStatus, verbose_name=_("Installation status"), max_length=10, blank=True, null=True, ) installation_id = models.CharField(_("Installation id"), max_length=254, blank=True, null=True) installation_details = models.CharField(_("Installation details"), max_length=254, blank=True, null=True) permit_decision_id = models.CharField(_("Permit decision id"), max_length=254, blank=True, null=True) validity_period_start = models.DateField(_("Validity period start"), blank=True, null=True) validity_period_end = models.DateField(_("Validity period end"), blank=True, null=True) condition = EnumIntegerField( Condition, verbose_name=_("Condition"), blank=True, null=True, ) affect_area = models.PolygonField(_("Affect area (2D)"), srid=settings.SRID, blank=True, null=True) scanned_at = models.DateTimeField(_("Scanned at"), blank=True, null=True) size = EnumField( Size, verbose_name=_("Size"), max_length=1, blank=True, null=True, ) reflection_class = EnumField( Reflection, verbose_name=_("Reflection"), max_length=2, blank=True, null=True, ) surface_class = EnumField( Surface, verbose_name=_("Surface"), max_length=6, blank=True, null=True, ) seasonal_validity_period_start = models.DateField( _("Seasonal validity period start"), blank=True, null=True) seasonal_validity_period_end = models.DateField( _("Seasonal validity period end"), blank=True, null=True) owner = models.ForeignKey( "traffic_control.Owner", verbose_name=_("Owner"), blank=False, null=False, on_delete=models.PROTECT, ) manufacturer = models.CharField(_("Manufacturer"), max_length=254, blank=True, null=True) rfid = models.CharField(_("RFID"), max_length=254, blank=True, null=True) lifecycle = EnumIntegerField(Lifecycle, verbose_name=_("Lifecycle"), default=Lifecycle.ACTIVE) road_name = models.CharField(_("Road name"), max_length=254, blank=True, null=True) lane_number = EnumField(LaneNumber, verbose_name=_("Lane number"), default=LaneNumber.MAIN_1, blank=True) lane_type = EnumField( LaneType, verbose_name=_("Lane type"), default=LaneType.MAIN, blank=True, ) location_specifier = EnumIntegerField( LocationSpecifier, verbose_name=_("Location specifier"), default=LocationSpecifier.RIGHT, blank=True, null=True, ) operation = models.CharField(_("Operation"), max_length=64, blank=True, null=True) attachment_url = models.URLField(_("Attachment url"), max_length=500, blank=True, null=True) objects = TrafficSignRealQuerySet.as_manager() class Meta: db_table = "traffic_sign_real" verbose_name = _("Traffic Sign Real") verbose_name_plural = _("Traffic Sign Reals") unique_together = ["source_name", "source_id"] def __str__(self): return f"{self.id} {self.device_type}" def save(self, *args, **kwargs): if self.device_type and not self.device_type.validate_relation( DeviceTypeTargetModel.TRAFFIC_SIGN): raise ValidationError( f'Device type "{self.device_type}" is not allowed for traffic signs' ) if not self.device_type: self.device_type = ( TrafficControlDeviceType.objects.for_target_model( DeviceTypeTargetModel.TRAFFIC_SIGN).filter( legacy_code=self.legacy_code).order_by("code").first()) super().save(*args, **kwargs) def has_additional_signs(self): return self.additional_signs.active().exists() @transaction.atomic def soft_delete(self, user): super().soft_delete(user) self.additional_signs.soft_delete(user)
class Powerplant(models.Model): """Power plants (status quo) Attributes ---------- id : DB id ags : Municipality key (Amtlicher Gemeindeschlüssel), refers to :class:`stemp_abw.models.RegMun` capacity : Nominal power in MW chp : Indicates if plant is of type CHP (combined heat and power) com_month : Month of commissioning com_year : Year of commissioning comment : Comment decom_month : Month of decommissioning decom_year : Year of decommissioning efficiency : Efficiency energy_source_level_1 : Indicates if renewable or conventional energy_source_level_2 : Indicates energy source energy_source_level_3 : More specific energy source geometry : Geometry SRID: EPSG:4326 (WGS84) technology : Technology thermal_capacity : Nominal thermal nominal power, if applicable coastdat2 : No. of coastdat2 weather cell (reegis) capacity_in : Capacity of inflow federal_state : Abbreviation of federal state name (2 letters according to ISO 3166-2:DE) Notes ----- Most of the attributes correspond to the OPSD dataset, some were added by reegis. """ id = models.BigIntegerField(primary_key=True) ags = models.ForeignKey(RegMun, on_delete=models.DO_NOTHING) capacity = models.FloatField(blank=True, null=True) chp = models.TextField(blank=True, null=True) com_month = models.FloatField(blank=True, null=True) com_year = models.FloatField(blank=True, null=True) comment = models.TextField(blank=True, null=True) decom_month = models.BigIntegerField(blank=True, null=True) decom_year = models.BigIntegerField(blank=True, null=True) efficiency = models.FloatField(blank=True, null=True) energy_source_level_1 = models.TextField(blank=True, null=True) energy_source_level_2 = models.TextField(blank=True, null=True) energy_source_level_3 = models.TextField(blank=True, null=True) geometry = geomodels.PointField(blank=True, null=True) state = models.TextField(blank=True, null=True) technology = models.TextField(blank=True, null=True) thermal_capacity = models.FloatField(blank=True, null=True) coastdat2 = models.FloatField(blank=True, null=True) capacity_in = models.FloatField(blank=True, null=True) federal_state = models.TextField(blank=True, null=True)
class City(NamedModel): point = models.PointField()