class TreeImportEvent(GenericImportEvent): """ A TreeImportEvent represents an attempt to upload a csv containing tree/plot information """ import_schema_version = 2 # Update if any column header name changes import_type = 'tree' plot_length_conversion_factor = models.FloatField(default=1.0) plot_width_conversion_factor = models.FloatField(default=1.0) diameter_conversion_factor = models.FloatField(default=1.0) tree_height_conversion_factor = models.FloatField(default=1.0) canopy_height_conversion_factor = models.FloatField(default=1.0) class Meta: app_label = 'importer' def row_set(self): return self.treeimportrow_set def __unicode__(self): return _('Tree Import #%s') % self.pk def get_udf_column_name(self, udf_def): name = self._get_udf_name(udf_def) return name.lower() def _get_udf_name(self, udf_def): # Prefix with model name, e.g. "Density" -> "Tree: Density" model_name = udf_def.model_type if model_name == 'Plot': model_name = 'Planting Site' return "%s: %s" % (model_name, udf_def.name) def _get_udf_names(self): def udf_names(model_name): return tuple( self._get_udf_name(udf_def) for udf_def in udf_defs(self.instance, model_name) if not udf_def.iscollection) return udf_names('Plot') + udf_names('Tree') def ordered_legal_fields(self): udf_names = self._get_udf_names() udf_names = tuple(n.lower() for n in udf_names) return fields.trees.ALL + udf_names def ordered_legal_fields_title_case(self): udf_names = self._get_udf_names() return fields.title_case(fields.trees.ALL) + udf_names def _required_fields(self): return {fields.trees.POINT_X, fields.trees.POINT_Y} def legal_and_required_fields(self): legal_fields = set(self.ordered_legal_fields()) return legal_fields, self._required_fields() def legal_and_required_fields_title_case(self): legal_fields = set(self.ordered_legal_fields_title_case()) return legal_fields, fields.title_case(self._required_fields())
class DataPoint(models.Model): location = models.PointField() uSv = models.FloatField() time = models.IntegerField()
class Sequence(models.Model): unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) user = models.ForeignKey(UserModel, on_delete=models.CASCADE) camera_make = models.ForeignKey(CameraMake, on_delete=models.CASCADE, null=True, blank=True) captured_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(null=True, blank=True) seq_key = models.CharField( max_length=100, null=True, blank=True, verbose_name="Mapillary Sequence Key", ) pano = models.BooleanField(default=False) user_key = models.CharField( max_length=100, null=True, blank=True, verbose_name="Mapillary User Key", ) username = models.CharField( max_length=100, null=True, blank=True, verbose_name="Mapillary Username", ) geometry_coordinates = models.LineStringField(null=True, blank=True, srid=4326) geometry_coordinates_ary = ArrayField(ArrayField( models.FloatField(default=1)), null=True, blank=True) coordinates_cas = ArrayField(models.FloatField(default=0), null=True, blank=True) coordinates_image = ArrayField(models.CharField(default='', max_length=100), null=True, blank=True) is_uploaded = models.BooleanField(default=False) is_private = models.BooleanField(default=False) updated_at = models.DateTimeField(default=datetime.now, blank=True) name = models.CharField(max_length=100, default='') description = models.TextField(default='') transport_type = models.ForeignKey(TransType, on_delete=models.CASCADE, null=True) tag = models.ManyToManyField(Tag) image_count = models.IntegerField(default=0) is_mapillary = models.BooleanField(default=True) is_published = models.BooleanField(default=True) is_image_download = models.BooleanField(default=False) is_map_feature = models.BooleanField(default=False) google_street_view = models.BooleanField(default=False) strava = models.CharField(max_length=50, null=True, blank=True) distance = models.FloatField(null=True, blank=True) objects = models.Manager() vector_tiles = CustomSequenceMVTManager( geo_col='geometry_coordinates', select_columns=['seq_key', 'unique_id'], is_show_id=False, source_layer='mtp-sequences') def get_absolute_url(self): from django.urls import reverse return reverse('sequence.sequence_detail', kwargs={'unique_id': str(self.unique_id)}) def get_image_count(self): if self.coordinates_image is not None: return len(self.coordinates_image) else: return 0 def get_tag_str(self): tags = [] if self.tag.all().count() == 0: return '' for tag in self.tag.all(): if tag and tag.is_actived: tags.append(tag.name) if len(tags) > 0: return ', '.join(tags) else: return '' def get_tags(self): tags = [] if self.tag.all().count() == 0: return '' for tag in self.tag.all(): if tag and tag.is_actived: tags.append(tag.name) return tags def get_short_description(self): description = self.description if len(description) > 100: return description[0:100] + '...' else: return description def get_first_image_key(self): if len(self.coordinates_image) > 0: return self.coordinates_image[0] else: return '' def get_mapillary_username(self): return self.username def get_like_count(self): liked_guidebook = SequenceLike.objects.filter(sequence=self) if not liked_guidebook: return 0 else: return liked_guidebook.count() def get_distance(self): all_distance = 0 if self.geometry_coordinates_ary is None: return all_distance if len(self.geometry_coordinates_ary) > 0: first_point = self.geometry_coordinates_ary[0] for i in range(len(self.geometry_coordinates_ary) - 1): if i == 0: continue second_point = self.geometry_coordinates_ary[i] d = distance(first_point, second_point) first_point = second_point all_distance += d all_distance = "%.3f" % all_distance return all_distance def get_cover_image(self): image_keys = self.coordinates_image if image_keys is None: return None if len(image_keys) > 0: return image_keys[0] else: return None def get_first_point_lat(self): if self.geometry_coordinates_ary is None: return None lat = self.geometry_coordinates_ary[0][1] return lat def get_first_point_lng(self): if self.geometry_coordinates_ary is None: return None lng = self.geometry_coordinates_ary[0][0] return lng
class Pavement(models.Model): Aerodrome_Entity = models.ForeignKey(Aerodrome_Entity,null=True,on_delete=models.SET_NULL) Pavement_Name = models.CharField(max_length=100) Width = models.FloatField(null=True) Shoulder_Width = models.FloatField(null=True) Design_Types = [('f','Flexible Design'),('r','Rigid Pavement')] Pavement_Design_Type = models.CharField(max_length=1,choices=Design_Types,null=True) Subgrade_Density = models.FloatField(null=True) Subgrade_Soil_Classification = models.CharField(max_length=100,null=True) Soil_Shear_Strength = models.FloatField(null=True) Soil_DCP_Test_Result = models.FloatField(null=True) Soil_CPT_Test_Result = models.FloatField(null=True) Soil_Percolation_Rate = models.FloatField(null=True) SubBase_Material = models.CharField(max_length=100,null=True) SubBase_Thickness = models.FloatField(null=True) Joint_Spacing = models.FloatField(null=True) SubBase_Base_Seperation_Material = models.CharField(max_length=100,null=True) Base_Material = models.CharField(max_length=100,null=True) Base_Thickness = models.CharField(max_length=100,null=True) Concrete_Compressive_Strength = models.FloatField(null=True) Asphalt_Thickness = models.FloatField(null=True) Binder_Thickness = models.FloatField(null=True) Wearing_Thickness = models.FloatField(null=True) Drainage_Longitudinal_Slope = models.FloatField(null=True) Drainage_Cross_Slope = models.FloatField(null=True) def __str__(self): return self.Pavement_Name class Meta: ordering=['Pavement_Name'] verbose_name_plural = "Pavements"
class Language(CulturalModel): """ family aliases (comma separated) total_schools avg_hrs_wk_languages_in_school ece_programs avg_hrs_wk_languages_in_ece language_nests avg_hrs_wk_languages_in_language_nests community_adult_language_classes language_audio (should include speaker, recorder, date recorded fields) greeting_audio (should include speaker, recorder, date recorded fields) fv_guid fv_url """ family = models.ForeignKey(LanguageFamily, null=True, on_delete=models.SET_NULL, blank=True) total_schools = models.IntegerField(default=0) avg_hrs_wk_languages_in_school = models.FloatField(default=0) ece_programs = models.IntegerField(default=0) avg_hrs_wk_languages_in_ece = models.FloatField(default=0) language_nests = models.IntegerField(default=0) avg_hrs_wk_languages_in_language_nests = models.FloatField(default=0) community_adult_language_classes = models.IntegerField(default=0) language_audio = models.ForeignKey( Recording, on_delete=models.SET_NULL, null=True, blank=True, related_name="languages", ) greeting_audio = models.ForeignKey( Recording, on_delete=models.SET_NULL, null=True, blank=True, related_name="greeting_languages", ) fv_guid = models.CharField(max_length=40, blank=True, default="") fv_archive_link = models.URLField(max_length=255, blank=True, default="") color = models.CharField(max_length=31, default="") regions = models.CharField(max_length=255, default="", blank=True) sleeping = models.BooleanField(default=False) notes = models.TextField(default="", blank=True) # Deprecated (switch to CommunityLanguageStats) fluent_speakers = models.IntegerField( default=0) # sum of field_tm_lna2_on_fluent_sum_value some_speakers = models.IntegerField( default=0) # field_tm_lna2_on_some_sum_value learners = models.IntegerField( default=0) # sum of field_tm_lna2_on_lrn_sum_value pop_total_value = models.IntegerField( default=0) # sum of field_tm_lna2_pop_total_value geom = models.PolygonField(null=True, default=None, blank=True) bbox = models.PolygonField(null=True, default=None, blank=True) # Deprecated, use recording instead. audio_file = models.FileField(null=True, blank=True)
class TrailSection(models.Model): objectid = models.IntegerField(null=True, blank=True) trailsection_id = models.AutoField(primary_key=True) shape = models.GeometryField(null=True, blank=True, srid=4326, dim=3) hikster_creation = models.IntegerField(null=True, blank=True) # 2017/07/11 Matty: in_trail field was used to decide which trail section should be shown on Map # in_trail = models.IntegerField(default=0) """ 2017/07/11 Matty Wang Changes made to Trail Section Model due to Hikster decision of implementing DB level triggers as Geotrek to automatically split/merge paths Geotrek is an open source project: https://github.com/GeotrekCE/Geotrek-admin older entries that existed before adding date_insert and date_update were given default value of the date when the new model is implemented """ date_insert = models.DateTimeField(auto_now_add=True, editable=False) date_update = models.DateTimeField(auto_now=True, editable=False) departure = models.CharField(null=True, blank=True, default="", max_length=250) arrival = models.CharField(null=True, blank=True, default="", max_length=250) valid = models.BooleanField(default=True) visible = models.BooleanField(default=True) name = models.CharField(null=True, blank=True, max_length=20) comments = models.TextField(null=True, blank=True) # external_id was introduced here because it appeared in Geotrek's triggers; not sure yet how relevant it is for us external_id = models.CharField(max_length=128, blank=True, null=True) shape_2d = models.LineStringField(blank=True, null=True, srid=4326, dim=2, spatial_index=False) ascent = models.IntegerField(default=0, null=True, blank=True) # denivellee_positive descent = models.IntegerField(default=0, null=True, blank=True) # denivellee_negative min_elevation = models.IntegerField(default=0, null=True, blank=True) # altitude_minimum max_elevation = models.IntegerField(default=0, null=True, blank=True) # altitude_maximum slope = models.FloatField(null=True, blank=True, default=0.0) # pente lgth = models.FloatField(default=0.0, null=True, blank=True) trailsection_activities_uuid = models.CharField(max_length=60, null=True, blank=True) objects_with_eager_loading = TrailSectionManager() objects = models.Manager() def __str__(self): return f"{self.trailsection_id} - {self.name}" @property def activity_ids(self): return self.activities.values_list("activity__id", flat=True)
class Trip(models.Model): """Jízdy""" DIRECTIONS = [ ('trip_to', _(u"Tam")), ('trip_from', _(u"Zpět")), ('recreational', _(u"Výlet")), ] DIRECTIONS_DICT = dict(DIRECTIONS) class Meta: verbose_name = _("Jízda") verbose_name_plural = _("Jízdy") unique_together = (("user_attendance", "date", "direction"),) ordering = ('date', '-direction') objects = BulkUpdateManager() user_attendance = models.ForeignKey( 'UserAttendance', related_name="user_trips", null=True, blank=False, default=None, on_delete=models.CASCADE, ) direction = models.CharField( verbose_name=_(u"Směr cesty"), choices=DIRECTIONS, max_length=20, default=None, null=False, blank=False, ) date = models.DateField( verbose_name=_(u"Datum cesty"), default=datetime.date.today, null=False, ) commute_mode = models.ForeignKey( 'CommuteMode', verbose_name=_("Dopravní prostředek"), on_delete=models.CASCADE, default=1, null=False, blank=False, ) track = models.MultiLineStringField( verbose_name=_(u"trasa"), help_text=MAP_DESCRIPTION, srid=4326, null=True, blank=True, geography=True, ) gpx_file = models.FileField( verbose_name=_(u"GPX soubor"), help_text=_( mark_safe( "Zadat trasu nahráním souboru GPX. " "Pro vytvoření GPX souboru s trasou můžete použít vyhledávání na naší " "<a href='https://mapa.prahounakole.cz/#hledani' target='_blank'>mapě</a>." ), ), upload_to=normalize_gpx_filename, blank=True, null=True, max_length=512, ) distance = models.FloatField( verbose_name=_(u"Ujetá vzdálenost (km)"), null=True, blank=True, default=None, validators=[ MaxValueValidator(1000), MinValueValidator(0), ], ) duration = models.PositiveIntegerField( verbose_name=_("Doba v sekundách"), null=True, blank=True, ) from_application = models.BooleanField( verbose_name=_(u"Nahráno z aplikace"), default=False, null=False, ) source_application = models.CharField( verbose_name=_("Zdrojová aplikace"), max_length=255, null=True, blank=True, ) source_id = models.CharField( verbose_name=_("Identifikátor v původní aplikaci"), max_length=255, null=True, blank=True, ) created = models.DateTimeField( verbose_name=_(u"Datum vytvoření"), auto_now_add=True, null=True, ) updated = models.DateTimeField( verbose_name=_(u"Datum poslední změny"), auto_now=True, null=True, ) def active(self): return self.user_attendance.campaign.day_active(self.date) def get_commute_mode_display(self): if self.commute_mode.slug == 'no_work' and self.date > util.today(): return _('Dovolená') return str(self.commute_mode) def get_direction_display(self): return self.DIRECTIONS_DICT[self.direction] def get_application_link(self): app_links = { "strava": "https://www.strava.com/", "urbancyclers": "https://play.google.com/store/apps/details?id=com.umotional.bikeapp", "SuperLife": "https://play.google.com/store/apps/details?id=cz.cncenter.superlife", } if self.source_application in app_links: return app_links[self.source_application] def get_trip_link(self): if self.source_application == 'strava': return "<a href='%sactivities/%s'>View on Strava</a>" % (self.get_application_link(), self.source_id) return ""
class Trek(StructureRelated, PicturesMixin, PublishableMixin, MapEntityMixin, Topology): topo_object = models.OneToOneField(Topology, parent_link=True, db_column='evenement') departure = models.CharField(verbose_name=_(u"Departure"), max_length=128, blank=True, help_text=_(u"Departure description"), db_column='depart') arrival = models.CharField(verbose_name=_(u"Arrival"), max_length=128, blank=True, help_text=_(u"Arrival description"), db_column='arrivee') description_teaser = models.TextField(verbose_name=_(u"Description teaser"), blank=True, help_text=_(u"A brief summary (map pop-ups)"), db_column='chapeau') description = models.TextField(verbose_name=_(u"Description"), blank=True, db_column='description', help_text=_(u"Complete description")) ambiance = models.TextField(verbose_name=_(u"Ambiance"), blank=True, db_column='ambiance', help_text=_(u"Main attraction and interest")) access = models.TextField(verbose_name=_(u"Access"), blank=True, db_column='acces', help_text=_(u"Best way to go")) disabled_infrastructure = models.TextField(verbose_name=_(u"Disabled infrastructure"), db_column='handicap', blank=True, help_text=_(u"Any specific infrastructure")) duration = models.FloatField(verbose_name=_(u"Duration"), default=0, blank=True, db_column='duree', help_text=_(u"In hours (1.5 = 1 h 30, 24 = 1 day, 48 = 2 days)"), validators=[MinValueValidator(0)]) is_park_centered = models.BooleanField(verbose_name=_(u"Is in the midst of the park"), db_column='coeur', help_text=_(u"Crosses center of park")) advised_parking = models.CharField(verbose_name=_(u"Advised parking"), max_length=128, blank=True, db_column='parking', help_text=_(u"Where to park")) parking_location = models.PointField(verbose_name=_(u"Parking location"), db_column='geom_parking', srid=settings.SRID, spatial_index=False, blank=True, null=True) public_transport = models.TextField(verbose_name=_(u"Public transport"), blank=True, db_column='transport', help_text=_(u"Train, bus (see web links)")) advice = models.TextField(verbose_name=_(u"Advice"), blank=True, db_column='recommandation', help_text=_(u"Risks, danger, best period, ...")) themes = models.ManyToManyField(Theme, related_name="treks", db_table="o_r_itineraire_theme", blank=True, null=True, verbose_name=_(u"Themes"), help_text=_(u"Main theme(s)")) networks = models.ManyToManyField('TrekNetwork', related_name="treks", db_table="o_r_itineraire_reseau", blank=True, null=True, verbose_name=_(u"Networks"), help_text=_(u"Hiking networks")) practice = models.ForeignKey('Practice', related_name="treks", blank=True, null=True, verbose_name=_(u"Practice"), db_column='pratique') accessibilities = models.ManyToManyField('Accessibility', related_name="treks", db_table="o_r_itineraire_accessibilite", blank=True, null=True, verbose_name=_(u"Accessibility")) route = models.ForeignKey('Route', related_name='treks', blank=True, null=True, verbose_name=_(u"Route"), db_column='parcours') difficulty = models.ForeignKey('DifficultyLevel', related_name='treks', blank=True, null=True, verbose_name=_(u"Difficulty"), db_column='difficulte') web_links = models.ManyToManyField('WebLink', related_name="treks", db_table="o_r_itineraire_web", blank=True, null=True, verbose_name=_(u"Web links"), help_text=_(u"External resources")) related_treks = models.ManyToManyField('self', through='TrekRelationship', verbose_name=_(u"Related treks"), symmetrical=False, help_text=_(u"Connections between treks"), related_name='related_treks+') # Hide reverse attribute information_desks = models.ManyToManyField(tourism_models.InformationDesk, related_name='treks', db_table="o_r_itineraire_renseignement", blank=True, null=True, verbose_name=_(u"Information desks"), help_text=_(u"Where to obtain information")) points_reference = models.MultiPointField(verbose_name=_(u"Points of reference"), db_column='geom_points_reference', srid=settings.SRID, spatial_index=False, blank=True, null=True) source = models.ManyToManyField('common.RecordSource', blank=True, related_name='treks', verbose_name=_("Source"), db_table='o_r_itineraire_source') portal = models.ManyToManyField('common.TargetPortal', blank=True, related_name='treks', verbose_name=_("Portal"), db_table='o_r_itineraire_portal') eid = models.CharField(verbose_name=_(u"External id"), max_length=128, blank=True, null=True, db_column='id_externe') eid2 = models.CharField(verbose_name=_(u"Second external id"), max_length=128, blank=True, null=True, db_column='id_externe2') objects = Topology.get_manager_cls(models.GeoManager)() category_id_prefix = 'T' capture_map_image_waitfor = '.poi_enum_loaded.services_loaded.info_desks_loaded.ref_points_loaded' class Meta: db_table = 'o_t_itineraire' verbose_name = _(u"Trek") verbose_name_plural = _(u"Treks") ordering = ['name'] def __unicode__(self): return self.name @models.permalink def get_map_image_url(self): return ('trekking:trek_map_image', [], {'pk': str(self.pk), 'lang': get_language()}) def get_map_image_path(self): basefolder = os.path.join(settings.MEDIA_ROOT, 'maps') if not os.path.exists(basefolder): os.makedirs(basefolder) return os.path.join(basefolder, '%s-%s-%s.png' % (self._meta.module_name, self.pk, get_language())) @models.permalink def get_document_public_url(self): """ Override ``geotrek.common.mixins.PublishableMixin`` """ return ('trekking:trek_document_public', [], {'lang': get_language(), 'pk': self.pk, 'slug': self.slug}) @property def related(self): return self.related_treks.exclude(deleted=True).exclude(pk=self.pk).distinct() @classproperty def related_verbose_name(cls): return _("Related treks") @property def relationships(self): # Does not matter if a or b return TrekRelationship.objects.filter(trek_a=self) @property def published_relationships(self): return self.relationships.filter(trek_b__published=True) @property def poi_types(self): if settings.TREKKING_TOPOLOGY_ENABLED: # Can't use values_list and must add 'ordering' because of bug: # https://code.djangoproject.com/ticket/14930 values = self.pois.values('ordering', 'type') else: values = self.pois.values('type') pks = [value['type'] for value in values] return POIType.objects.filter(pk__in=set(pks)) @property def length_kilometer(self): return "%.1f" % (self.length / 1000.0) @property def networks_display(self): return ', '.join([unicode(n) for n in self.networks.all()]) @property def districts_display(self): return ', '.join([unicode(d) for d in self.districts]) @property def themes_display(self): return ', '.join([unicode(n) for n in self.themes.all()]) @property def city_departure(self): cities = self.cities return unicode(cities[0]) if len(cities) > 0 else '' def kml(self): """ Exports trek into KML format, add geometry as linestring and POI as place marks """ kml = simplekml.Kml() # Main itinerary geom3d = self.geom_3d.transform(4326, clone=True) # KML uses WGS84 line = kml.newlinestring(name=self.name, description=plain_text(self.description), coords=geom3d.coords) line.style.linestyle.color = simplekml.Color.red # Red line.style.linestyle.width = 4 # pixels # Place marks for poi in self.pois: place = poi.geom_3d.transform(settings.API_SRID, clone=True) kml.newpoint(name=poi.name, description=plain_text(poi.description), coords=[place.coords]) return kml.kml() def has_geom_valid(self): """A trek should be a LineString, even if it's a loop. """ return super(Trek, self).has_geom_valid() and self.geom.geom_type.lower() == 'linestring' @property def duration_pretty(self): return trekking_tags.duration(self.duration) @classproperty def duration_pretty_verbose_name(cls): return _("Formated duration") @classmethod def path_treks(cls, path): treks = cls.objects.existing().filter(aggregations__path=path) # The following part prevents conflict with default trek ordering # ProgrammingError: SELECT DISTINCT ON expressions must match initial ORDER BY expressions return treks.order_by('topo_object').distinct('topo_object') @classmethod def topology_treks(cls, topology): if settings.TREKKING_TOPOLOGY_ENABLED: qs = cls.overlapping(topology) else: area = topology.geom.buffer(settings.TREK_POI_INTERSECTION_MARGIN) qs = cls.objects.existing().filter(geom__intersects=area) return qs @classmethod def published_topology_treks(cls, topology): return cls.topology_treks(topology).filter(published=True) # Rando v1 compat @property def usages(self): return [self.practice] if self.practice else [] @classmethod def get_create_label(cls): return _(u"Add a new trek") @property def parents(self): return Trek.objects.filter(trek_children__child=self, deleted=False) @property def parents_id(self): parents = self.trek_parents.values_list('parent__id', flat=True) return list(parents) @property def children(self): return Trek.objects.filter(trek_parents__parent=self, deleted=False).order_by('trek_parents__order') @property def children_id(self): """ Get children IDs """ children = self.trek_children.order_by('order')\ .values_list('child__id', flat=True) return list(children) def previous_id_for(self, parent): children_id = parent.children_id index = children_id.index(self.id) if index == 0: return None return children_id[index - 1] def next_id_for(self, parent): children_id = parent.children_id index = children_id.index(self.id) if index == len(children_id) - 1: return None return children_id[index + 1] @property def previous_id(self): """ Dict of parent -> previous child """ return {parent.id: self.previous_id_for(parent) for parent in self.parents.filter(published=True, deleted=False)} @property def next_id(self): """ Dict of parent -> next child """ return {parent.id: self.next_id_for(parent) for parent in self.parents.filter(published=True, deleted=False)} def clean(self): """ Custom model validation """ if self.pk in self.trek_children.values_list('child__id', flat=True): raise ValidationError(_(u"Cannot use itself as child trek.")) @property def prefixed_category_id(self): if settings.SPLIT_TREKS_CATEGORIES_BY_PRACTICE and self.practice: return '{prefix}{id}'.format(prefix=self.category_id_prefix, id=self.practice.id) else: return self.category_id_prefix def distance(self, to_cls): if self.practice and self.practice.distance is not None: return self.practice.distance else: return settings.TOURISM_INTERSECTION_MARGIN def is_public(self): for parent in self.parents: if parent.any_published: return True return self.any_published @property def picture_print(self): picture = super(Trek, self).picture_print if picture: return picture for poi in self.published_pois: picture = poi.picture_print if picture: return picture def save(self, *args, **kwargs): if self.pk is not None and kwargs.get('update_fields', None) is None: field_names = set() for field in self._meta.concrete_fields: if not field.primary_key and not hasattr(field, 'through'): field_names.add(field.attname) old_trek = Trek.objects.get(pk=self.pk) if self.geom is not None and old_trek.geom.equals_exact(self.geom, tolerance=0.00001): field_names.remove('geom') if self.geom_3d is not None and old_trek.geom_3d.equals_exact(self.geom_3d, tolerance=0.00001): field_names.remove('geom_3d') return super(Trek, self).save(update_fields=field_names, *args, **kwargs) super(Trek, self).save(*args, **kwargs) @property def portal_display(self): return ', '.join([unicode(portal) for portal in self.portal.all()]) @property def source_display(self): return ','.join([unicode(source) for source in self.source.all()])
class Campaign(Pricable, models.Model): """kampaň""" class Meta: verbose_name = _(u"kampaň") verbose_name_plural = _(u"kampaně") permissions = (("can_see_application_links", "Can see application links"), ) ordering = ('-id', ) name = models.CharField( unique=True, verbose_name=_(u"Jméno kampaně"), max_length=60, null=False, ) slug = models.SlugField( unique=True, verbose_name=u"Doména v URL", blank=False, ) slug_identifier = models.SlugField( unique=True, verbose_name="Identifikátor kampaně", blank=True, null=True, ) previous_campaign = models.ForeignKey( 'Campaign', verbose_name=_(u"Předchozí kampaň"), null=True, blank=True, on_delete=models.SET_NULL, ) email_footer = models.TextField( verbose_name=_(u"Patička uživatelských e-mailů"), default="", max_length=5000, null=True, blank=True, ) mailing_list_id = models.CharField( verbose_name=_(u"ID mailing listu"), max_length=60, default="", blank=True, null=False, ) show_application_links = models.BooleanField( verbose_name=_("Ukázat odkazy na aplikace"), default=False, null=False, ) mailing_list_enabled = models.NullBooleanField( verbose_name=_(u"Povolit mailing list"), default=None, null=True, blank=True, unique= True, # Enabling mailing lists on more campaigns cause problems. This prevents it until it is fixed. ) extra_agreement_text = models.TextField( verbose_name=_("Další text pro uživatelské souhlas"), blank=True, default="", ) days_active = models.PositiveIntegerField( verbose_name=_("Počet minulých dní, které jdou zapisovat"), default=7, blank=False, null=False, ) minimum_rides_base = models.PositiveIntegerField( verbose_name=_(u"Minimální základ počtu jízd"), help_text= _(u"Minimální počet jízd, které je nutné si zapsat, aby bylo možné dosáhnout 100% jízd" ), default=25, blank=False, null=False, ) minimum_percentage = models.PositiveIntegerField( verbose_name=_( u"Minimální procento pro kvalifikaci do pravidelnostní soutěže"), default=66, blank=False, null=False, ) trip_plus_distance = models.PositiveIntegerField( verbose_name=_(u"Maximální navýšení vzdálenosti"), help_text=_( u"Počet kilometrů, o které je možné prodloužit si jednu jízdu"), default=5, blank=True, null=True, ) tracking_number_first = models.PositiveIntegerField( verbose_name=_(u"První číslo řady pro doručování balíčků"), default=0, blank=False, null=False, ) tracking_number_last = models.PositiveIntegerField( verbose_name=_(u"Poslední číslo řady pro doručování balíčků"), default=999999999, blank=False, null=False, ) package_height = models.PositiveIntegerField( verbose_name=_(u"Výška balíku"), default=1, blank=True, null=True, ) package_width = models.PositiveIntegerField( verbose_name=_(u"Šířka balíku"), default=26, blank=True, null=True, ) package_depth = models.PositiveIntegerField( verbose_name=_(u"Hloubka balíku"), default=35, blank=True, null=True, ) package_max_count = models.PositiveIntegerField( verbose_name=_("Maximální počet triček v krabici"), default=50, blank=True, null=True, ) package_weight = models.FloatField( verbose_name=_(u"Váha balíku"), null=True, blank=True, default=0.25, validators=[ MaxValueValidator(1000), MinValueValidator(0), ], ) invoice_sequence_number_first = models.PositiveIntegerField( verbose_name=_(u"První číslo řady pro faktury"), default=1, blank=False, null=False, ) invoice_sequence_number_last = models.PositiveIntegerField( verbose_name=_(u"Poslední číslo řady pro faktury"), default=999999999, blank=False, null=False, ) benefitial_admission_fee = models.FloatField( verbose_name=_(u"Benefiční startovné"), null=False, default=0, ) benefitial_admission_fee_company = models.FloatField( verbose_name=_(u"Benefiční startovné pro organizace"), null=False, default=0, ) free_entry_cases_html = models.TextField( verbose_name=_(u"Případy, kdy je startovné zdarma"), null=True, blank=True, ) club_membership_integration = models.BooleanField( verbose_name=_("Povolit integraci s klubem přátel?"), default=True, ) track_required = models.BooleanField( verbose_name=_("Je povinné zadávat trasu"), default=True, null=False, ) tracks = models.BooleanField( verbose_name=_("Umožnit soutěžícím uložit trasu?"), default=True, ) recreational = models.BooleanField( verbose_name=_("Lze zadávat i výlety"), default=False, ) wp_api_url = models.URLField( default="http://www.dopracenakole.cz", verbose_name=_("Adresa pro Wordpress API se články"), null=True, blank=True, ) wp_api_date_from = models.DateField( verbose_name=_( "Datum, od kterého se zobrazují příspěvky z Wordpress API se články" ), null=True, blank=True, ) web = models.URLField( verbose_name=_("Web kampáně"), default="http://www.dopracenakole.cz", blank=True, ) contact_email = models.CharField( verbose_name=_("Kontaktní e-mail"), default="*****@*****.**", max_length=80, blank=False, ) sitetree_postfix = models.CharField( verbose_name=_("Postfix pro menu"), max_length=60, null=False, blank=True, default="", ) LANGUAGE_PREFIXES = [ ('dpnk', _("Do práce na kole")), ('dsnk', _("Do školy na kole")), ] language_prefixes = models.CharField( verbose_name=_("Jazyková sada"), choices=LANGUAGE_PREFIXES, max_length=16, null=False, blank=False, default='dpnk', ) max_team_members = models.PositiveIntegerField( verbose_name=_("Počet lidí v týmu"), default=5, blank=True, null=True, ) sandwich_type = models.ForeignKey( PdfSandwichType, null=True, blank=True, default='', on_delete=models.SET_NULL, ) team_diploma_sandwich_type = models.ForeignKey( PdfSandwichType, related_name='team_diploma_campaign', null=True, blank=True, default='', on_delete=models.SET_NULL, ) def sitetree_postfix_maintree(self): if self.sitetree_postfix: return "maintree_%s" % self.sitetree_postfix else: return "maintree" def __str__(self): return self.name def competitors_choose_team(self): return self.max_team_members > 1 def too_much_members(self, member_count): if self.max_team_members is None: return False return member_count > self.max_team_members def day_active(self, day): """ Return if this day can be changed by user """ day_today = util.today() try: entry_phase = self.phase('entry_enabled') if not entry_phase.is_actual(): return False except Phase.DoesNotExist: pass return ((day <= day_today) and (day > day_today - datetime.timedelta(days=self.days_active))) def vacation_day_active(self, day): """ Return if this day can be added as vacation """ day_today = util.today() last_day = self.competition_phase().date_to return ((day <= last_day) and (day > day_today)) def possible_vacation_days(self): """ Return days, that can be added as vacation """ competition_phase = self.competition_phase() return [ d for d in util.daterange(competition_phase.date_from, competition_phase.date_to) if self.vacation_day_active(d) ] def user_attendances_for_delivery(self): from t_shirt_delivery.models import PackageTransaction return UserAttendance.objects.filter( campaign=self, payment_status__in=('done', 'no_admission'), t_shirt_size__ship=True, ).exclude( transactions__packagetransaction__status__in=PackageTransaction. shipped_statuses, ).exclude(team=None, ).annotate( payment_created=Max('transactions__payment__created'), ).order_by('payment_created', ).distinct() def get_directions(self): if self.recreational: return ('trip_to', 'trip_from', 'recreational') else: return ('trip_to', 'trip_from') @denormalized(models.BooleanField, default=True) @depend_on_related('t_shirt_delivery.TShirtSize', type='backward', foreign_key='campaign') def has_any_tshirt(self): return self.tshirtsize_set.exists() def phase(self, phase_type): """ Return phase of given type from this campaign. @phase_type Type of phase. """ @cached(60) def get_phase(pk, phase_type): return self.phase_set.get(phase_type=phase_type) return get_phase(self.pk, phase_type) def competition_phase(self): return self.phase('competition')
class Campaign(Pricable, models.Model): """kampaň""" class Meta: verbose_name = _("kampaň") verbose_name_plural = _("kampaně") permissions = (("can_see_application_links", "Can see application links"),) ordering = ("-id",) unique_together = [ ("mailing_list_type", "mailing_list_id"), ("campaign_type", "year"), ] campaign_type = models.ForeignKey( "CampaignType", verbose_name=_("Typ kampaně"), null=True, blank=True, on_delete=models.SET_NULL, related_name="campaigns", ) year = models.CharField( unique=False, verbose_name=_("Ročník kampaně"), max_length=60, null=False, default=datetime.datetime.now().year, ) slug = models.SlugField( unique=True, verbose_name="Doména v URL", blank=False, ) slug_identifier = models.SlugField( unique=True, verbose_name="Identifikátor kampaně", blank=True, null=True, ) previous_campaign = models.ForeignKey( "Campaign", verbose_name=_("Předchozí kampaň"), help_text=_( "Kampaň, ze které se přebírá velikost trička, nazev týmu atd... při vytváření nové účasti v kampani" ), null=True, blank=True, on_delete=models.SET_NULL, ) email_footer = models.TextField( verbose_name=_("Patička uživatelských e-mailů"), default="", max_length=5000, null=True, blank=True, ) mailing_list_id = models.CharField( verbose_name=_("ID mailing listu"), max_length=60, default="", blank=True, null=False, ) mailing_list_type = models.CharField( verbose_name=_("ID mailing listu"), choices=[ (None, _("Disabled")), ("campaign_monitor", _("Campaign monitor")), ("ecomail", _("EcoMail")), ], max_length=60, default=None, blank=True, null=True, ) show_application_links = models.BooleanField( verbose_name=_("Ukázat odkazy na aplikace"), default=False, null=False, ) mailing_list_enabled = models.NullBooleanField( verbose_name=_("Povolit mailing list"), default=None, null=True, blank=True, unique=True, # Enabling mailing lists on more campaigns cause problems. This prevents it until it is fixed. ) extra_agreement_text = models.TextField( verbose_name=_("Další text pro uživatelské souhlas"), blank=True, default="", ) days_active = models.PositiveIntegerField( verbose_name=_("Počet minulých dní, které jdou zapisovat"), default=7, blank=False, null=False, ) minimum_rides_base = models.PositiveIntegerField( verbose_name=_("Minimální základ počtu jízd"), help_text=_( "Minimální počet jízd, které je nutné si zapsat, aby bylo možné dosáhnout 100% jízd" ), default=25, blank=False, null=False, ) minimum_percentage = models.PositiveIntegerField( verbose_name=_("Minimální procento pro kvalifikaci do pravidelnostní soutěže"), default=66, blank=False, null=False, ) trip_plus_distance = models.PositiveIntegerField( verbose_name=_("Maximální navýšení vzdálenosti"), help_text=_("Počet kilometrů, o které je možné prodloužit si jednu jízdu"), default=5, blank=True, null=True, ) tracking_number_first = models.PositiveIntegerField( verbose_name=_("První číslo řady pro doručování balíčků"), default=0, blank=False, null=False, ) tracking_number_last = models.PositiveIntegerField( verbose_name=_("Poslední číslo řady pro doručování balíčků"), default=999999999, blank=False, null=False, ) package_height = models.PositiveIntegerField( verbose_name=_("Výška balíku"), default=1, blank=True, null=True, ) package_width = models.PositiveIntegerField( verbose_name=_("Šířka balíku"), default=26, blank=True, null=True, ) package_depth = models.PositiveIntegerField( verbose_name=_("Hloubka balíku"), default=35, blank=True, null=True, ) package_max_count = models.PositiveIntegerField( verbose_name=_("Maximální počet triček v krabici"), default=50, blank=True, null=True, ) package_weight = models.FloatField( verbose_name=_("Váha balíku"), null=True, blank=True, default=0.25, validators=[ MaxValueValidator(1000), MinValueValidator(0), ], ) invoice_sequence_number_first = models.PositiveIntegerField( verbose_name=_("První číslo řady pro faktury"), default=1, blank=False, null=False, ) invoice_sequence_number_last = models.PositiveIntegerField( verbose_name=_("Poslední číslo řady pro faktury"), default=999999999, blank=False, null=False, ) benefitial_admission_fee = models.FloatField( verbose_name=_("Benefiční startovné"), null=False, default=0, ) benefitial_admission_fee_company = models.FloatField( verbose_name=_("Benefiční startovné pro organizace"), null=False, default=0, ) free_entry_cases_html = models.TextField( verbose_name=_("Případy, kdy je startovné zdarma"), null=True, blank=True, ) club_membership_integration = models.BooleanField( verbose_name=_("Povolit integraci s klubem přátel?"), default=True, ) track_required = models.BooleanField( verbose_name=_("DEPRECATED"), default=False, null=False, ) tracks = models.BooleanField( verbose_name=_("Umožnit soutěžícím uložit trasu?"), default=True, ) recreational = models.BooleanField( verbose_name=_("Lze zadávat i výlety"), default=False, ) wp_api_date_from = models.DateField( verbose_name=_( "Datum, od kterého se zobrazují příspěvky z Wordpress API se články" ), null=True, blank=True, ) max_team_members = models.PositiveIntegerField( verbose_name=_("Počet lidí v týmu"), default=5, blank=True, null=True, ) sandwich_type = models.ForeignKey( PdfSandwichType, null=True, blank=True, default="", on_delete=models.SET_NULL, ) team_diploma_sandwich_type = models.ForeignKey( PdfSandwichType, related_name="team_diploma_campaign", null=True, blank=True, default="", on_delete=models.SET_NULL, ) city_in_campaign_diploma_sandwich_type = models.ForeignKey( PdfSandwichType, related_name="city_in_campaign_diploma_campaign", null=True, blank=True, default="", on_delete=models.SET_NULL, ) def display_name(self): if self.name: return self.name if self.campaign_type is None: return "No campaign type " + str(self.year) return self.campaign_type.name + " " + str(self.year) def __str__(self): return self.display_name() def tagline(self): return self.campaign_type.tagline % str(self.year) def competitors_choose_team(self): return self.max_team_members > 1 def too_much_members(self, member_count): if self.max_team_members is None: return False return member_count > self.max_team_members def active(self): return self.day_active(util.today()) def entry_enabled_end(self): return self.phase("entry_enabled").date_to def day_active(self, day, day_today=None): """Return if this day can be changed by user""" try: entry_phase = self.phase("entry_enabled") if not entry_phase.is_actual(): return False except Phase.DoesNotExist: pass competition_phase = self.phase("competition") return self.day_recent(day, day_today) and competition_phase.day_in_phase(day) def day_recent(self, day, day_today=None): """ Returns true if the current day is today or in the recent past. Recent beting defined by "campaign.days_active". """ if day_today is None: day_today = util.today() return (day <= day_today) and ( day > self._first_possibly_active_day(day_today=day_today) ) def _first_possibly_active_day(self, day_today=None): if day_today is None: day_today = util.today() return day_today - datetime.timedelta(days=self.days_active) def vacation_day_active(self, day): """Return if this day can be added as vacation""" day_today = util.today() last_day = self.competition_phase().date_to return (day <= last_day) and (day > day_today) def possible_vacation_days(self): """Return days, that can be added as vacation""" @cached(60) def get_days(pk): competition_phase = self.competition_phase() return [ d for d in util.daterange( competition_phase.date_from, competition_phase.date_to ) if self.vacation_day_active(d) ] return get_days(self.pk) def user_attendances_for_delivery(self): from t_shirt_delivery.models import PackageTransaction return ( UserAttendance.objects.filter( campaign=self, payment_status__in=("done", "no_admission"), t_shirt_size__ship=True, ) .exclude( transactions__packagetransaction__status__in=PackageTransaction.shipped_statuses, ) .exclude( team=None, ) .annotate( payment_created=Max("transactions__payment__created"), ) .order_by( "payment_created", ) .distinct() ) def get_complementary_school_campaign(self): @cached(60) def get_campaign(pk): try: return Campaign.objects.get(year=self.year, campaign_type__slug="skoly") except Campaign.DoesNotExist: return None return get_campaign(self.pk) def get_complementary_main_campaign(self): try: return Campaign.objects.get(year=self.year, campaign_type__slug="dpnk") except Campaign.DoesNotExist: return None def get_base_url(self, request=None): return util.get_base_url(request, self.slug) def get_directions(self): if self.recreational: return ("trip_to", "trip_from", "recreational") else: return ("trip_to", "trip_from") @denormalized(models.BooleanField, default=True) @depend_on_related( "t_shirt_delivery.TShirtSize", type="backward", foreign_key="campaign" ) def has_any_tshirt(self): return self.tshirtsize_set.exists() def phase(self, phase_type): """ Return phase of given type from this campaign. @phase_type Type of phase. """ @cached(60) def get_phase(pk, phase_type): try: return self.phase_set.get(phase_type=phase_type) except Phase.DoesNotExist: return False result = get_phase(self.pk, phase_type) if not result: get_phase.invalidate(self.pk, phase_type) raise Phase.DoesNotExist return result def competition_phase(self): return self.phase("competition") ############################################# # DEPRECATED ################################ ############################################# name = models.CharField( unique=False, verbose_name=_("Deprecated: Jméno kampaně"), max_length=60, null=True, ) wp_api_url = models.URLField( default="http://www.dopracenakole.cz", verbose_name=_("Deprecated: Adresa pro Wordpress API se články"), null=True, blank=True, ) web = models.URLField( verbose_name=_("Deprecated: Web kampáně"), default="http://www.dopracenakole.cz", blank=True, null=True, ) contact_email = models.CharField( verbose_name=_("Deprecated: Kontaktní e-mail"), default="*****@*****.**", max_length=80, blank=True, null=True, ) sitetree_postfix = models.CharField( verbose_name=_("Deprecated: Postfix pro menu"), max_length=60, null=True, blank=True, default="", ) LANGUAGE_PREFIXES = [ ("dpnk", _("Do práce na kole")), ("dsnk", _("Do školy na kole")), ] language_prefixes = models.CharField( verbose_name=_("Deprecated: Jazyková sada"), choices=LANGUAGE_PREFIXES, max_length=16, null=False, blank=False, default="dpnk", ) main_color = ColorField( default="#1EA04F", verbose_name="Deprecated: Hlavní barva kampaně", )
class NHDLake(models.Model): reachcode = models.CharField(max_length=32, primary_key=True) title = models.CharField(max_length=255, blank=True) permanent_id = models.CharField(max_length=64) fdate = models.DateField() ftype = models.IntegerField() fcode = models.IntegerField() shape_length = models.FloatField() shape_area = models.FloatField() resolution = models.IntegerField() gnis_id = models.CharField(max_length=32) gnis_name = models.CharField(max_length=255) area_sq_km = models.FloatField() elevation = models.FloatField() parent = models.ForeignKey('self', null=True, db_column="parent", blank=True) aol_page = models.IntegerField(null=True, blank=True) body = models.TextField() fishing_zone = models.ForeignKey('FishingZone', null=True) huc6 = models.ForeignKey('HUC6', null=True, blank=True) county_set = models.ManyToManyField('County', through="LakeCounty") plants = models.ManyToManyField('Plant', through="LakePlant") # denormalized fields # unfortunately, for performance reasons, we need to cache whether a lake # has plant data, has mussels, was in the original AOL, has docs, has # photos etc has_mussels = models.BooleanField(default=False, blank=True) has_plants = models.BooleanField(default=False, blank=True) has_docs = models.BooleanField(default=False, blank=True) has_photos = models.BooleanField(default=False, blank=True) # some lakes in the NHD are not in oregon, so we cache this value so we # don't have to join on the lake_county table is_in_oregon = models.BooleanField(default=False, blank=True) objects = NHDLakeManager() unfiltered = models.Manager() class Meta: db_table = 'nhd' def __unicode__(self): return self.title or self.gnis_name or self.pk @classmethod def update_cached_fields(cls): """ Important lakes are lakes with plant data, mussel data, photos docs, etc. We cache these booleans (has_mussels, has_plants, etc) on the model itself for performance reasons, so this class method needs to be called to refresh thos booleans. It also refreshes the is_in_oregon flag """ cursor = connection.cursor() results = cursor.execute(""" SELECT reachcode, MAX(has_plants) AS has_plants, MAX(has_docs) AS has_docs, MAX(has_photos) AS has_photos, MAX(has_aol_page) AS has_aol_page, MAX(has_mussels) AS has_mussels FROM ( -- get all reachcodes for lakes with plants SELECT reachcode, 1 AS has_plants, 0 AS has_docs, 0 AS has_photos, 0 AS has_aol_page, 0 AS has_mussels FROM "lake_plant" GROUP BY reachcode HAVING COUNT(*) >= 1 UNION -- get all reachcodes for lakes with documents SELECT reachcode, 0 AS has_plants, 1 AS has_docs, 0 AS has_photos, 0 AS has_aol_page, 0 AS has_mussels FROM "document" GROUP BY reachcode HAVING COUNT(*) >= 1 UNION -- get all reachcodes for lakes with photos SELECT reachcode, 0 AS has_plants, 0 AS has_docs, 1 AS has_photos, 0 AS has_aol_page, 0 AS has_mussels FROM "photo" GROUP BY reachcode HAVING COUNT(*) >= 1 UNION -- get all original AOL lakes SELECT reachcode, 0 AS has_plants, 0 AS has_docs, 0 AS has_photos, 1 AS has_aol_page, 0 AS has_mussels FROM "nhd" WHERE aol_page IS NOT NULL UNION SELECT DISTINCT reachcode, 0 AS has_plants, 0 AS has_docs, 0 AS has_photos, 0 AS has_aol_page, 1 AS has_mussels FROM mussels.observation INNER JOIN "lake_geom" ON (ST_BUFFER(ST_TRANSFORM(observation.the_geom, 3644), %s) && lake_geom.the_geom) ) k GROUP BY reachcode """, [DISTANCE_FROM_ITEM]) for row in dictfetchall(cursor): NHDLake.unfiltered.filter(reachcode=row['reachcode']).update( has_plants=row['has_plants'], has_docs=row['has_docs'], has_photos=row['has_photos'], has_mussels=row['has_mussels'] ) # now update the is_in_oregon flag cursor.execute(""" WITH foo AS (SELECT COUNT(*) AS the_count, reachcode FROM lake_county GROUP BY reachcode) UPDATE nhd SET is_in_oregon = foo.the_count > 0 FROM foo WHERE foo.reachcode = nhd.reachcode """) @property def area(self): """Returns the number of acres this lake is""" if not hasattr(self, "_area"): cursor = connections['default'].cursor() # 43560 is the number of square feet in an arre cursor.execute("SELECT ST_AREA(the_geom)/43560 FROM lake_geom WHERE reachcode = %s", (self.reachcode,)) self._area = cursor.fetchone()[0] return self._area @property def perimeter(self): """Returns the number of acres this lake is""" if not hasattr(self, "_perimeter"): cursor = connections['default'].cursor() # 5280 is the number of feet in a mile cursor.execute("SELECT ST_PERIMETER(the_geom)/5280 FROM lake_geom WHERE reachcode = %s", (self.reachcode,)) self._perimeter = cursor.fetchone()[0] return self._perimeter @property def bounding_box(self): if not hasattr(self, "_bbox"): lakes = LakeGeom.objects.raw("""SELECT reachcode, Box2D(ST_Envelope(st_expand(the_geom,1000))) as coords from lake_geom WHERE reachcode = %s""", (self.pk,)) lake = list(lakes)[0] self._bbox = re.sub(r'[^0-9.-]', " ", lake.coords).split() return self._bbox @property def counties(self): """Return a nice comma separated list of the counties this lake belongs to""" if not hasattr(self, "_counties"): self._counties = ",".join(c.name for c in self.county_set.all()) return self._counties @counties.setter def counties(self, value): """ We need a setter since the raw query we perform in the manager class generates the comma separated list of counties in the query itself """ self._counties = value @property def watershed_tile_url(self): """ Returns the URL to the watershed tile thumbnail from the arcgis server for this lake """ # get the bounding box of the huc6 geom for the lake. The magic 300 # here is from the original AOL results = HUC6.objects.raw(""" SELECT Box2D(st_envelope(st_expand(the_geom, 300))) AS bbox, huc6.huc6_id FROM huc6 WHERE huc6.huc6_id = %s """, (self.huc6_id,)) try: bbox = list(results)[0].bbox except IndexError: # this lake does not have a watershed return None return self._bbox_thumbnail_url(bbox) @property def basin_tile_url(self): """ Return the URL to the lakebasin tile thumbnail from the arcgis server """ # the magic 1000 here is from the original AOL too results = LakeGeom.objects.raw(""" SELECT Box2D(st_envelope(st_expand(the_geom,1000))) as bbox, reachcode FROM lake_geom where reachcode = %s """, (self.pk,)) bbox = results[0].bbox return self._bbox_thumbnail_url(bbox) def _bbox_thumbnail_url(self, bbox): """ Take a boundingbox string from postgis, for example: BOX(727773.25 1372170,829042.75 1430280.75) and build the URL to a tile of that bounding box in the arcgis server """ # extract out the numbers from the bbox, and comma separate them bbox = re.sub(r'[^0-9.-]', " ", bbox).split() bbox = ",".join(bbox) path = "export?bbox=%s&bboxSR=&layers=&layerdefs=&size=&imageSR=&format=jpg&transparent=false&dpi=&time=&layerTimeOptions=&f=image" return SETTINGS.TILE_URL + (path % bbox) @property def mussels(self): """ This queries the mussel DB for any mussels that are within a certain distance of this lake. It returns a comma separated string of the status of the mussels """ if not hasattr(self, "_mussels"): cursor = connection.cursor() cursor.execute(""" SELECT DISTINCT specie.name as species, date_checked, agency.name as agency FROM mussels.observation INNER JOIN mussels.specie USING(specie_id) INNER JOIN mussels.agency USING(agency_id) WHERE ST_BUFFER(ST_TRANSFORM(the_geom, 3644), %s) && (SELECT the_geom FROM lake_geom WHERE reachcode = %s) ORDER BY date_checked DESC """, (DISTANCE_FROM_ITEM, self.pk)) results = [] for row in cursor: results.append({ "species": row[0], "date_checked": row[1], "source": row[2] }) self._mussels = results return self._mussels
class place(models.Model): city = models.CharField(max_length=50, blank=True, null=True) district = models.CharField(max_length=50, blank=True, null=True) street = models.CharField(max_length=50, blank=True, null=True) house = models.CharField(max_length=20, blank=True, null=True) lat = models.FloatField(blank=True, null=True) lon = models.FloatField(blank=True, null=True) def __unicode__(self): name = '' if self.city: name += self.city + ' ' elif self.district: name += self.district + ' ' elif self.street: name += self.street + ' ' elif self.house: name += self.house + ' ' return unicode(name) def es_repr(self): data = {} mapping = es_mappings[self.__class__.__name__] data['_id'] = self.pk for field_name in mapping['properties'].keys(): data[field_name] = self.field_es_repr(field_name) return data def field_es_repr(self, field_name): mapping = es_mappings[self.__class__.__name__] config = mapping['properties'][field_name] field_es_value = getattr(self, field_name) return field_es_value @classmethod def get_es_index(cls): return model_es_indices[cls.__name__]['index_name'] @classmethod def get_es_type(cls): return model_es_indices[cls.__name__]['type'] @classmethod def es_search(cls, term, srch_fields=['city', 'district', 'street', 'house']): es = settings.ES_CLIENT query = cls.gen_query(term, srch_fields) print json.dumps(query, ensure_ascii=False) recs = [] res = es.search(index=cls.get_es_index(), doc_type=cls.get_es_type(), body=query) if res['hits']['total'] > 0: print 'found %s' % res['hits']['total'] ids = [c['_id'] for c in res['hits']['hits']] clauses = ' '.join( ['WHEN id=%s THEN %s' % (pk, i) for i, pk in enumerate(ids)]) ordering = 'CASE %s END' % clauses recs = cls.objects.filter(id__in=ids).extra( select={'ordering': ordering}, order_by=('ordering', )) print recs[0] return recs @classmethod def gen_query(cls, term, srch_fields): val = term query = { "query": { "filtered": { "query": { "bool": { "should": [{ "multi_match": { "type": "cross_fields", "fields": ["city"], "fuzziness": "AUTO", "query": term, "boost": 25 } }, { "multi_match": { "type": "cross_fields", "fields": ["district"], "fuzziness": "AUTO", "query": term, "boost": 25 } }, { "multi_match": { "type": "cross_fields", "fields": ["street"], "fuzziness": "AUTO", "query": term, "boost": 5 } }, { "multi_match": { "type": "cross_fields", "fields": ["house"], "query": term } }] } } } }, "size": 10, } return json.dumps(query)
class Presentation(models.Model): """GP prescribing products. Import from BNF codes file from BSA. ADQs imported from BSA data. Where codes have changed or otherwise been mapped, the `replaced_by` field has a value. """ bnf_code = models.CharField( max_length=15, primary_key=True, validators=[isAlphaNumeric] ) name = models.CharField(max_length=200) is_generic = models.NullBooleanField(default=None) is_current = models.BooleanField(default=True) replaced_by = models.ForeignKey( "self", null=True, blank=True, on_delete=models.PROTECT ) # An ADQ is the assumed average maintenance dose per day for a # drug used for its main indication in adults. # # If a presentation's ADQ is "20mg", and its `quantity` field is # measured in 10 mg tablets, then the `adq_per_quantity` whould be # 2. In other words, `adq_per_quantity` is a factor to apply to # `quantity`, to obtain an ADQ. # # See https://github.com/ebmdatalab/openprescribing/issues/934 for # more detail adq_per_quantity = models.FloatField(null=True, blank=True) # Usually `quantity` measures something that comes *in* a pack e.g. "number # of tablets" or "number of ml of liquid". (Note this is "pack" in the DM+D # sense were a bottle and pump are both "packs"). Occasionally though, # `quantity` measures the number of packs itself. This field is set by the # `set_quantity_means_pack` management command and should not be modified # by anything else, especially not by hand. quantity_means_pack = models.NullBooleanField(default=None) # The name of the corresponding product (or product pack) in dm+d. This # tends to be more user-friendly than the names in the BNF. See # set_dmd_names in import_dmd command for details of how this is set. dmd_name = models.CharField(max_length=255, null=True) objects = PresentationManager() def __str__(self): return "%s: %s" % (self.bnf_code, self.product_name) def save(self, *args, **kwargs): if len(self.bnf_code) > 10: code = self.bnf_code[9:11] is_generic = code == "AA" else: is_generic = None self.is_generic = is_generic super(Presentation, self).save(*args, **kwargs) @property def current_version(self): """BNF codes are replaced over time. Return the most recent version the code. """ version = self next_version = self.replaced_by seen = [] while next_version: if next_version in seen: break # avoid loops else: seen.append(next_version) version = next_version next_version = version.replaced_by return version def tariff_categories(self): """Return all tariff categories for this presentation.""" vmpps = VMPP.objects.filter(bnf_code=self.bnf_code) return DtPaymentCategory.objects.filter(dtinfo__vmpp__in=vmpps).distinct() def tariff_categories_descr(self): """Return a description of the presentation's tariff category/ies.""" return ", ".join(tc.descr for tc in self.tariff_categories()) def availability_restrictions(self): """Return all availability restrictions for this presentation.""" amps = AMP.objects.filter(bnf_code=self.bnf_code) return AvailabilityRestriction.objects.filter(amp__in=amps).distinct() def availability_restrictions_descr(self): """Return a description of the presentation's availabilty restriction/s. If any AMPs have "None" as their availability restriction, we consider that the presentation itself has no availability restriction. """ descrs = [ar.descr for ar in self.availability_restrictions()] if "None" in descrs: return "None" else: return ", ".join(descrs) def prescribability_statuses(self): """Return all prescribability statuses for this presentation. """ vmps = VMP.objects.filter(bnf_code=self.bnf_code) return VirtualProductPresStatus.objects.filter(vmp__in=vmps).distinct() def prescribability_statuses_descr(self): """Return a description of the presentation's prescribability status/es.""" return ", ".join(ps.descr for ps in self.prescribability_statuses()) def dmd_info(self): """Return dictionary of information about this presentation extracted from the dm+d data.""" info = { "tariff_categories": self.tariff_categories_descr(), "availability_restrictions": self.availability_restrictions_descr(), "prescribability_statuses": self.prescribability_statuses_descr(), } return {k: v for k, v in info.items() if v} @property def product_name(self): return self.dmd_name or self.name @classmethod def names_for_bnf_codes(cls, bnf_codes): """ Given a list of BNF codes return a dictionary mapping those codes to their DM&D names """ name_map = cls.objects.filter(bnf_code__in=bnf_codes).values_list( "bnf_code", Coalesce("dmd_name", "name") ) return dict(name_map)
class Country(models.Model): slug = models.CharField(max_length=150) name = models.CharField(max_length=150) featurecla = models.CharField(max_length=32) soviso = models.CharField(max_length=100) scalerank = models.IntegerField() sovereignt = models.CharField(max_length=200) sov_a3 = models.CharField(max_length=200) level = models.FloatField() type = models.CharField(max_length=200) sortname = models.CharField(max_length=200) adm0_a3 = models.CharField(max_length=200) name_sm = models.CharField(max_length=200) name_lng = models.CharField(max_length=200) terr = models.CharField(max_length=200) parentheti = models.CharField(max_length=200) name_alt = models.CharField(max_length=200) local_lng = models.CharField(max_length=200) local_sm = models.CharField(max_length=200) former = models.CharField(max_length=200) abbrev = models.CharField(max_length=200) fips_10 = models.CharField(max_length=200) iso_a2 = models.CharField(max_length=200) iso_a3 = models.CharField(max_length=200) iso_n3 = models.FloatField() map_color = models.FloatField() people = models.FloatField() gdp_usdm = models.FloatField() itu = models.CharField(max_length=200) ioc = models.CharField(max_length=200) fifa = models.CharField(max_length=200) ds = models.CharField(max_length=200) wmo = models.CharField(max_length=200) gaul = models.FloatField() marc = models.CharField(max_length=200) stanag1059 = models.CharField(max_length=200) gw_id = models.FloatField() dial = models.FloatField() internet = models.CharField(max_length=3) cog = models.CharField(max_length=5) actual = models.CharField(max_length=1) capay = models.CharField(max_length=5) crpay = models.CharField(max_length=5) ani = models.CharField(max_length=4) libenr = models.CharField(max_length=50) ancnom = models.CharField(max_length=20) pays_r_gio = models.CharField(max_length=50) comment = models.CharField(max_length=260) # Multipolygon since Polygon fields in Shapefiles can be Multipolygons mpoly = models.MultiPolygonField() objects = models.GeoManager() # So the model is pluralized correctly in the admin. class Meta: verbose_name_plural = "Countries" # Returns the string representation of the model. def __unicode__(self): return self.name def save(self, *args, **kwargs): from django.template.defaultfilters import slugify self.slug = slugify(self.name) super(self.__class__, self).save(*args, **kwargs) # Call the "real" save() method.
class Activity(models.Model): name = models.CharField(max_length=250, null=False) flat_pace = models.FloatField(verbose_name="flat pace (M)", null=False, default=1) ascent_pace = models.FloatField(verbose_name="ascent pace (M)", null=False, default=1) descent_pace = models.FloatField(verbose_name="descent pace (M)", null=False, default=1) dev1 = models.FloatField(verbose_name="dev 1 (M)", null=False, default=1) dev2 = models.FloatField(verbose_name="dev 2 (M)", null=False, default=1) dev3 = models.FloatField(verbose_name="dev 3 (M)", null=False, default=1) dev4 = models.FloatField(verbose_name="dev 4 (M)", null=False, default=1) distance1 = models.FloatField(verbose_name="distance 1 (KM)", null=False, default=1) distance2 = models.FloatField(verbose_name="distance 2 (KM)", null=False, default=1) distance3 = models.FloatField(verbose_name="distance 3 (KM)", null=False, default=1) distance4 = models.FloatField(verbose_name="distance 4 (KM)", null=False, default=1) class Meta: verbose_name = "Activity" verbose_name_plural = "Activities" def __str__(self): return "Activity {0}: {1}".format(self.pk, self.name)
class Circuit(mixins.ObjectWithPictureMixin, AuditableModel): """ Circuit class, collection of places and topics """ name = models.CharField( verbose_name=strings.CIRCUIT_NAME, max_length=256, ) slug = AutoSlugField( verbose_name=strings.CIRCUIT_SLUG, populate_from='name', unique=False, editable=False, ) remixed_from = models.ForeignKey( 'self', verbose_name=strings.CIRCUIT_REMIXED_FROM, blank=True, null=True, ) category = models.IntegerField( verbose_name=strings.CIRCUIT_CATEGORY, choices=constants.CIRCUIT_CATEGORY_CHOICES, ) description = models.CharField( verbose_name=strings.CIRCUIT_DESCRIPTION, max_length=512, blank=True, null=True, ) author = models.ForeignKey( User, verbose_name=strings.CIRCUIT_AUTHOR, ) rating = models.FloatField( verbose_name=strings.CIRCUIT_RATING, default=0.0, ) picture = ImageField( verbose_name=strings.CIRCUIT_PICTURE, upload_to=uuid_based_picture_name('pictures/circuits'), blank=True, null=True, ) # Relationship with Topic topics = models.ManyToManyField( Topic, verbose_name=strings.CIRCUIT_TOPICS, related_name='circuits', null=True, blank=True, ) visits = generic.GenericRelation( Visit, verbose_name=strings.CIRCUIT_VISITS, ) published = models.BooleanField( verbose_name=strings.CIRCUIT_PUBLISHED, default=True, ) highlighted = models.BooleanField( verbose_name=strings.CIRCUIT_HIGHLIGHTED, default=False, ) source = models.PositiveIntegerField( choices=constants.CIRCUIT_SOURCE_CHOICES, default=constants.CIRCUIT_SOURCE_CHOICES.WORLDRAT_USERS, blank=True, null=True, ) adult_content = models.BooleanField( verbose_name=strings.CIRCUIT_ADULT_CONTENT, default=False, ) #Relation to comments model comments = generic.GenericRelation(Comment, object_id_field='object_pk') objects = CircuitManager() def __unicode__(self): return u'%s' % (self.name, ) def delete(self): """ Extension of parent delete method """ # in case circuit is remix of some other circuit if self.remixed_from: # Send signal to update Redis DB circuit_remix_deleted.send(sender=self, remixed_circuit_id=self.pk) # Send signal to update Redis DB circuit_deleted.send(sender=self) # call parent method super(Circuit, self).delete() def save(self): """ Extension of parent save method """ # Signal to verify updated data circuit_updated.send(sender=self) # if circuit has 0 stops make published = False if self.circuit_stops.count() == 0: self.published = False else: self.published = True # Call parent method super(Circuit, self).save() # Send signal to update Redis DB circuit_created.send(sender=self) # position fixer method self.fix_stops_position() class Meta: verbose_name = strings.CIRCUIT_VERBOSE_NAME verbose_name_plural = strings.CIRCUIT_VERBOSE_NAME_PLURAL def stats(self): """ Returns circuit stats obtained from Redis """ R = RedisStatsQuery() return { 'visit_count': R.circuit_visits(self.pk), 'favorite_count': R.circuit_fav_count(self.pk), 'remix_count': R.circuit_remix_count(self.pk), #'comment_count': self.comments.count() } def fix_stops_position(self): stops = [] for stop in self.circuit_stops.all(): stops.append(stop) # super magical sorting function stops.sort(key=lambda CircuitStop: CircuitStop.place.coordinates.x) # set new position to stop counter = 1 for stop in stops: stop.position = counter stop.save() counter += 1 def get_data(self): pic = self.get_picture(settings.THUMB_SMALL_SIZE) return { 'id': self.id, 'name': self.name, 'category': self.category, 'category_display': self.get_category_display(), 'description': self.description, 'adult_content': self.adult_content, 'picture_url': pic['url'], 'picture_ratio_hw': pic['ratio_hw'], } def to_json(self): """Return a JSON representation for easy storage and reuse in the template. """ return json.dumps(self.get_data()) def get_update_url(self): return reverse('circuit_update_controller_resource', kwargs={'circuit_id': self.id}) @property def verbose_category(self): return unicode(constants.CIRCUIT_CATEGORY_CHOICES[self.category][1]) def get_visitors(self): """ Returns a QuerySet containing all the User objects who visited this circuit """ user_id_list = [] for uv in self.visits.all(): if uv.visitor.id not in user_id_list: user_id_list.append(uv.visitor.id) return User.objects.filter(id__in=user_id_list) def get_places(self): """ Returns a QuerySet containing the related places """ raw_places = self.circuit_stops.values('place') places = [] for elem in raw_places: places.append(elem['place']) return Place.objects.filter(pk__in=places) def get_raters(self): """ Returns a QuerySet containing the Users that rated this circuit """ raw_users = self.circuit_ratings.values('user') users = [] for elem in raw_users: users.append(elem['user']) return User.objects.filter(id_in=users) def get_circuit_rating(self, user): try: circuit_rating = CircuitRating.objects.get(circuit=self, user=user) return circuit_rating except CircuitRating.DoesNotExist: return None def register_vote(self, user, vote): cr = self.get_circuit_rating(user) is_pending = (vote == CircuitRating.VOTE_TYPE_CHOICES.PENDING) if cr: print cr.vote, vote if is_pending: cr.delete() elif cr.vote != vote: cr.vote = vote cr.save() return # Add only if it's an upvote or a downvote if is_pending: return cr = CircuitRating( circuit=self, user=user, vote=vote, ) cr.save() def register_upvote(self, user): vote = CircuitRating.VOTE_TYPE_CHOICES.UPVOTE return self.register_vote(user, vote) def register_downvote(self, user): vote = CircuitRating.VOTE_TYPE_CHOICES.DOWNVOTE return self.register_vote(user, vote) def reset_vote(self, user): vote = CircuitRating.VOTE_TYPE_CHOICES.PENDING return self.register_vote(user, vote) def get_vote(self, user): """Returns 1, -1 or 0 according if the vote of `user` is an upvote, downvote or none. """ cr = self.get_circuit_rating(user) if not cr: return 0 if cr.vote == CircuitRating.VOTE_TYPE_CHOICES.UPVOTE: return 1 if cr.vote == CircuitRating.VOTE_TYPE_CHOICES.DOWNVOTE: return -1 return 0 def arrange_positions(self, uuid_list): missing = [] current = OrderedDict() qs = self.circuit_stops.all() for cs in qs: current[cs.uuid] = cs found = 0 unspecified = 0 updated = 0 new_order = [] position = 0 # Update position of circuit stops included # in the list of UUIDs for uuid in uuid_list: position += 1 if uuid in current: found += 1 cs = current[uuid] if cs.position != position: cs.position = position cs.save() updated += 1 del (current[uuid]) new_order.append(uuid) else: missing.append(uuid) # Remaining circuit stops for uuid in current: position += 1 cs = current[uuid] if cs.position != position: cs.position = position cs.save() updated += 1 unspecified += 1 del (current[uuid]) new_order.append(uuid) results = OrderedDict() results['found'] = found results['unspecified'] = unspecified results['updated'] = updated results['new_order'] = new_order results['missing'] = missing return results def calculate_rating(self): """ sets the member rating to the % of upvotes and downvotes """ all_ct_ratings = self.circuit_ratings.values('vote') if len(all_ct_ratings): all_votes = [] for vote in all_ct_ratings: all_votes.append(vote['vote']) upvotes = 0 for vote in all_votes: if vote == 1: upvotes += 1 self.rating = (upvotes * 100) / len(all_votes) self.save() def get_absolute_url(self): return reverse( 'circuit_detail_with_slug', #'circuit_detail_without_slug', kwargs={ 'circuit_id': self.pk, 'slug': self.slug, }) def get_restful_url(self): return "%s%s" % (settings.API_V1_PREFIX.rstrip('/'), reverse('circuit_resource', kwargs={'circuit_id': self.id})) def get_restful_link_metadata(self): metadata = OrderedDict() metadata['href'] = self.get_restful_url() metadata['rel'] = 'alternate' metadata['title'] = self.name metadata['type'] = 'application/json' return metadata def get_last_position(self): """ Checks for greater position ad returns the integer of it """ return self.circuit_stops.count() def is_authorized(self, user): if self.author == user: return True elif user.is_staff: return True return False # TODO: method not needed, not allowing default circuit now @staticmethod def create_default_circuit(author): circuit = Circuit(name=strings.DEFAULT_CIRCUIT_NAME, category=constants.DEFAULT_CIRCUIT_CATEGORY, author=author) circuit.save() return circuit def get_category_object(self): return CircuitCategory(self.category) @staticmethod def delete_empty_citcuits(): """ deletes all circuits with no stops associated to it, created to wipe nexstop empty circuits """ all_cts = Circuit.objects.all() for ct in all_cts: if len(ct.get_places()) == 0: ct.delete() def remix(self, new_author, new_title, new_category, new_description=None, adult_content=False): """ Returns a Copy of the current circuit and stops with a new id, unsaved. new_author: UserProfile """ new_circuit = Circuit() new_circuit.name = new_title new_circuit.author = new_author new_circuit.category = new_category new_circuit.description = new_description new_circuit.picture = self.picture if adult_content: new_circuit.adult_content = True if self.remixed_from: new_circuit.remixed_from = self.remixed_from else: new_circuit.remixed_from = self new_circuit.save() for stop in self.circuit_stops.all(): new_stop = CircuitStop() new_stop.circuit = new_circuit new_stop.place = stop.place new_stop.description = u'' new_stop.position = stop.position new_stop.picture = stop.picture new_stop.save() # Send signal to update Redis DB circuit_remixed.send(sender=self, remixed_circuit_id=new_circuit.pk) return new_circuit def get_coordinates_box(self): min_lat = None max_lat = None min_lng = None max_lng = None # Get min and max latitude for stop in self.circuit_stops.all(): lat = stop.lat lng = stop.lng # Minimal latitude if min_lat is None: min_lat = lat elif lat < min_lat: min_lat = lat # Maximal latitude if max_lat is None: max_lat = lat elif lat > max_lat: max_lat = lat # Minimal longitude if min_lng is None: min_lng = lng elif lng < min_lng: min_lng = lng # Maximal longitude if max_lng is None: max_lng = lng elif lng > max_lng: max_lng = lng result = OrderedDict() result['lat'] = OrderedDict() result['lat']['min'] = min_lat result['lat']['max'] = max_lat result['lng'] = OrderedDict() result['lng']['min'] = min_lng result['lng']['max'] = max_lng return result def get_followers(self): return [profile.user for profile in self.follower_profiles.all()] @staticmethod # FIXME: @mathiasbc returns duplicated circuits, must fix it def filter_by_gmac(gmac): """ gmac = GMAC instance """ stops = CircuitStop.filter_by_gmac(gmac) cts = Circuit.objects.filter(circuit_stops__in=stops) circuits = [] for ct in cts: if ct not in circuits: circuits.append(ct) return circuits @staticmethod def filter_by_gmacs(gmacs): stops = [] for gmac in gmacs: stops += Circuit.filter_by_gmac(gmac) return stops
class Trail(models.Model): PATH_TYPES = ( (0, "Non determine"), (1, "Aller simple"), (2, "Boucle"), (3, "Aller-retour"), ) id_field = "trail_id" index_type = index_types.TYPE_TRAIL objectid = models.IntegerField(null=True, blank=True) trail_id = models.AutoField(primary_key=True) slug = models.SlugField(max_length=300, default="", blank=True) trail_type = models.IntegerField(default=1) name = models.CharField(max_length=250, null=True, blank=True) description = models.TextField(null=True, blank=True) total_length = models.FloatField(null=True, blank=True) min_elevation = models.IntegerField(default=0, null=True, blank=True) # altitude_minimum max_elevation = models.IntegerField(default=0, null=True, blank=True) # altitude_maximum path_type = models.IntegerField(choices=PATH_TYPES, null=True, blank=True) height_positive = models.IntegerField(null=True, blank=True) height_negative = models.IntegerField(null=True, blank=True) height_difference = models.IntegerField(null=True, blank=True) private = models.BooleanField(default=False) hikster_creation = models.IntegerField(null=True, blank=True) shape = models.GeometryField(srid=4326, null=True, blank=True, dim=3) opening_dates = JSONField(null=True, blank=True) last_modified = models.DateField(auto_now=True) # Related fields location = models.ForeignKey( "location.Location", related_name="trails", null=True, blank=True, on_delete=models.SET_NULL, ) region = models.ForeignKey("location.Location", null=True, blank=True, on_delete=models.SET_NULL) trail_sections = models.ManyToManyField(TrailSection, blank=True) # Managers objects = models.Manager() objects_with_eager_loading = TrailManager() shape_2d = models.GeometryField(srid=4326, null=True, blank=True, dim=2) @property def object_type(self): return self.__class__.__name__ def format_duration(self, duration): return functions.pretty_time_delta(duration.total_seconds()) def __str__(self): value = f"{self.trail_id} Trail name: {self.name}" if self.location: value = f"{value} - Park: {self.location.pk}" return value def save(self, *args, **kwargs): self.slug = original = slugify(self.name) for x in itertools.count(1): if not Trail.objects.filter(slug=self.slug).exists(): break self.slug = "%s-%d" % (original, x) super(Trail, self).save(*args, **kwargs) @property def activity_names(self): return self.activities.values_list("activity__name", flat=True) @property def banner(self): return self.images.banners().first() @property def activity_ids(self): return self.activities.values_list("activity__id", flat=True) @property def markers(self): event_trail_sections = list( EventTrailSection.objects.filter(evnt=self.event).values( "start_position", "end_position", "trailsection").order_by("order")) return event_trail_sections
class Gps_points(models.Model): location = models.PointField() temprature = models.FloatField(default=20) date = models.DateTimeField(default=datetime.now, blank=True)
class Peak(models.Model): location = models.PointField(null=False, blank=False) elevation = models.FloatField(null=False, blank=False, validators=[validators.MinValueValidator(0)]) name = models.CharField(max_length=128, null=False, blank=False)
class AbstractObservation(models.Model): originates_in_vespawatch = models.BooleanField( default=True, help_text= "The observation was first created in VespaWatch, not iNaturalist") taxon = models.ForeignKey(Taxon, on_delete=models.PROTECT, blank=True, null=True) observation_time = models.DateTimeField(verbose_name=_("Observation date"), validators=[no_future]) comments = models.TextField( verbose_name=_("Comments"), blank=True, help_text= _("Comments are public: use them to describe your observation and help verification." )) latitude = models.FloatField( validators=[MinValueValidator(-90), MaxValueValidator(90)], verbose_name=_("Latitude")) longitude = models.FloatField( validators=[MinValueValidator(-180), MaxValueValidator(180)], verbose_name=_("Longitude")) inaturalist_id = models.BigIntegerField(verbose_name=_("iNaturalist ID"), blank=True, null=True) inaturalist_species = models.CharField( verbose_name=_("iNaturalist species"), max_length=100, blank=True, null=True) # TODO: check if this is still in use or useful inat_vv_confirmed = models.BooleanField( blank=True, null=True) # The community ID of iNaturalist says it's Vespa Velutina # Observer info observer_name = models.CharField(verbose_name=_("Name"), max_length=255, blank=True, null=True) observer_email = models.EmailField(verbose_name=_("Email address"), blank=True, null=True) observer_phone = models.CharField(verbose_name=_("Telephone number"), max_length=20, blank=True, null=True) created_at = models.DateTimeField(default=timezone.now) # Managers objects = models.Manager() # The default manager. from_inat_objects = InatCreatedObservationsManager() from_vespawatch_objects = VespawatchCreatedObservationsManager() new_vespawatch_objects = VespawatchNewlyCreatedObservationsManager() class Meta: abstract = True # We got some duplicates and don't exactly know why, this is an attempt to block them without being too # aggresive and introduce bugs (hence the limited number of fields). unique_together = [ 'taxon', 'observation_time', 'latitude', 'longitude', 'comments' ] @property def vernacular_names_in_all_languages(self): """Returns a dict such as: {'en': XXXX, 'nl': YYYY}""" vn = {} for lang in settings.LANGUAGES: code = lang[0] vn[code] = getattr(self.taxon, f'vernacular_name_{code}') return vn @property def display_vernacular_name(self): if self.taxon: return _(self.taxon.vernacular_name) else: return '' @property def display_scientific_name(self): if self.taxon: return self.taxon.name else: return self.inaturalist_species or _('Unknown') @property def can_be_edited_in_admin(self): if self.originates_in_vespawatch: if self.exists_in_inaturalist: return False else: return True else: # Comes from iNaturalist: we can never delete return False @property def can_be_edited_or_deleted(self): """Return True if this observation can be edited in Vespa-Watch (admin, ...)""" return self.originates_in_vespawatch # We can't edit obs that comes from iNaturalist (they're never pushed). @property def taxon_can_be_locally_changed(self): if self.originates_in_vespawatch and self.exists_in_inaturalist: return False # Because we rely on community: info is always pulled and never pushed return True @property def exists_in_inaturalist(self): return self.inaturalist_id is not None @property def inaturalist_obs_url(self): if self.exists_in_inaturalist: return f'https://www.inaturalist.org/observations/{self.inaturalist_id}' return None def has_warnings(self): return len(self.warnings.all()) > 0 has_warnings.boolean = True def _params_for_inat(self): """(Create/update): Common ground for the pushed data to iNaturalist. taxon_id is not part of it because we rely on iNaturalist to correct the identification, if necessary. All the rest is pushed. """ vespawatch_evidence_value = 'nest' if self.__class__ == Nest else 'individual' ofv = [{ 'observation_field_id': settings.VESPAWATCH_ID_OBS_FIELD_ID, 'value': self.pk }, { 'observation_field_id': settings.VESPAWATCH_EVIDENCE_OBS_FIELD_ID, 'value': vespawatch_evidence_value }] if vespawatch_evidence_value == 'individual' and self.behaviour: ofv.append( { 'observation_field_id': settings.VESPAWATCH_BEHAVIOUR_OBS_FIELD_ID, 'value': self.get_behaviour_display() } ) # TODO: get_behaviour_display(): what will happen to push if we translate the values for the UI return { 'observed_on_string': self.observation_time.isoformat(), 'time_zone': 'Brussels', 'description': self.comments, 'latitude': self.latitude, 'longitude': self.longitude, 'observation_field_values_attributes': [{ 'observation_field_id': settings.VESPAWATCH_ID_OBS_FIELD_ID, 'value': self.pk }, { 'observation_field_id': settings.VESPAWATCH_EVIDENCE_OBS_FIELD_ID, 'value': vespawatch_evidence_value }] } def flag_warning(self, text): if text in [x.text for x in self.warnings.all()]: return # warning already set if self.__class__.__name__ == 'Nest': warning = NestObservationWarning(text=text, datetime=now(), observation=self) warning.save() elif self.__class__.__name__ == 'Individual': warning = IndividualObservationWarning(text=text, datetime=now(), observation=self) warning.save() def flag_based_on_inat_data(self, inat_observation_data): """ The observation was no longer found on iNaturalist with our general filters. Check why, and flag this observation """ # Project is vespawatch? if not settings.VESPAWATCH_PROJECT_ID in inat_observation_data[ 'project_ids']: self.flag_warning('not in vespawatch project') # Taxon known in VW? returned_taxon_id = '' if 'community_taxon_id' in inat_observation_data and inat_observation_data[ 'community_taxon_id']: returned_taxon_id = inat_observation_data['community_taxon_id'] elif 'taxon' in inat_observation_data: if 'id' in inat_observation_data['taxon']: returned_taxon_id = inat_observation_data['taxon']['id'] if returned_taxon_id not in [ y for x in Taxon.objects.all() for y in x.inaturalist_pull_taxon_ids ]: self.flag_warning('unknown taxon') def update_from_inat_data(self, inat_observation_data): # Check the vespawatch_evidence # ------ # If the observation is a nest but the vespawatch evidence is not nest => flag the nest if 'ofvs' in inat_observation_data: vw_evidence_list = [ x['value'] for x in inat_observation_data['ofvs'] if x['field_id'] == settings.VESPAWATCH_EVIDENCE_OBS_FIELD_ID ] if len(vw_evidence_list) > 0: vw_evidence = vw_evidence_list[0] if self.__class__.__name__ == 'Nest': if vw_evidence != 'nest': self.flag_warning('individual at inaturalist') # If the observation is an individual but the vespawatch evidence is a nest and the observation originates in vespawatch => delete the individual and create a nest elif self.__class__.__name__ == 'Individual': if vw_evidence == 'nest': if self.originates_in_vespawatch: self.flag_warning('nest at inaturalist') else: create_observation_from_inat_data( inat_observation_data) self.delete() return # Update taxon data and set inat_vv_confirmed (use inat_data_confirms_vv() ) self.inat_vv_confirmed = inat_data_confirms_vv(inat_observation_data) # Update photos # ------------- # When we pull again and the API returns additional images, those are not added. This is done # because we insert a UUID in the filename when we pull it. The result of that is that we cannot # compare that image with the image url that we retrieve from iNaturalist. So to prevent adding # the same image again and again with subsequent pulls, we only add images when the observation # has none. if len(self.pictures.all()) == 0: for photo in inat_observation_data['photos']: self.assign_picture_from_url(photo['url']) # Update location self.latitude = inat_observation_data['geojson']['coordinates'][1] self.longitude = inat_observation_data['geojson']['coordinates'][0] # Update time # ------------- observation_time = dateparser.parse( inat_observation_data['observed_on_string'], settings={'TIMEZONE': inat_observation_data['observed_time_zone']}) if observation_time is None: # Sometimes, dateparser doesn't understand the string but we have the bits and pieces in # inaturalist_data['observed_on_details'] details = inat_observation_data['observed_on_details'] observation_time = datetime( year=details['year'], month=details['month'], day=details['day'], hour=details['hour'] ) # in the observed cases, we had nothing more precise than the hour # Sometimes, the time is naive (even when specifying it to dateparser), because (for the detected cases, at least) # The time is 00:00:00. In that case we make it aware to avoid Django warnings (in the local time zone since all # observations occur in Belgium if is_naive(observation_time): # Some dates (apparently) observation_time = make_aware(observation_time) self.observation_time = observation_time self.comments = inat_observation_data['description'] or '' # Update taxon # ------------- try: self.inaturalist_species = '' taxon = get_taxon_from_inat_taxon_id( inat_observation_data['taxon']['id']) self.taxon = taxon except Taxon.DoesNotExist: self.taxon = None self.inaturalist_species = inat_observation_data['taxon'][ 'name'] if 'name' in inat_observation_data['taxon'] else '' self.save() def create_at_inaturalist(self, access_token, user_agent): """Creates a new observation at iNaturalist for this observation It will update the current object so self.inaturalist_id is properly set. On the other side, it will also set the vespawatch_id observation field so the observation can be found from the iNaturalist record. :param access_token: as returned by pyinaturalist.rest_api.get_access_token( """ params_only_for_create = { 'taxon_id': self.taxon.inaturalist_push_taxon_id } # TODO: with the new sync, does it still makes sense to separate the create/update parameters? params = { 'observation': { **params_only_for_create, **self._params_for_inat() } } r = create_observations(params=params, access_token=access_token, user_agent=user_agent) self.inaturalist_id = r[0]['id'] self.save() self.push_attached_pictures_at_inaturalist(access_token=access_token, user_agent=user_agent) def get_photo_filename(self, photo_url): # TODO: Find a cleaner solution to this # It seems the iNaturalist only returns small thumbnails such as # 'https://static.inaturalist.org/photos/1960816/square.jpg?1444437211' # We can circumvent the issue by hacking the URL... photo_url = photo_url.replace('square.jpg', 'large.jpg') photo_url = photo_url.replace('square.jpeg', 'large.jpeg') photo_filename = photo_url[photo_url.rfind("/") + 1:].split('?', 1)[0] return photo_filename def assign_picture_from_url(self, photo_url): photo_filename = self.get_photo_filename(photo_url) if photo_filename not in [x.image.name for x in self.pictures.all()]: if self.__class__ == Nest: photo_obj = NestPicture() else: photo_obj = IndividualPicture() photo_content = ContentFile(requests.get(photo_url).content) photo_obj.observation = self photo_obj.image.save(photo_filename, photo_content) photo_obj.save() def push_attached_pictures_at_inaturalist(self, access_token, user_agent): if self.inaturalist_id: for picture in self.pictures.all(): add_photo_to_observation(observation_id=self.inaturalist_id, file_object=picture.image.read(), access_token=access_token, user_agent=user_agent) def get_taxon_name(self): if self.taxon: return self.taxon.name else: return '' @property def formatted_observation_date(self): # We need to be aware of the timezone, hence the defaultfilter trick return defaultfilters.date(self.observation_time, 'Y-m-d') @property def observation_time_iso(self): return self.observation_time.isoformat() def save(self, *args, **kwargs): # Let's make sure model.clean() is called on each save(), for validation self.full_clean() return super(AbstractObservation, self).save(*args, **kwargs) def delete(self, *args, **kwargs): if self.originates_in_vespawatch and self.exists_in_inaturalist: InatObsToDelete.objects.create(inaturalist_id=self.inaturalist_id) return super(AbstractObservation, self).delete(*args, **kwargs)
class Aerodrome_Utility_Sewage_Line(models.Model): Aerodrome_Entity = models.ForeignKey(Aerodrome_Entity,null=True,on_delete=models.SET_NULL) Line_Size= models.FloatField() Remark = models.TextField() Line_geom = models.LineStringField()
class Sink(models.Model): TYPE_CHOICES = ( ("CATCHMENT", "Catchment Basin"), ("SINKHOLE", "Sinkhole/Karst Feature"), ("QUARRY", "Quarry"), ("DC", "Ditch/Culvert"), ("FOUNDATION", "Building Foundation"), ("OTHER", "Other"), ("UNKNOWN", "Unknown"), ) CON_CHOICES = ( ("PROBABLE", "Probable"), ("POSSIBLE", "Possible"), ) DEPTH_CAT_CHOICES = ( ("0-1", "0-1 ft"), ("1-2", "1-2 ft"), ("2-5", "2-5 ft"), ("5+", "5+ ft"), ) sink_id = models.IntegerField(null=True, blank=True) sink_type = models.CharField(max_length=20, choices=TYPE_CHOICES, blank=True) dem_check = models.IntegerField(null=True, blank=True) img_check = models.IntegerField(null=True, blank=True) evidence = models.CharField(max_length=1, blank=True, null=True) depth = models.FloatField(null=True, blank=True) depth_cat = models.CharField(max_length=20, choices=DEPTH_CAT_CHOICES, null=True, blank=True) elevation = models.FloatField(null=True, blank=True) in_nfhl = models.NullBooleanField() in_row = models.NullBooleanField() bm_hs = models.BooleanField(default=False) bm_aerial = models.BooleanField(default=False) bm_tpi = models.BooleanField(default=False) bm_usgs = models.BooleanField(default=False) field_chk = models.BooleanField(default=False) field_eval = models.CharField(max_length=20, choices=CON_CHOICES, blank=True, null=True) confidence = models.CharField(max_length=20, choices=CON_CHOICES, blank=True, null=True) comment = models.TextField(max_length=254, blank=True, null=True) last_update = models.DateTimeField(auto_now=True) event_no = models.IntegerField(null=True, blank=True) geom = models.PointField(null=True) def save(self, *args, **kwargs): if self.depth is not None: if self.depth < 1: self.depth_cat = "0-1" elif self.depth < 2: self.depth_cat = "1-2" elif self.depth < 5: self.depth_cat = "2-5" else: self.depth_cat = "5+" super(Sink, self).save(*args, **kwargs) return
class Energyusage(models.Model): energy_type = models.CharField( choices=( ("national_en", "National system"), ("renewable_en", "Renewable"), ("both_en", "Both"), ("national_ro", "Sistem Național"), ("renewable_ro", "Regenerabilă"), ("both_ro", "Amândouă"), ), max_length=12, null=True, blank=True, verbose_name="What type of energy do you use?", ) more_renewable = models.CharField( choices=( ("yes_en", "Yes"), ("no_en", "No"), ("yes_ro", "Da"), ("no_ro", "Nu"), ), max_length=6, null=True, blank=True, verbose_name="Would you like to use more renewable energy?", ) renewable_type = models.CharField( choices=( ("solar_en", "Solar"), ("wind_en", "Wind"), ("water_en", "Water"), ("geothermal_en", "Geothermal"), ("bio_en", "Bio"), ("other_en", "Other"), ("solar_ro", "Solar"), ("wind_ro", "Vânt"), ("water_ro", "Apă"), ("geothermal_ro", "Geotermală"), ("bio_ro", "Bio"), ("other_ro", "Alte"), ), max_length=13, null=True, blank=True, verbose_name="What type of renewable energy do you use?", ) toggle = models.CharField( choices=( ("gps", "Folosește GPS / Use GPS"), ("interactive", "Punct pe hartă / Point on Map"), ("manual", "Introducere manuală / Enter Manually"), ), max_length=11, null=True, blank=True, verbose_name="Location Mode", ) geometry = models.PointField( srid=4326, null=True, blank=True, verbose_name="Location", ) latitude = models.FloatField( null=True, blank=True, verbose_name="Latitude", ) longitude = models.FloatField( null=True, blank=True, verbose_name="Longitude", ) accuracy = models.FloatField( null=True, blank=True, verbose_name="GPS Accuracy", ) class Meta: verbose_name = "energyusage" verbose_name_plural = "energyusages"
class Well(models.Model): wi_unique_well_no = models.CharField(primary_key=True, max_length=6, verbose_name="WI Unique Well ID") watr_seq_no = models.IntegerField(null=True, blank=True, verbose_name="Water Seq No.") dnr_lat_dd_amt = models.FloatField(null=True, blank=True, verbose_name="DNR Lat.") dnr_long_dd_amt = models.FloatField(null=True, blank=True, verbose_name="DNR Long.") survey_township = models.IntegerField(null=True, blank=True, verbose_name="Survey Township") survey_range = models.IntegerField(null=True, blank=True, verbose_name="Survey Range") survey_range_dir = models.CharField(max_length=1, null=True, blank=True, verbose_name="Survey Range Dir.") survey_section = models.IntegerField(null=True, blank=True, verbose_name="Survey Section") q_section = models.CharField(max_length=2, null=True, blank=True, verbose_name="Q Section") qq_section = models.CharField(max_length=2, null=True, blank=True, verbose_name="QQ Section") well_addr = models.CharField(max_length=100, null=True, blank=True, verbose_name="Well Address") owner_mailing_addr = models.CharField(max_length=60, blank=True, null=True, verbose_name="Owner Mailing Address") esri_oid = models.IntegerField(null=True, blank=True, verbose_name="ESRI OID") muni = models.CharField(max_length=45, blank=True, null=True, verbose_name="Municipality") well_depth_amt = models.FloatField(null=True, blank=True, verbose_name="Well Depth") well_depth_amt_text = models.CharField(max_length=45, null=True, blank=True, verbose_name="Well Depth - text") constructor_name = models.CharField(max_length=60, blank=True, null=True, verbose_name="Constructor Name") well_complete_date = models.DateField(blank=True, null=True, verbose_name="Well Completion Date") well_status = models.CharField(max_length=15, blank=True, null=True, verbose_name="Well Status") static_depth_amt = models.FloatField(null=True, blank=True, verbose_name="Static Depth") static_depth_above_below = models.CharField( max_length=25, blank=True, null=True, verbose_name="Static Depth Above Below") location_method = models.CharField(max_length=25, blank=True, null=True, verbose_name="Location Method") casing_depth_amt = models.FloatField(null=True, blank=True, verbose_name="Casing Depth") casing_depth_amt_txt = models.CharField(max_length=45, blank=True, null=True, verbose_name="Casing Depth - text") decade_complete = models.CharField(max_length=15, blank=True, null=True, verbose_name="Decade Completed") well_constr_url = models.CharField(max_length=115, blank=True, null=True, verbose_name="WCR URL") sample_db_url = models.CharField(max_length=650, blank=True, null=True, verbose_name="Sample DB URL") geom = models.PointField(null=True, verbose_name="Location") geo_corrected = models.BooleanField(max_length=650, blank=True, null=True, verbose_name="Geometry corrected?") geo_corrected_by = models.CharField(max_length=100, blank=True, null=True, verbose_name="Geometry corrected by") geo_corrected_time = models.DateTimeField( blank=True, null=True, verbose_name="Geometry corrected on") def __str__(self): return self.pk def save(self, *args, **kwargs): url = "https://prodoasext.dnr.wi.gov/inter1/pk_wr583_sample_query.p_sample_list?"\ "i_county=HIDDEN&i_township=HIDDEN&i_range=HIDDEN&i_range_dir=&i_section=HIDDEN"\ f"&i_wuwn=HIDDEN&i_wuwn={self.wi_unique_well_no}&i_wuwn=&i_wuwn=&i_wuwn=&i_wuwn="\ "&i_well_use=HIDDEN&i_well_use=&i_watershed=HIDDEN&i_watershed=&i_labslip=HIDDEN"\ "&i_labslip=&i_labslip=&i_labslip=&i_labslip=&i_labslip=&i_sample_from="\ "&i_sample_to=&i_dg_samples=HIDDEN&i_dg_samples=YES&i_pw_samples=HIDDEN&"\ "i_pw_samples=YES&i_sw_samples=HIDDEN&i_storet_group=&i_storet=HIDDEN&i_storet="\ "&i_storet=&i_storet=&i_storet=&i_storet=&i_cas=HIDDEN&i_cas=&i_cas=&i_cas=&i_cas=&i_cas="\ "&i_res_criteria=ALL&i_storet_index=1" self.sample_db_url = url super(Well, self).save(*args, **kwargs)
class Anomaly(models.Model): col0 = models.BigIntegerField(null=True) mmsi = models.FloatField(null=True) datetime = models.FloatField(null=True) lat = models.FloatField(null=True) lon = models.FloatField(null=True) sog = models.FloatField(null=True) cog = models.FloatField(null=True) heading = models.BigIntegerField(null=True) vesselname = models.CharField(max_length=254, null=True) imo = models.CharField(max_length=254, null=True) callsign = models.CharField(max_length=254, null=True) vesseltype = models.FloatField(null=True) status = models.FloatField(null=True) length = models.FloatField(null=True) width = models.FloatField(null=True) draft = models.FloatField(null=True) cargo = models.FloatField(null=True) transcieve = models.CharField(max_length=254, null=True) geom = models.MultiPointField(srid=4326, null=True)
class CountyByMonth(models.Model): """ The unemployment data in county in a particular month. """ # The time and place county = models.ForeignKey(County) year = models.IntegerField() month = models.IntegerField() # The goodies labor_force = models.IntegerField() employment = models.IntegerField() unemployment = models.IntegerField() unemployment_rate = models.FloatField() # The boring stuff is_seasonally_adjusted = models.BooleanField('seasonally adjusted') is_preliminary = models.BooleanField('preliminary') benchmark = models.CharField(max_length=150) # Managers objects = models.GeoManager() adjusted = SeasonallyAdjustedManager() unadjusted = UnadjustedManager() class Meta: ordering = ( 'county', 'year', 'month', ) verbose_name = 'County by Month' verbose_name_plural = 'Counties by Month' def __unicode__(self): return u'%s %s-%s' % (self.county, self.year, self.month) def get_month_display(self): """ Return the month in AP style. """ from django.utils.dateformat import format return format(datetime.date(2010, self.month, 01), 'N') def get_month_obj(self): """ Return the month as a datetime object. """ return datetime.datetime(self.year, self.month, 1) def get_next_month(self): """ Return the month after this one. """ from dateutil.relativedelta import relativedelta return self.get_month_obj() + relativedelta(months=+1) def get_previous_month(self): """ Return the month before this one. """ from dateutil.relativedelta import relativedelta return self.get_month_obj() + relativedelta(months=-1)
class Locality(UpdateMixin, ChangesetMixin): """ A Locality is uniquely defined by an *uuid* attribute. Attribute *geom* stores geometry as a point object. *upstream_id* is used to preserve link to the originating dataset which is used to find and update a Locality on any reoccurring data imports. A Locality is in a *Domain* and data values for Attributes, to be exact, their Specifications, are defined through *Value* """ DEFINED_DAYS = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] domain = models.ForeignKey('Domain') uuid = models.TextField(unique=True) upstream_id = models.TextField(null=True, unique=True) geom = models.PointField(srid=4326) specifications = models.ManyToManyField('Specification', through='Value') name = models.TextField() source = models.TextField(default='healthsites.io') migrated = models.BooleanField(default=False) # completeness is a big calculation # so it has to be an field completeness = models.FloatField(null=True, default=0.0) is_master = models.BooleanField(default=True) objects = PassThroughGeoManager.for_queryset_class(LocalitiesQuerySet)() tracker = FieldTracker() def before_save(self, *args, **kwargs): # make sure that we don't allow uuid modifications if self.tracker.previous('uuid') and self.tracker.has_changed('uuid'): self.uuid = self.tracker.previous('uuid') def _get_attr_map(self): return (self.domain.specification_set.order_by('id').values( 'id', 'attribute__key')) def set_geom(self, lon, lat): """ Helper method to set Locality geometry """ self.geom.set_x(lon) self.geom.set_y(lat) def set_values(self, changed_data, social_user, changeset=None): """ Set values for a Locality which are defined by Specifications Once all of values are set, 'SIG_locality_values_updated' signal will be triggered to update FullTextSearch index for this Locality """ special_key = [ 'scope_of_service', 'ancillary_services', 'activities', 'inpatient_service', 'staff' ] attrs = self._get_attr_map() tmp_changeset = changeset changed_values = [] for key, data in changed_data.iteritems(): if key in special_key: data = data.replace(',', '|') data = data.replace('| ', '|') # try to match key from changed items with a key from attr_map attr_list = [ attr for attr in attrs if attr['attribute__key'] == key ] if attr_list: # get specification id for specific key spec_id = attr_list[0]['id'] # update or create new values try: obj = self.value_set.get(specification_id=spec_id) _created = False except Value.DoesNotExist: # in case there is no value for the specification, create obj = Value() obj.locality = self obj.specification_id = spec_id _created = True # set data obj.data = data # check if Value.data actually changed, and save if it did if obj.tracker.changed(): if not (tmp_changeset): tmp_changeset = Changeset.objects.create( social_user=social_user) obj.changeset = tmp_changeset obj.save() changed_values.append((obj, _created)) else: # nothing changed, don't save the value pass else: # attr_id was not found (maybe a bad attribute) LOG.warning('Locality %s has no attribute key %s', self.pk, key) # send values_updated signal signals.SIG_locality_values_updated.send(sender=self.__class__, instance=self) # calculate completeness if changed_values: self.completeness = self.calculate_completeness() self.save() return changed_values def repr_dict(self, clean=False, in_array=False): """ Basic locality representation, as a dictionary """ dict = { u'uuid': self.uuid, u'upstream': self.upstream_id, u'source': self.source, u'name': self.name, u'geom': (self.geom.x, self.geom.y), u'version': self.version, u'date_modified': self.changeset.created, u'completeness': '%s%%' % format(self.completeness, '.2f'), } dict['values'] = {} data_query = (self.value_set.select_related().exclude( data__isnull=True).exclude(data__exact='')) for val in data_query: if in_array: dict['values'][val.specification.attribute.key] = [ data for data in val.data.split('|') if data ] clean_data = dict['values'][val.specification.attribute.key] if len(clean_data) == 0: dict['values'][val.specification.attribute.key] = '-' elif len(clean_data) == 1: dict['values'][ val.specification.attribute.key] = clean_data[0] continue if clean: # clean if empty temp = val.data.replace('|', '') if len(temp) == 0: val.data = '' # clean data val.data = val.data.replace('|', ',') val.specification.attribute.key = ( val.specification.attribute.key.replace('_', '-')) cleaned_data = val.data.replace(',', '') if len(cleaned_data) > 0: dict['values'][val.specification.attribute.key] = val.data else: dict['values'][val.specification.attribute.key] = val.data try: site = Site.objects.get(name=dict[u'source']) dict[u'source_url'] = site.domain except Site.DoesNotExist: pass # exclusive for open street map if self.upstream_id is not None and 'openstreetmap' in self.upstream_id.lower( ): osm_whole_id = self.upstream_id.split(u'¶') if len(osm_whole_id) > 0: osm_whole_id = osm_whole_id[1] identifier = osm_whole_id[0] osm_id = osm_whole_id[1:] if identifier == 'n': dict['osm_type'] = 'node' url = 'http://www.openstreetmap.org/node/' + osm_id elif identifier == 'r': dict['osm_type'] = 'relation' url = 'http://www.openstreetmap.org/relation/' + osm_id elif identifier == 'w': dict['osm_type'] = 'way' url = 'http://www.openstreetmap.org/way/' + osm_id if url: dict['osm_id'] = osm_id dict['source_url'] = url return dict def is_type(self, value): if value != '': try: self.value_set.filter( specification__attribute__key='type').get(data=value) return True except Exception: return False return True def calculate_completeness(self): DEFAULT_VALUE = 4 # GUID & GEOM & NAME & DATA SOURCE global_attr = attributes_availables['global'] specific_attr = attributes_availables['hospital'] for key in attributes_availables.keys(): try: self.value_set.filter( specification__attribute__key='type').get( data__icontains=key) specific_attr = attributes_availables[key] except Value.DoesNotExist: continue values = self.repr_dict()['values'] counted_value = DEFAULT_VALUE max_value = len(global_attr) + len(specific_attr) + DEFAULT_VALUE for attr in global_attr + specific_attr: if attr in values: data = values[attr] if len(data.replace('-', '').replace('|', '').strip()) != 0: counted_value += 1 return (counted_value + 0.0) / (max_value + 0.0) * 100 def prepare_for_fts(self): """ Retrieve and group *Value* objects, for this Locality, based on their FTS ordering (defined by *Specification*) """ data_values = itertools.groupby( self.value_set.order_by('specification__fts_rank').values_list( 'specification__fts_rank', 'data'), lambda x: x[0]) return {k: ' '.join([x[1] for x in v]) for k, v in data_values} def update_what3words(self, user, changeset): from utils import get_what_3_words what3words = get_what_3_words(self.geom) if what3words != '': self.set_values({'what3words': what3words}, user, changeset) def get_synonyms(self): synonyms = SynonymLocalities.objects.get(locality=self) return synonyms def __unicode__(self): return u'{}'.format(self.id) def validate_data_by_defined_list(self, data, key, options, required=False): """ Check value data by key if it is string and it is in options. :param data: Data to be inserted :param key: Key data that is checked :param options: Options to be checked :return: """ try: value = data[key] if isinstance(value, list): raise ValueError('nature_of_facility should be string') if value not in options: raise ValueError('%s is not recognized, options : %s' % (key, options)) except KeyError as e: if required: raise ValueError('%s is required' % e) pass def validate_data(self, data): """ Validate data based on fields :param data: Data that will be inserted :type data: dict """ try: data['lng'] = float(data['lng']) except ValueError: raise ValueError('lng is not in float') try: data['lat'] = float(data['lat']) except ValueError: raise ValueError('lat is not in float') if not data['name']: raise ValueError('name is empty') domain = Domain.objects.get(name='Health') attributes = Specification.objects.filter(domain=domain).filter( required=True) for attribute in attributes: if not data[attribute.attribute.key]: raise ValueError('%s is empty' % attribute.attribute.key) # inpatient_service try: inpatient_service = data['inpatient_service'] try: full_time_beds = inpatient_service['full_time_beds'] except KeyError: raise ValueError( 'full_time_beds needs to be in inpatient_service') try: part_time_beds = inpatient_service['part_time_beds'] except KeyError: raise ValueError( 'part_time_beds needs to be in inpatient_service') data['inpatient_service'] = '%s|%s' % (full_time_beds, part_time_beds) except KeyError: pass # staff try: staff = data['staff'] try: doctors = staff['doctors'] except KeyError: raise ValueError('doctors needs to be in staff') try: nurses = staff['nurses'] except KeyError: raise ValueError('nurses needs to be in staff') data['staff'] = '%s|%s' % (doctors, nurses) except KeyError: pass # nature of facility options = [ 'clinic without beds', 'clinic with beds', 'first referral hospital', 'second referral hospital or General hospital', 'tertiary level including University hospital' ] self.validate_data_by_defined_list(data, 'nature_of_facility', options) # nature of facility options = ['public', 'private not for profit', 'private commercial'] self.validate_data_by_defined_list(data, 'ownership', options) # defined_hours try: defined_hours = [] for index, day in enumerate(Locality.DEFINED_DAYS): try: hours = data['defining_hours'][day] if isinstance(hours, str) or isinstance(hours, unicode): if hours == '': hours = [] else: try: hours = json.loads(hours) except ValueError: pass if not isinstance(hours, list): raise ValueError('%s is need to be in list' % day) if len(hours) == 1: hours.append('-') elif len(hours) > 2: raise ValueError('maximum lenght of %s is 2' % day) hours = '-'.join(hours) if not hours: hours = '-' defined_hours.append(hours) except KeyError as e: raise ValueError('%s is required on defined_hours' % e) data['defining_hours'] = defined_hours except KeyError: pass except TypeError: raise TypeError('defining_hours needs to be in dictionary') return True def update_data(self, data, user): """ Update locality data with new data. :param data: Data that will be inserted :type data: dict """ import uuid from django.contrib.gis.geos import Point from localities.tasks import regenerate_cache, regenerate_cache_cluster self.validate_data(data) old_geom = None try: old_geom = [self.geom.x, self.geom.y] self.set_geom(data['lng'], data['lat']) except AttributeError: self.geom = Point(data['lng'], data['lat']) self.name = data['name'] # there are some changes so create a new changeset changeset = Changeset.objects.create(social_user=user) self.changeset = changeset del data['lng'] del data['lat'] created = False if not self.pk: created = True self.domain = Domain.objects.get(name='Health') self.changeset = changeset self.uuid = uuid.uuid4().hex self.upstream_id = u'web¶{}'.format(self.uuid) self.save() self.set_specifications(data, changeset) if not created and self.tracker.changed(): self.changeset = changeset self.save() # generate some attributes if location changed/created new_geom = [self.geom.x, self.geom.y] if new_geom != old_geom or created: try: self.update_what3words(user, changeset) except AttributeError: pass regenerate_cache_cluster.delay() regenerate_cache.delay(changeset.pk, self.pk) return True def set_specifications(self, data, changeset, autocreate_specification=True): """ Set values for a Locality which are defined by Specifications Once all of values are set, 'SIG_locality_values_updated' signal will be triggered to update FullTextSearch index for this Locality :param data: Data to be inserted as specification :type data: dict """ fields = self._meta.get_all_field_names() domain = Domain.objects.get(name='Health') changed_values = [] for key, value in data.iteritems(): if key in fields: continue if isinstance(value, list): value = '|'.join(value) else: value = '%s' % value value = value.replace(',', '|') value = value.replace('| ', '|') try: specification = Specification.objects.get(domain=domain, attribute__key=key) except Specification.DoesNotExist: if autocreate_specification: try: attribute = Attribute.objects.get(key=key) except Attribute.DoesNotExist: attribute = Attribute.objects.create( key=key, changeset=changeset) specification = Specification.objects.create( domain=domain, attribute=attribute, changeset=changeset) else: continue try: obj = self.value_set.get(specification=specification) except Value.DoesNotExist: # in case there is no value for the specification, create obj = Value() obj.locality = self obj.specification = specification obj.data = value # check if Value.data actually changed, and save if it did if obj.tracker.changed(): obj.changeset = changeset obj.save() changed_values.append(obj) else: # nothing changed, don't save the value pass # send values_updated signal signals.SIG_locality_values_updated.send(sender=self.__class__, instance=self) # calculate completeness if changed_values: self.completeness = self.calculate_completeness() self.save() return changed_values
class Transformer_C2(models.Model): fid_1 = models.IntegerField(null=True, blank=True) objectid = models.BigIntegerField(null=True, blank=True) tag = models.CharField(null=True, blank=True, max_length=15) subtypecod = models.BigIntegerField(null=True, blank=True) op_volt = models.CharField(null=True, blank=True, max_length=2) facilityid = models.CharField(null=True, blank=True, max_length=13) phasedesig = models.BigIntegerField(null=True, blank=True) ratekva = models.FloatField(null=True, blank=True) circuitcou = models.IntegerField(null=True, blank=True) configurat = models.CharField(null=True, blank=True, max_length=2) owner = models.CharField(null=True, blank=True, max_length=1) presenttap = models.CharField(null=True, blank=True, max_length=1) customerty = models.CharField(null=True, blank=True, max_length=1) loadstatus = models.IntegerField(null=True, blank=True) phasea_kva = models.FloatField(null=True, blank=True) phaseb_kva = models.FloatField(null=True, blank=True) phasec_kva = models.FloatField(null=True, blank=True) capnum = models.IntegerField(null=True, blank=True) totalkvar = models.IntegerField(null=True, blank=True) lanum = models.IntegerField(null=True, blank=True) constructi = models.CharField(null=True, blank=True, max_length=1) location = models.CharField(null=True, blank=True, max_length=50) angle = models.FloatField(null=True, blank=True) labeltext = models.CharField(null=True, blank=True, max_length=60) transforme = models.IntegerField(null=True, blank=True) installati = models.IntegerField(null=True, blank=True) creationus = models.CharField(null=True, blank=True, max_length=50) datecreate = models.DateField(null=True, blank=True) lastuser = models.CharField(null=True, blank=True, max_length=50) datemodifi = models.DateField(null=True, blank=True) feederid = models.CharField(null=True, blank=True, max_length=7) feederid2 = models.CharField(null=True, blank=True, max_length=7) feederinfo = models.BigIntegerField(null=True, blank=True) electrictr = models.BigIntegerField(null=True, blank=True) enabled = models.IntegerField(null=True, blank=True) wbs = models.CharField(null=True, blank=True, max_length=55) existingkw = models.FloatField(null=True, blank=True) existingkv = models.FloatField(null=True, blank=True) existing_1 = models.FloatField(null=True, blank=True) workreques = models.CharField(null=True, blank=True, max_length=20) designid = models.CharField(null=True, blank=True, max_length=20) worklocati = models.CharField(null=True, blank=True, max_length=20) workflowst = models.BigIntegerField(null=True, blank=True) workfuncti = models.BigIntegerField(null=True, blank=True) designtext = models.CharField(null=True, blank=True, max_length=100) graphicdes = models.CharField(null=True, blank=True, max_length=10) numberofus = models.BigIntegerField(null=True, blank=True) attachment = models.CharField(null=True, blank=True, max_length=254) matrefno = models.CharField(null=True, blank=True, max_length=30) globalid = models.CharField(null=True, blank=True, max_length=38) opvoltint = models.BigIntegerField(null=True, blank=True) objectid_1 = models.IntegerField(null=True, blank=True) name = models.CharField(null=True, blank=True, max_length=45) code = models.CharField(null=True, blank=True, max_length=14) creation_1 = models.CharField(null=True, blank=True, max_length=50) datecrea_1 = models.DateField(null=True, blank=True) lastuser_1 = models.CharField(null=True, blank=True, max_length=50) datemodi_1 = models.DateField(null=True, blank=True) workrequ_1 = models.CharField(null=True, blank=True, max_length=20) designid_1 = models.CharField(null=True, blank=True, max_length=20) workloca_1 = models.CharField(null=True, blank=True, max_length=20) workflow_1 = models.IntegerField(null=True, blank=True) workfunc_1 = models.IntegerField(null=True, blank=True) area_code = models.CharField(null=True, blank=True, max_length=2) shape_leng = models.FloatField(null=True, blank=True) shape_area = models.FloatField(null=True, blank=True) lat = models.FloatField(null=True, blank=True) long = models.FloatField(null=True, blank=True) loadProfile_base = models.CharField(null=True, blank=True, max_length=800) loadProfile_ev = models.CharField(null=True, blank=True, max_length=800) impact = models.CharField(null=True, blank=True, max_length=800) flag = models.BooleanField(default=False) geom = models.MultiPointField(srid=4326) def __str__(self): return self.facilityid
class Image(models.Model): unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) user = models.ForeignKey(UserModel, on_delete=models.CASCADE) camera_make = models.ForeignKey(CameraMake, on_delete=models.CASCADE, null=True, blank=True) camera_model = models.ForeignKey(CameraModel, on_delete=models.CASCADE, null=True, blank=True) cas = models.FloatField(default=0) captured_at = models.DateTimeField(null=True, blank=True) sequence = models.ForeignKey(Sequence, on_delete=models.CASCADE, null=True, blank=True) seq_key = models.CharField(max_length=100, default='') image_key = models.CharField(max_length=100) pano = models.BooleanField(default=False) user_key = models.CharField(max_length=100, default='') username = models.CharField(max_length=100, default='') organization_key = models.CharField(max_length=255, null=True) is_uploaded = models.BooleanField(default=False) is_private = models.BooleanField(default=False) is_mapillary = models.BooleanField(default=True) lat = models.FloatField(default=0) lng = models.FloatField(default=0) ele = models.FloatField(default=0) type = models.CharField(max_length=50, default='Point') point = models.PointField(null=True, blank=True) mapillary_image = models.ImageField(upload_to=image_directory_path, null=True, blank=True) image_label = models.ManyToManyField(LabelType, through='ImageLabel') map_feature_keys = ArrayField(ArrayField(models.CharField(max_length=50)), null=True, blank=True) map_feature_values = ArrayField(ArrayField( models.CharField(max_length=50)), null=True, blank=True) objects = models.Manager() vector_tiles = CustomImageMVTManager( geo_col='point', select_columns=['image_key', 'unique_id'], is_show_id=False, source_layer='mtp-images') def get_sequence_by_key(self): if self.seq_key != '': sequence = Sequence.objects.get(seq_key=self.seq_key) if sequence is None or not sequence: return None return sequence return None
class CompetitionResult(models.Model): """Výsledek soutěže""" class Meta: verbose_name = _("Výsledek soutěže") verbose_name_plural = _("Výsledky soutěží") unique_together = (("user_attendance", "competition"), ("team", "competition")) user_attendance = models.ForeignKey( UserAttendance, related_name="competitions_results", null=True, blank=True, default=None, on_delete=models.CASCADE, ) team = models.ForeignKey( Team, related_name="competitions_results", null=True, blank=True, default=None, on_delete=models.CASCADE, ) company = models.ForeignKey( Company, related_name="company_results", null=True, blank=True, default=None, on_delete=models.CASCADE, ) competition = models.ForeignKey( "Competition", related_name="results", null=False, blank=False, on_delete=models.CASCADE, ) result = models.DecimalField( verbose_name=_("Výsledek"), max_digits=10, decimal_places=6, null=True, blank=True, default=None, db_index=True, ) result_divident = models.FloatField( verbose_name=_("Dělenec"), null=True, blank=True, default=None, ) result_divisor = models.FloatField( verbose_name=_("Dělitel"), null=True, blank=True, default=None, ) created = models.DateTimeField( verbose_name=_("Datum vytvoření"), auto_now_add=True, null=True, ) updated = models.DateTimeField( verbose_name=_("Datum poslední změny"), auto_now=True, null=True, ) frequency = models.FloatField( verbose_name=_("Pravidelnost"), null=True, blank=True, default=0, ) distance = models.FloatField( verbose_name=_("Vzdalenost"), null=True, blank=True, default=0, ) def get_sequence_range(self): """ Return range of places of this result. Means, that the competitor is placed on one or more places. """ lower_range = (CompetitionResult.objects.filter( competition=self.competition, result__gt=self.result, ).count() + 1) upper_range = CompetitionResult.objects.filter( competition=self.competition, result__gte=self.result, ).count() return lower_range, upper_range def get_occupation(self): if self.user_attendance: return getattr(self.user_attendance.userprofile.occupation, "name", "-") def get_sex(self): if self.user_attendance: return self.user_attendance.userprofile.get_sex_display() def get_team(self): if self.competition.competitor_type in ["liberos", "single_user"]: return self.user_attendance.team if self.competition.competitor_type == "team": return self.team def get_team_name(self): return self.get_team().name or "" def get_company(self): if self.competition.competitor_type == "company": return self.company team = self.get_team() if team: return team.subsidiary.company def get_subsidiary(self): return "%s / %s" % (self.get_street(), self.get_company()) def get_street(self): team = self.get_team() if team: return team.subsidiary.address_street def get_city(self): team = self.get_team() if team: return team.subsidiary.city if self.company: return self.company.city def get_result(self): """Get result in kilometers rounded to reasonable number of decimal places.""" return round(self.result, 1) def get_result_divisor(self): if self.competition.competition_type == "frequency": return int(round(self.result_divisor)) else: return round(self.result_divisor, 1) def get_result_divident(self): if self.competition.competition_type == "frequency": return int(round(self.result_divident)) else: return round(self.result_divident, 1) def get_result_percentage(self): """ Get result as percentage of all rides. @return percentage in rounded integer """ if self.result: return round(self.result * 100, 1) else: return 0 def get_emissions(self): from ..util import get_emissions return get_emissions(self.distance) def competitor_attr(self, team_getter, company_getter, user_attendance_getter, default=""): if self.competition.competitor_type == "team": if self.team: return team_getter(self.team) elif self.competition.competitor_type == "company": if self.company: return company_getter(self.company) else: if self.user_attendance: return user_attendance_getter(self.user_attendance) return default def get_icon_url(self): return self.competitor_attr( lambda team: "%s" % team.icon_url(), lambda company: "%s" % company.icon_url(), lambda user_attendance: "%s" % user_attendance.avatar_url(), ) def __str__(self): return self.competitor_attr( lambda team: "%s" % team.name, lambda company: "%s" % company.name, lambda user_attendance: "%s" % user_attendance.userprofile.name(), ) def user_attendances(self): competition = self.competition if (competition.competitor_type == "single_user" or competition.competitor_type == "libero"): return [self.user_attendance] elif competition.competitor_type == "team": return self.team.members elif competition.competitor_type == "company": return UserAttendance.objects.filter( team__subsidiary__company=self.company)