class Plot(models.Model, ManagementMixin, PendingMixin): present = models.BooleanField(default=True) width = models.FloatField( null=True, blank=True, error_messages={'invalid': "Error: This value must be a number."}) length = models.FloatField( null=True, blank=True, error_messages={'invalid': "Error: This value must be a number."}) type = models.CharField(max_length=256, null=True, blank=True, choices=settings.CHOICES["plot_types"]) powerline_conflict_potential = models.CharField( max_length=256, choices=settings.CHOICES["powerlines"], help_text="Are there overhead powerlines present?", null=True, blank=True) sidewalk_damage = models.CharField(max_length=256, null=True, blank=True, choices=settings.CHOICES["sidewalks"]) address_street = models.CharField(max_length=256, blank=True, null=True) address_city = models.CharField(max_length=256, blank=True, null=True) address_zip = models.CharField(max_length=30, blank=True, null=True) neighborhood = models.ManyToManyField(Neighborhood, null=True) neighborhoods = models.CharField( max_length=150, null=True, blank=True) # Really this should be 'blank=True' and null=False zipcode = models.ForeignKey( ZipCode, null=True, blank=True) # Because it is calculated in the save method geocoded_accuracy = models.IntegerField(null=True, blank=True) geocoded_address = models.CharField(max_length=256, null=True, blank=True) geocoded_lat = models.FloatField(null=True, blank=True) geocoded_lon = models.FloatField(null=True, blank=True) geometry = models.PointField(srid=4326) #geocoded_geometry = models.PointField(null=True, srid=4326) #owner_geometry = models.PointField(null=True, srid=4326) #should we keep this? last_updated = models.DateTimeField(auto_now=True) last_updated_by = models.ForeignKey( User, related_name='plot_updated_by') # TODO set to current user history = audit.AuditTrail() import_event = models.ForeignKey(ImportEvent) objects = models.GeoManager() # The locate Manager encapsulates plot search functionality locate = PlotLocateManager() #original data to help owners associate back to their own db data_owner = models.ForeignKey(User, related_name="owner", null=True, blank=True) owner_orig_id = models.CharField(max_length=256, null=True, blank=True) owner_additional_id = models.CharField(max_length=255, null=True, blank=True) owner_additional_properties = models.TextField( null=True, blank=True, help_text="Additional Properties (not searchable)") readonly = models.BooleanField(default=False) def itree_region(self): zone = ClimateZone.objects.filter(geometry__contains=self.geometry) if len(zone) == 0: return None else: return zone[0].itree_region def validate(self): self.full_clean() em = ExclusionMask.objects.filter(geometry__contains=self.geometry) if em.count() > 0: raise ValidationError( "Geometry may not be within an exclusion zone.") def get_plot_type_display(self): for key, value in settings.CHOICES["plot_types"]: if key == self.type: return value return None def get_plot_size(self): length = self.length width = self.width if length == None: length = 'Missing' elif length == 99: length = '15+ ft' else: length = '%.2f ft' % length if width == None: width = 'Missing' elif width == 99: width = '15+ ft' else: width = '%.2f ft' % width #print length, width return '%s x %s' % (length, width) def get_sidewalk_damage_display(self): for key, value in settings.CHOICES["sidewalks"]: if key == self.sidewalk_damage: return value return None def get_powerline_conflict_display(self): for key, value in settings.CHOICES["powerlines"]: if key == self.powerline_conflict_potential: return value return None def get_stewardship_count(self): return len(self.plotstewardship_set.all()) def current_tree(self): trees = self.tree_set.filter(present=True) if trees.count() > 0: return trees[0] else: return None def get_active_pends(self): pends = self.plotpending_set.filter(status='pending') return pends def get_active_geopends(self): pends = self.plotpending_set.filter(status='pending').exclude( geometry=None) return pends def get_active_pends_with_tree_pends(self): plot_pends = self.plotpending_set.filter(status='pending') if self.current_tree(): tree_pends = self.current_tree().get_active_pends() else: tree_pends = [] pends = list(chain(plot_pends, tree_pends)) return pends def get_plot_size(self): length = self.length width = self.width if length == None: length = 'Missing' elif length == 99: length = '15+ ft' else: length = '%.2f ft' % length if width == None: width = 'Missing' elif width == 99: width = '15+ ft' else: width = '%.2f ft' % width return '%s x %s' % (length, width) def quick_save(self, *args, **kwargs): super(Plot, self).save(*args, **kwargs) def save(self, *args, **kwargs): self.validate() pnt = self.geometry n = Neighborhood.objects.filter(geometry__contains=pnt) z = ZipCode.objects.filter(geometry__contains=pnt) if n: oldns = self.neighborhoods new_nhoods = [] for nhood in n: if nhood: new_nhoods.append(nhood.id.__str__()) self.neighborhoods = " ".join(new_nhoods) else: self.neighborhoods = "" oldns = None if self.id: oldn = self.neighborhood.all() oldz = self.zipcode else: oldn = [] oldz = None super(Plot, self).save(*args, **kwargs) if n: self.neighborhood.clear() for nhood in n: if nhood: self.neighborhood.add(nhood) else: self.neighborhood.clear() if z: self.zipcode = z[0] else: self.zipcode = None if self.current_tree(): set_environmental_summaries(self.current_tree()) super(Plot, self).save(*args, **kwargs) if self.neighborhoods != oldns: done = [] if n: for nhood in n: if nhood.id in done: continue if self.current_tree(): self.current_tree().update_aggregate( AggregateNeighborhood, nhood) else: self.update_aggregate(AggregateNeighborhood, nhood) done.append(nhood.id) if oldn: for nhood in oldn: if nhood.id in done: continue if self.current_tree(): self.current_tree().update_aggregate( AggregateNeighborhood, nhood) else: self.update_aggregate(AggregateNeighborhood, nhood) done.append(nhood.id) if self.current_tree() and z and z[0] != oldz: if z: self.current_tree().update_aggregate(AggregateZipCode, z[0]) if oldz: self.current_tree().update_aggregate(AggregateZipCode, oldz) def update_aggregate(self, ag_model, location): agg = ag_model.objects.filter(location=location) if agg: agg = agg[0] else: agg = ag_model(location=location) #print agg.__dict__ #summaries = [] trees = Tree.objects.filter(plot__geometry__within=location.geometry) plots = Plot.objects.filter(geometry__within=location.geometry) #print trees agg.total_trees = trees.count() agg.total_plots = plots.count() agg.save() def validate_proximity(self, return_trees=False, max_count=1): if not self.geometry: return None nearby = Plot.objects.filter(present=True, geometry__distance_lte=(self.geometry, D(ft=10.0))) if nearby.count() > max_count: if return_trees: return nearby return (nearby.count() - max_count).__str__() #number greater than max_count allows return None def remove(self): """ Mark the plot and its associated objects as not present. """ if self.current_tree(): tree = self.current_tree() tree.remove() self.present = False self.save() for audit_trail_record in self.history.all(): audit_trail_record.present = False audit_trail_record.save()
class Biology(Occurrence): infraspecificepithet = models.CharField(null=True, blank=True, max_length=50) infraspecificrank = models.CharField(null=True, blank=True, max_length=50) authoryearofscientificname = models.CharField(null=True, blank=True, max_length=50) nomenclaturalcode = models.CharField(null=True, blank=True, max_length=50) identificationqualifier = models.CharField(null=True, blank=True, max_length=50) identifiedby = models.CharField(null=True, blank=True, max_length=100) dateidentified = models.DateTimeField(null=True, blank=True) typestatus = models.CharField(null=True, blank=True, max_length=50) sex = models.CharField(null=True, blank=True, max_length=50) lifestage = models.CharField(null=True, blank=True, max_length=50) preparations = models.CharField(null=True, blank=True, max_length=50) morphobanknum = models.IntegerField(null=True, blank=True) side = models.CharField(null=True, blank=True, max_length=50) attributes = models.CharField(null=True, blank=True, max_length=50) faunanotes = models.TextField(null=True, blank=True, max_length=64000) toothupperorlower = models.CharField(null=True, blank=True, max_length=50) toothnumber = models.CharField(null=True, blank=True, max_length=50) toothtype = models.CharField(null=True, blank=True, max_length=50) umtoothrowlengthmm = models.FloatField(null=True, blank=True) um1lengthmm = models.FloatField(null=True, blank=True) um1widthmm = models.FloatField(null=True, blank=True) um2lengthmm = models.FloatField(null=True, blank=True) um2widthmm = models.FloatField(null=True, blank=True) um3lengthmm = models.FloatField(null=True, blank=True) um3widthmm = models.FloatField(null=True, blank=True) lmtoothrowlengthmm = models.FloatField(null=True, blank=True) lm1length = models.FloatField(null=True, blank=True) lm1width = models.FloatField(null=True, blank=True) lm2length = models.FloatField(null=True, blank=True) lm2width = models.FloatField(null=True, blank=True) lm3length = models.FloatField(null=True, blank=True) lm3width = models.FloatField(null=True, blank=True) element = models.CharField(null=True, blank=True, max_length=50) elementmodifier = models.CharField(null=True, blank=True, max_length=50) # TODO convert this field to boolean uli1 = models.IntegerField(null=True, blank=True) uli2 = models.IntegerField(null=True, blank=True) uli3 = models.IntegerField(null=True, blank=True) uli4 = models.IntegerField(null=True, blank=True) uli5 = models.IntegerField(null=True, blank=True) uri1 = models.IntegerField(null=True, blank=True) uri2 = models.IntegerField(null=True, blank=True) uri3 = models.IntegerField(null=True, blank=True) uri4 = models.IntegerField(null=True, blank=True) uri5 = models.IntegerField(null=True, blank=True) ulc = models.IntegerField(null=True, blank=True) urc = models.IntegerField(null=True, blank=True) ulp1 = models.IntegerField(null=True, blank=True) ulp2 = models.IntegerField(null=True, blank=True) ulp3 = models.IntegerField(null=True, blank=True) ulp4 = models.IntegerField(null=True, blank=True) urp1 = models.IntegerField(null=True, blank=True) urp2 = models.IntegerField(null=True, blank=True) urp3 = models.IntegerField(null=True, blank=True) urp4 = models.IntegerField(null=True, blank=True) ulm1 = models.IntegerField(null=True, blank=True) ulm2 = models.IntegerField(null=True, blank=True) ulm3 = models.IntegerField(null=True, blank=True) urm1 = models.IntegerField(null=True, blank=True) urm2 = models.IntegerField(null=True, blank=True) urm3 = models.IntegerField(null=True, blank=True) lli1 = models.IntegerField(null=True, blank=True) lli2 = models.IntegerField(null=True, blank=True) lli3 = models.IntegerField(null=True, blank=True) lli4 = models.IntegerField(null=True, blank=True) lli5 = models.IntegerField(null=True, blank=True) lri1 = models.IntegerField(null=True, blank=True) lri2 = models.IntegerField(null=True, blank=True) lri3 = models.IntegerField(null=True, blank=True) lri4 = models.IntegerField(null=True, blank=True) lri5 = models.IntegerField(null=True, blank=True) llc = models.IntegerField(null=True, blank=True) lrc = models.IntegerField(null=True, blank=True) llp1 = models.IntegerField(null=True, blank=True) llp2 = models.IntegerField(null=True, blank=True) llp3 = models.IntegerField(null=True, blank=True) llp4 = models.IntegerField(null=True, blank=True) lrp1 = models.IntegerField(null=True, blank=True) lrp2 = models.IntegerField(null=True, blank=True) lrp3 = models.IntegerField(null=True, blank=True) lrp4 = models.IntegerField(null=True, blank=True) llm1 = models.IntegerField(null=True, blank=True) llm2 = models.IntegerField(null=True, blank=True) llm3 = models.IntegerField(null=True, blank=True) lrm1 = models.IntegerField(null=True, blank=True) lrm2 = models.IntegerField(null=True, blank=True) lrm3 = models.IntegerField(null=True, blank=True) taxon = models.ForeignKey(Taxon, related_name='drp_biology_occurrences') identification_qualifier = models.ForeignKey( IdentificationQualifier, related_name='drp_biology_occurrences') class Meta: verbose_name = "DRP Biology" verbose_name_plural = "DRP Biology" #db_table='drp_biology' def __unicode__(self): return str(self.taxon.__unicode__())
class Record(GroutModel): """ An entity in the database. An entry of a given RecordType, following a schema defined by a certain RecordSchema. """ schema = models.ForeignKey('RecordSchema', on_delete=models.CASCADE) data = JSONField(blank=True) # `blank` lets us store empty dicts ({}). archived = models.BooleanField(default=False) occurred_from = models.DateTimeField(null=True, blank=True) occurred_to = models.DateTimeField(null=True, blank=True) geom = models.GeometryField(srid=settings.GROUT['SRID'], null=True, blank=True) location_text = models.CharField(max_length=200, null=True, blank=True) class Meta(object): ordering = ('-created', ) def clean_geom(self): """ Validate that the geometry of a new Record matches the geometry_type of its associated RecordType. :return: None if schema validates; otherwise, returns an error dict in the format {'geom': '<error message>'} """ expected_geotype = self.schema.record_type.get_geometry_type_display() if self.geom: incoming_geotype = self.geom.geom_type else: incoming_geotype = 'None' if incoming_geotype != expected_geotype: return { 'geom': GEOMETRY_TYPE_ERROR.format(incoming=incoming_geotype, expected=expected_geotype, uuid=self.schema.record_type.uuid) } else: return None def clean_datetime(self): """ Validate that the values for `occurred_from` and `occurred_to` match the `temporal` setting of the parent Record type and are syntactically valid. :return: None if the fields validate; otherwise, returns an error dict in the format {'occurred_from': '<error_message>', 'occurred_to': '<error_message>'} """ errors = {} datetime_required = self.schema.record_type.temporal if datetime_required: if self.occurred_from is None or self.occurred_to is None: # `occurred_from` and `occurred_to` must be present on a temporal # Record. if self.occurred_from is None: errors['occurred_from'] = DATETIME_REQUIRED.format( uuid=self.schema.record_type.uuid) if self.occurred_to is None: errors['occurred_to'] = DATETIME_REQUIRED.format( uuid=self.schema.record_type.uuid) else: # `occurred_from` cannot be a later date than `occurred_to`. # Since by the time that this method is called the values are # already attributes on the model, and since we've already # confirmed that neither value is null, we can assume for the # purposes of comparison that the values are datetime objects. if self.occurred_from > self.occurred_to: errors['occurred_from'] = MIN_DATE_RANGE_ERROR errors['occurred_to'] = MAX_DATE_RANGE_ERROR else: if self.occurred_from is not None: errors['occurred_from'] = DATETIME_NOT_PERMITTED.format( uuid=self.schema.record_type.uuid) if self.occurred_to is not None: errors['occurred_to'] = DATETIME_NOT_PERMITTED.format( uuid=self.schema.record_type.uuid) if errors.keys(): return errors else: return None def clean_data(self): """ Validate that the JSON represented by the `data` field matches the schema for this Record. :return: None if the schema validates; otherwise, returns an error dict in the format {'data': '<error_message'} """ try: return self.schema.validate_json(self.data) except jsonschema.exceptions.ValidationError as e: return { 'data': SCHEMA_MISMATCH_ERROR.format(uuid=self.schema.uuid, message=e.message) } def clean(self): """ Provide custom field validation for this model. """ errors = {} # Make sure that incoming geometry matches the geometry_type of the # RecordType for this Record. geom_error = self.clean_geom() if geom_error: errors.update(geom_error) # Make sure that incoming datetime information matches the `temporal` # flag on the RecordType for this Record. datetime_error = self.clean_datetime() if datetime_error: errors.update(datetime_error) # Make sure that the incoming JSON data matches the RecordSchema # for this Record. schema_error = self.clean_data() if schema_error: errors.update(schema_error) if errors.keys(): # Raise a DRF ValidationError instead of a Django Core validation # error, since this exception needs to get handled by the serializer. raise serializers.ValidationError(errors) def save(self, *args, **kwargs): """ Extend the model's save method to run custom field validators. """ self.clean() return super(Record, self).save(*args, **kwargs)
class Factory(SoftDeleteMixin): """Factories that are potential to be illegal.""" # List of fact_type & status factory_type_list = [ ("2-1","沖床、銑床、車床、鏜孔"), ("2-2", "焊接、鑄造、熱處理"), ("2-3", "金屬表面處理、噴漆"), ("3", "塑膠加工、射出"), ("4", "橡膠加工"), ("5", "非金屬礦物(石材)"), ("6", "食品"), ("7", "皮革"), ("8", "紡織"), ("9", "其他"), ] cet_review_status_list = [ ("A", "尚未審查"), ("O", "已審查-不檢舉"), ("P", "已審查-需補件"), ("Q", "已審查-待檢舉"), ("X", "已審查-已生成公文"), ] cet_report_status_list = [ ("A", "未舉報"), ("O", "第一次發文待回覆"), ("P", "第一次發文已播電話追蹤"), ("Q", "第一次回文"), ("X", "第二次發文待回覆"), ("Y", "第二次發文已播電話追蹤"), ("Z", "第二次回文"), ("B", "已結案"), ] source_list = [ ("G", "政府"), ("U", "使用者"), ] # All Features id = models.UUIDField( primary_key=True, default=uuid.uuid4, editable=False, verbose_name="ID", ) lat = models.FloatField() lng = models.FloatField() point = models.PointField(srid=settings.POSTGIS_SRID) landcode = models.CharField(max_length=50, blank=True, null=True) towncode = models.CharField(max_length=50, blank=True, null=True) townname = models.CharField(max_length=50, blank=True, null=True) sectcode = models.CharField(max_length=50, blank=True, null=True) sectname = models.CharField(max_length=50, blank=True, null=True) name = models.CharField(max_length=50, blank=True, null=True) factory_type = models.CharField( max_length=3, choices=factory_type_list, blank=True, null=True, ) before_release = models.BooleanField(default=False) # 從 full-info.csv 匯入的那些都是 True ,使用者新增的通通是 False source = models.CharField( max_length=1, choices=source_list, default="U", ) cet_review_status = models.CharField( max_length=1, choices=cet_review_status_list, default="A", ) # 地球公民基金會的審閱狀態(舉報前) cet_report_status = models.CharField( max_length=1, choices=cet_report_status_list, default="A", ) # 地球公民基金會的舉報狀態 status_time = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def save(self, *args, **kwargs): self.point = Point(self.lng, self.lat, srid=4326) self.point.transform(settings.POSTGIS_SRID) super(Factory, self).save(*args, **kwargs)
class PositionReport(models.Model): timestamp_received = models.DateTimeField(null=True, blank=True) latitude = models.DecimalField(max_digits=11, decimal_places=6, null=True, blank=True) longitude = models.DecimalField(max_digits=11, decimal_places=6, null=True, blank=True) heading = models.IntegerField(null=True, blank=True) altitude = models.IntegerField(null=True, blank=True) speed = models.IntegerField(null=True, blank=True) source = models.ForeignKey(PositionReportSource) quality = models.IntegerField( 'The receiver-generated "quality" of this particular report.', null=True, blank=True) satellites_visible = models.IntegerField( 'The number of satellites visible to the receiver', null=True, blank=True) hdop = models.CharField(max_length=10, null=True, blank=True) geoid_height_above_wgs84_meters = models.DecimalField(max_digits=10, decimal_places=3, null=True, blank=True) geoidal_seperation_meters = models.IntegerField(null=True, blank=True) time_since_dgps_update = models.CharField(max_length=10, null=True, blank=True) dgps_reference_station_id = models.CharField(max_length=10, null=True, blank=True) receiver_warning = models.NullBooleanField(null=True, blank=True) magnetic_variation = models.DecimalField(max_digits=10, decimal_places=3, null=True, blank=True) magnetic_variation_direction = models.CharField(max_length=1, null=True, blank=True) checksum_gpgga = models.CharField('$GPGGA Checksum', max_length=10, null=True, blank=True) checksum_gprmc = models.CharField('$GPRMC Checksum', max_length=10, null=True, blank=True) duration_seconds = models.IntegerField('Duration (seconds)', null=True, blank=True) user = models.ForeignKey(User) timestamp = models.DateTimeField(auto_now_add=True) active = models.BooleanField(default=False) public = models.BooleanField(default=True) should_tumble = models.BooleanField(default=True) point = models.PointField(srid=4326, null=True, blank=True) objects = models.GeoManager() def get_bubble_text(self): bubble_text = 'Source: %s<br/>Received %s (Central)' % ( self.source.name, self.timestamp_received) if self.speed != None and self.heading != None: bubble_text = '%s<br/>Compass Direction: %s degrees' % ( bubble_text, self.heading) elif self.heading != None: bubble_text = '%s<br/>Compass Direction: %s degrees' % ( bubble_text, self.speed, self.heading) if self.source.name == 'InstaMapper iPhone': bubble_text = '%s<br/>InstaMapper Available (Free) @ <a href=\'http://www.instamapper.com\'>instamapper.com</a>' % bubble_text return bubble_text def __unicode__(self): return u'Received %s, Lat/%s Lon/%s' % (self.timestamp_received, self.latitude, self.longitude)
class Marvin(models.Model): objects = MarvinManager() name = models.CharField(_('name'), max_length=100, unique=True) hostname = models.CharField( _('hostname'), max_length=127, validators=[ RegexValidator(URLValidator.host_re, message=_("Please provide a valid host name")) ]) type = models.CharField(_('type'), max_length=50) version = ArrayField(models.PositiveSmallIntegerField(), verbose_name=_('version')) browser_name = models.CharField(_('browser name'), max_length=150) browser_version = ArrayField(models.PositiveSmallIntegerField(), verbose_name=_('browser version')) instance_type = models.CharField(_('instance type'), max_length=10, choices=[ ('dual-stack', _('Dual-stack')), ('v4only', _('IPv4-only')), ('v6only', _('IPv6-only')), ('nat64', _('IPv6 with NAT64')), ]) addresses = ArrayField(models.GenericIPAddressField(), verbose_name=_('addresses'), default=list) first_seen = models.DateTimeField(_('first seen'), auto_now_add=True) last_seen = models.DateTimeField(_('last seen')) is_alive = models.BooleanField(_('is alive'), default=True) parallel_tasks_limit = models.PositiveIntegerField( _('parallel tasks limit')) class Meta: ordering = ('-is_alive', 'instance_type', '-last_seen') def __str__(self): return _('{name} ({type}: {is_alive})').format( name=self.name, type=self.instance_type, is_alive=self.is_alive and _('alive') or _('dead')) def natural_key(self): return self.name @property def cache_key(self): return 'marvin_{}'.format(self.name) @property def tasks(self): return cache.get(self.cache_key, 0) @tasks.setter def tasks(self, value): cache.set(self.cache_key, value) def __enter__(self): key = self.cache_key cache.add(key, 0) cache.incr(key) def __exit__(self, exc_type, exc_val, exc_tb): key = self.cache_key cache.add(key, 1) cache.decr(key) def display_version(self): return '.'.join(map(str, self.version)) display_version.short_description = _('version') display_version.admin_order_field = 'version' def display_browser_version(self): return '.'.join(map(str, self.browser_version)) display_browser_version.short_description = _('browser version') display_browser_version.admin_order_field = 'browser_version' def last_seen_display(self): return naturaltime(self.last_seen) last_seen_display.short_description = _('last seen') last_seen_display.admin_order_field = 'last_seen'
class Tour(models.Model): unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True) user = models.ForeignKey(UserModel, on_delete=models.CASCADE) tour_tag = models.ManyToManyField(TourTag) name = models.CharField(max_length=100, default='') username = models.CharField(max_length=100, null=True) description = models.TextField(default='') created_at = models.DateTimeField(default=datetime.now, blank=True) updated_at = models.DateTimeField(default=datetime.now, blank=True) is_published = models.BooleanField(default=True) def get_absolute_url(self): from django.urls import reverse return reverse('tour.tour_detail', kwargs={'unique_id': str(self.unique_id)}) def get_tag_str(self): tags = [] if self.tour_tag is None: return '' for tag in self.tour_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.tour_tag is None: return [] for tag in self.tour_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 geometry_sequence(self): tour_sequences = TourSequence.objects.filter(tour=self) geometry = [] if tour_sequences.count() > 0: for t_s in tour_sequences: sequence = t_s.sequence first_image = sequence.geometry_coordinates_ary[0] geometry.append(first_image) return geometry def get_first_sequence_captured(self): tour_sequences = TourSequence.objects.filter(tour=self) if tour_sequences.count() > 0: return tour_sequences[0].sequence.captured_at else: return '' def get_first_sequence_created(self): tour_sequences = TourSequence.objects.filter(tour=self) if tour_sequences.count() > 0: return tour_sequences[0].sequence.created_at else: return '' def get_image_count(self): tour_sequences = TourSequence.objects.filter(tour=self) image_count = 0 if tour_sequences.count() > 0: for tour_sequence in tour_sequences: sequence = tour_sequence.sequence image_count += sequence.get_image_count() return image_count def get_like_count(self): liked_tour = TourLike.objects.filter(tour=self) if not liked_tour: return 0 else: return liked_tour.count() def getSequenceCount(self): tour_sequences = TourSequence.objects.filter(tour=self) return tour_sequences.count() def get_distance(self): tour_sequences = TourSequence.objects.filter(tour=self) distance = 0 if tour_sequences.count() > 0: for t_s in tour_sequences: distance += float(t_s.sequence.get_distance()) return "%.3f" % distance def get_cover_image(self): tour_sequences = TourSequence.objects.filter(tour=self) if tour_sequences.count() > 0: sequence = tour_sequences[0].sequence return sequence.get_cover_image() else: return None def get_cover_imageUserOnMapillary(self): tour_sequences = TourSequence.objects.filter(tour=self) if tour_sequences.count() > 0: sequence = tour_sequences[0].sequence return sequence.username else: return ''
class Person(models.Model): gender = models.CharField(max_length=1, choices=GENDER_CHOICES) # Jards Macalé is an amazing brazilian musician! =] enjoy_jards_macale = models.BooleanField(default=True) like_metal_music = models.BooleanField(default=False) name = models.CharField(max_length=30) nickname = models.SlugField(max_length=36) age = models.IntegerField() bio = models.TextField() birthday = models.DateField() birth_time = models.TimeField() appointment = models.DateTimeField() blog = models.URLField() occupation = models.CharField(max_length=10, choices=OCCUPATION_CHOICES) uuid = models.UUIDField(primary_key=False) name_hash = models.BinaryField(max_length=16) days_since_last_login = models.BigIntegerField() duration_of_sleep = models.DurationField() email = models.EmailField() id_document = models.CharField(unique=True, max_length=10) try: from django.db.models import JSONField data = JSONField() except ImportError: # Skip JSONField-related fields pass try: from django.contrib.postgres.fields import ArrayField, HStoreField from django.contrib.postgres.fields import JSONField as PostgresJSONField from django.contrib.postgres.fields.citext import ( CICharField, CIEmailField, CITextField, ) from django.contrib.postgres.fields.ranges import ( BigIntegerRangeField, DateRangeField, DateTimeRangeField, IntegerRangeField, ) if settings.USING_POSTGRES: acquaintances = ArrayField(models.IntegerField()) postgres_data = PostgresJSONField() hstore_data = HStoreField() ci_char = CICharField(max_length=30) ci_email = CIEmailField() ci_text = CITextField() int_range = IntegerRangeField() bigint_range = BigIntegerRangeField() date_range = DateRangeField() datetime_range = DateTimeRangeField() except ImportError: # Skip PostgreSQL-related fields pass try: from django.contrib.postgres.fields.ranges import FloatRangeField if settings.USING_POSTGRES: float_range = FloatRangeField() except ImportError: # Django version greater or equal than 3.1 pass try: from django.contrib.postgres.fields.ranges import DecimalRangeField if settings.USING_POSTGRES: decimal_range = DecimalRangeField() except ImportError: # Django version lower than 2.2 pass if BAKER_GIS: geom = models.GeometryField() point = models.PointField() line_string = models.LineStringField() polygon = models.PolygonField() multi_point = models.MultiPointField() multi_line_string = models.MultiLineStringField() multi_polygon = models.MultiPolygonField() geom_collection = models.GeometryCollectionField()
class OTP(models.Model): to = models.CharField(max_length=35, default='') otp = models.CharField(max_length=8, default='') status = models.CharField(max_length=20, default='') time = models.DateTimeField(default='')
class Experiment(models.Model): description = models.TextField() name = models.TextField() run_time = models.DateTimeField()
class Walk(models.Model): """ A Walk contains the route of the walk and all location data about the start location """ name = models.CharField(max_length=255) slug = models.SlugField(max_length=255) description = models.TextField() start = models.PointField(blank=True, null=True) route = models.LineStringField() submitter = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user)) route_length = models.FloatField( help_text="The length of the walk in miles", default=0 ) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) # Reverse Geocoding from OpenCage reverse_geocode_cache_time = models.DateTimeField(blank=True, null=True) # annotations what3words = models.CharField(max_length=100, blank=True, null=True) geohash = models.CharField(max_length=50, blank=True, null=True) # components continent = models.CharField(max_length=100, blank=True, null=True) country = models.CharField(max_length=100, blank=True, null=True) state = models.CharField(max_length=100, blank=True, null=True) county = models.CharField(max_length=100, blank=True, null=True) city = models.CharField(max_length=100, blank=True, null=True) suburb = models.CharField(max_length=100, blank=True, null=True) road = models.CharField(max_length=100, blank=True, null=True) postcode = models.CharField(max_length=100, blank=True, null=True) formatted = models.CharField(max_length=500, blank=True, null=True) attributes = models.ManyToManyField(Attribute, blank=True) def get_absolute_url(self): return reverse( "walk-detail", kwargs={"username": self.submitter.username, "slug": self.slug}, ) def __str__(self): return self.name def save(self, *args, **kwargs): initial_start = self.start self.slug = slugify(self.name) super().save(*args, **kwargs) # Check if the start location has changed start_location_changed = False if not self.start: self.start = self.calculate_walk_start() start_location_changed = True if self.start != initial_start: start_location_changed = True # check if the cache_has expired if self.reverse_geocode_cache_time is None: cache_expired = True else: cache_expired = self.reverse_geocode_cache_time < ( now() - timedelta(days=180) ) # update the location details if we need to if start_location_changed or cache_expired: if settings.OPENCAGE_API_KEY: geocoder = OpenCageGeocode(settings.OPENCAGE_API_KEY) try: results = geocoder.reverse_geocode( round(self.start[1], 6), round(self.start[0], 6), language="en", limit=1, ) if results and len(results): self.what3words = ( results[0].get("annotations").get("what3words").get("words") ) self.geohash = results[0].get("annotations").get("geohash") self.continent = results[0].get("components").get("continent") self.country = results[0].get("components").get("country") self.state = results[0].get("components").get("state") self.county = results[0].get("components").get("county") self.city = results[0].get("components").get("city") self.suburb = results[0].get("components").get("suburb") self.road = results[0].get("components").get("road") self.postcode = results[0].get("components").get("postcode") self.formatted = results[0].get("formatted") self.reverse_geocode_cache_time = now() super().save(*args, **kwargs) except RateLimitExceededError as e: print(e) self.route.transform(3857) self.route_length = D(m=self.route.length).mi super().save(*args, **kwargs) def calculate_walk_start(self): return Point(srid=self.route.srid, x=self.route[0][0], y=self.route[0][1],)
class Audit(models.Model): model = models.CharField(max_length=255, null=True, db_index=True) model_id = models.IntegerField(null=True, db_index=True) instance = models.ForeignKey('Instance', null=True, blank=True, db_index=True) field = models.CharField(max_length=255, null=True) previous_value = models.TextField(null=True) current_value = models.TextField(null=True, db_index=True) user = models.ForeignKey('treemap.User') action = models.IntegerField() """ These two fields are part of the pending edit system If requires_auth is True then this audit record represents a change that was *requested* but not applied. When an authorized user approves a pending edit it creates an audit record on this model with an action type of either "PendingApprove" or "PendingReject" ref can be set on *any* audit to note that it has been looked at and approved or rejected. If this is the case, the ref audit will be of type "ReviewApproved" or "ReviewRejected" An audit that is "PendingApproved/Rejected" cannot be be "ReviewApproved/Rejected" """ requires_auth = models.BooleanField(default=False) ref = models.ForeignKey('Audit', null=True) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True, db_index=True) class Type: Insert = 1 Delete = 2 Update = 3 PendingApprove = 4 PendingReject = 5 ReviewApprove = 6 ReviewReject = 7 TYPES = { Type.Insert: trans('Create'), Type.Delete: trans('Delete'), Type.Update: trans('Update'), Type.PendingApprove: trans('Approved Pending Edit'), Type.PendingReject: trans('Reject Pending Edit'), Type.ReviewReject: trans('Rejected Edit'), Type.ReviewApprove: trans('Approved Edit') } def _deserialize_value(self, value): """ A helper method to transform deserialized audit strings When an audit record is written to the audit table, the value is stored as a string. When deserializing these values for presentation purposes or constructing objects, they need to be converted to their correct python value. Where possible, django model field classes are used to convert the value. """ # some django fields can't handle .to_python(None), but # for insert audits (None -> <value>) this method will # still be called. if value is None: return None # get the model/field class for each audit record and convert # the value to a python object cls = get_auditable_class(self.model) field_query = cls._meta.get_field_by_name(self.field) field_cls, fk_model_cls, is_local, m2m = field_query field_modified_value = field_cls.to_python(value) # handle edge cases if isinstance(field_cls, models.GeometryField): field_modified_value = GEOSGeometry(field_modified_value) elif isinstance(field_cls, models.ForeignKey): field_modified_value = field_cls.rel.to.objects.get( pk=field_modified_value) return field_modified_value def _unit_format(self, value): model_name = self.model.lower() if isinstance(value, GEOSGeometry): if value.geom_type == 'Point': return '%d,%d' % (value.x, value.y) if value.geom_type in {'MultiPolygon', 'Polygon'}: value = value.area if is_convertible_or_formattable(model_name, self.field): _, value = get_display_value(self.instance, model_name, self.field, value) if value and is_convertible(model_name, self.field): units = get_unit_name( get_units(self.instance, model_name, self.field)) value += (' %s' % units) return value @property def clean_current_value(self): if self.field and self.field.startswith('udf:'): return self.current_value else: return self._deserialize_value(self.current_value) @property def clean_previous_value(self): if self.field.startswith('udf:'): return self.previous_value else: return self._deserialize_value(self.previous_value) @property def current_display_value(self): return self._unit_format(self.clean_current_value) @property def previous_display_value(self): return self._unit_format(self.clean_previous_value) @property def field_display_name(self): if not self.field: return '' name = self.field if name.startswith('udf:'): return name[4:] else: return name.replace('_', ' ') @property def display_action(self): return Audit.TYPES[self.action] @classmethod def audits_for_model(clz, model_name, inst, pk): return Audit.objects.filter(model=model_name, model_id=pk, instance=inst).order_by('created') @classmethod def pending_audits(clz): return Audit.objects.filter(requires_auth=True)\ .filter(ref__isnull=True)\ .order_by('created') @classmethod def audits_for_object(clz, obj): return clz.audits_for_model(obj._model_name, obj.instance, obj.pk) def short_descr(self): cls = get_auditable_class(self.model) # If a model has a defined short_descr method, use that if hasattr(cls, 'short_descr'): return cls.short_descr(self) format_string = cls.action_format_string_for_audit(self) if hasattr(cls, 'display_name'): model_display_name = cls.display_name else: model_display_name = trans(self.model) return format_string % { 'field': self.field_display_name, 'model': model_display_name.lower(), 'value': self.current_display_value } def dict(self): return { 'model': self.model, 'model_id': self.model_id, 'instance_id': self.instance.pk, 'field': self.field, 'previous_value': self.previous_value, 'current_value': self.current_value, 'user_id': self.user.pk, 'action': self.action, 'requires_auth': self.requires_auth, 'ref': self.ref.pk if self.ref else None, 'created': str(self.created) } def __unicode__(self): return u"pk=%s - action=%s - %s.%s:(%s) - %s => %s" % \ (self.pk, self.TYPES[self.action], self.model, self.field, self.model_id, self.previous_value, self.current_value) def is_pending(self): return self.requires_auth and not self.ref
class CommentFlag(models.Model): flagged = models.BooleanField(default=False) flagged_date = models.DateTimeField(auto_now=True) comment = models.ForeignKey(ThreadedComment, related_name="comment_flags") user = models.ForeignKey(User)
class Tree(models.Model, ManagementMixin, PendingMixin): def __init__(self, *args, **kwargs): super(Tree, self).__init__(*args, **kwargs) #save, in order to get ID for the tree #owner properties based on wiki/DatabaseQuestions plot = models.ForeignKey(Plot) tree_owner = models.CharField(max_length=256, null=True, blank=True) steward_name = models.CharField(max_length=256, null=True, blank=True) #only modifyable by admin steward_user = models.ForeignKey( User, null=True, blank=True, related_name="steward") #only modifyable by admin sponsor = models.CharField(max_length=256, null=True, blank=True) #only modifyable by us species = models.ForeignKey(Species, verbose_name="Scientific name", null=True, blank=True) species_other1 = models.CharField(max_length=255, null=True, blank=True) species_other2 = models.CharField(max_length=255, null=True, blank=True) orig_species = models.CharField(max_length=256, null=True, blank=True) dbh = models.FloatField(null=True, blank=True) #gets auto-set on save height = models.FloatField( null=True, blank=True, error_messages={'invalid': "Error: This value must be a number."}) canopy_height = models.FloatField( null=True, blank=True, error_messages={'invalid': "Error: This value must be a number."}) date_planted = models.DateField(null=True, blank=True) date_removed = models.DateField(null=True, blank=True) present = models.BooleanField(default=True) last_updated = models.DateTimeField(auto_now=True) last_updated_by = models.ForeignKey( User, related_name='updated_by') # TODO set to current user s_order = models.IntegerField(null=True, blank=True) photo_count = models.IntegerField(null=True, blank=True) objects = models.GeoManager() history = audit.AuditTrail() projects = models.CharField(max_length=20, null=True, blank=True) import_event = models.ForeignKey(ImportEvent) condition = models.CharField(max_length=256, null=True, blank=True, choices=settings.CHOICES["conditions"]) canopy_condition = models.CharField( max_length=256, null=True, blank=True, choices=settings.CHOICES["canopy_conditions"]) readonly = models.BooleanField(default=False) url = models.URLField(max_length=255, null=True, blank=True) pests = models.CharField(max_length=256, null=True, blank=True, choices=settings.CHOICES["pests"]) def has_common_attributes(self): if self.get_flag_count > 0: return True if self.species: spp = self.species if spp.flower_conspicuous or spp.fall_conspicuous or spp.palatable_human or spp.native_status: return True return False def get_absolute_url(self): return "/trees/%i/" % self.id def get_display(self, choices, val): for key, value in settings.CHOICES[choices]: if key == val: return value return None def get_condition_display(self): return self.get_display("conditions", self.condition) def get_canopy_condition_display(self): return self.get_display("canopy_condition", self.canopy_condition) def get_pests_display(self): return self.get_display("pests", self.pests) def get_scientific_name(self): if self.species: sn = self.species.scientific_name if not sn: sn = self.species.genus if self.species.cultivar_name: sn += " '%s'" % self.species.cultivar_name return sn else: return 'unavailable' def get_common_name(self): if self.species: return self.species.common_name return 'unavailable' def get_eco_impact(self): tr = TreeResource.objects.filter(tree=self) if tr: return "%0.2f" % tr[0].total_benefit() def get_action_count(self): return len(self.treeaction_set.all()) def get_alert_count(self): return len(self.treealert_set.all()) def get_flag_count(self): return len(self.treeflags_set.all()) def get_stewardship_count(self): return len(self.treestewardship_set.all()) def get_active_pends(self): pends = self.treepending_set.filter(status='pending') return pends def is_complete(self): if self.species >= 0 and self.dbh: return True else: return False def set_species(self, species_id, commit=True): """ sets the species, and updates the species tree count """ self.old_species = self.species new_species = Species.objects.get(id=species_id) self.species = new_species if commit: self.save() def save(self, *args, **kwargs): #save new neighborhood/zip connections if needed self.photo_count = self.treephoto_set.count() self.projects = "" for fl in self.treeflags_set.all(): self.projects = self.projects + " " + fl.key super(Tree, self).save(*args, **kwargs) self.quick_save(*args, **kwargs) def quick_save(self, *args, **kwargs): super(Tree, self).save(*args, **kwargs) set_environmental_summaries(self) #set new species counts if hasattr(self, 'old_species') and self.old_species: self.old_species.save() if hasattr(self, 'species') and self.species: self.species.save() self.plot.last_updated = self.last_updated self.plot.save() def update_aggregate(self, ag_model, location): agg = ag_model.objects.filter(location=location) if agg: agg = agg[0] else: agg = ag_model(location=location) #print agg.__dict__ #summaries = [] trees = Tree.objects.filter(plot__geometry__within=location.geometry) plots = Plot.objects.filter(geometry__within=location.geometry) #print trees agg.total_trees = trees.count() agg.total_plots = plots.count() trees = trees.exclude(Q(dbh=None) | Q(dbh=0.0)).exclude(species=None) #print agg.total_trees #TODO figure out how to summarize diff stratum stuff field_names = [ x.name for x in ResourceSummaryModel._meta.fields if not x.name == 'id' ] if agg.total_trees == 0: for f in field_names: setattr(agg, f, 0.0) else: #TODO speed this up for f in field_names: fn = 'treeresource__' + f s = trees.aggregate(Sum(fn))[fn + '__sum'] or 0.0 setattr(agg, f, s) agg.save() def percent_complete(self): has = 0 attr = settings.COMPLETE_ARRAY for item in attr: if hasattr(self, item): if getattr(self, item): has += 1 elif hasattr(self.plot, item): if getattr(self.plot, item): has += 1 return has / float(len(attr)) * 100 def validate_all(self): #print watch_tests for test, method in watch_tests.iteritems(): #print test result = getattr(self, method)() #print result # check for results and save - passed tests return None if not result: TreeWatch.objects.filter(tree=self, key=watch_choices[test]).delete() continue # if identical watch already exists skip it if TreeWatch.objects.filter(tree=self, key=watch_choices[test], value=result): continue TreeWatch.objects.filter(tree=self, key=watch_choices[test]).delete() self.treewatch_set.create( key=watch_choices[test], value=result, ) def validate_proximity(self, return_trees=False, max_count=1): if not self.plot.geometry: return None return self.plot.validate_proximity() # Disallowed combinations: # Dead + 0% loss, Dead + 25% loss, Dead + 50% loss, Dead + 75% loss # Excellent + 100% loss, Excellent + 75% loss def validate_canopy_condition(self): if not self.canopy_condition or not self.condition: return None cond = self.condition c_cond = self.canopy_condition if cond == 'Dead': if not c_cond == 'Little or None (up to 100% missing)' and not c_cond == 'None': return cond + ", " + c_cond elif cond == 'Excellent': if c_cond == 'Little or None (up to 100% missing)' or c_cond == 'Large Gaps (up to 75% missing)': return cond + ", " + c_cond return None # discussions: http://www.nativetreesociety.org/measure/tdi/diameter_height_ratio.htm def validate_height_dbh(self): if not self.height or not self.dbh: return None getcontext().prec = 3 cbh = self.dbh * math.pi cbh_feet = cbh * .75 / 9 float_ratio = self.height / cbh_feet hd_ratio = Decimal(float_ratio.__str__()) #print hd_ratio if hd_ratio < 100: return None return round(hd_ratio, 2).__str__() def validate_max_dbh(self): if not self.dbh or not self.species or not self.species.v_max_dbh: return None if self.dbh > self.species.v_max_dbh: return "%s (species max: %s )" % (str( self.dbh), str(self.species.v_max_dbh)) return None def validate_max_height(self): if not self.height or not self.species or not self.species.v_max_height: return None if self.height > self.species.v_max_height: return "%s (species max: %s)" % (str( self.height), str(self.species.v_max_height)) return None def remove(self): """ Mark the tree and its associated objects as not present. """ self.present = False self.save() for audit_trail_record in self.history.all(): audit_trail_record.present = False audit_trail_record.save() def __unicode__(self): if self.species: return u'%s, %s, %s' % (self.species.common_name or '', self.species.scientific_name, self.plot.geocoded_address) else: return self.plot.geocoded_address
class Event(models.Model): """ Meeting or hearing associated with a proposal. """ title = models.CharField(max_length=256, db_index=True) created = models.DateTimeField(auto_now_add=True) date = models.DateTimeField(db_index=True) duration = models.DurationField(null=True) location = models.CharField( max_length=256, default="Somerville City Hall, 93 Highland Ave") region_name = models.CharField(max_length=128, default="Somerville, MA") description = models.TextField() proposals = models.ManyToManyField(Proposal, related_name="events", related_query_name="event") minutes = models.URLField(blank=True) objects = EventManager() class Meta: unique_together = (("date", "title", "region_name")) def to_json_dict(self): d = model_to_dict(self, exclude=["created", "proposals"]) return d @classmethod def make_event(cls, event_json): """ event_json should have the following fields: - title (str) - Name of the event - description (str) - date (datetime with local timezone) - When will the event occur? - cases - A list of case numbers - region_name - duration (timedelta, optional) - how long will the event last? - agenda_url (string, optional) """ keys = ("title", "description", "date", "region_name", "duration") try: event = cls.objects.get(title=event_json["title"], date=event_json["date"], region_name=event_json["region_name"]) for k in keys: setattr(event, k, event_json.get(k)) event.minutes = event_json.get("agenda_url") except cls.DoesNotExist: kwargs = {k: event_json.get(k) for k in keys} kwargs["minutes"] = event_json.get("agenda_url", "") event = cls(**kwargs) event.save() for case_number in event_json["cases"]: try: proposal = Proposal.objects.get(case_number=case_number) event.proposals.add(proposal) except Proposal.DoesNotExist: continue return event
class Map(NamedModel): """ A single thematical map. """ ANONYMOUS = 1 EDITORS = 2 OWNER = 3 PUBLIC = 1 OPEN = 2 PRIVATE = 3 EDIT_STATUS = ( (ANONYMOUS, _('Everyone can edit')), (EDITORS, _('Only editors can edit')), (OWNER, _('Only owner can edit')), ) SHARE_STATUS = ( (PUBLIC, _('everyone (public)')), (OPEN, _('anyone with link')), (PRIVATE, _('editors only')), ) slug = models.SlugField(db_index=True) description = models.TextField(blank=True, null=True, verbose_name=_("description")) center = models.PointField(geography=True, verbose_name=_("center")) zoom = models.IntegerField(default=7, verbose_name=_("zoom")) locate = models.BooleanField(default=False, verbose_name=_("locate"), help_text=_("Locate user on load?")) licence = models.ForeignKey(Licence, help_text=_("Choose the map licence."), verbose_name=_('licence'), on_delete=models.SET_DEFAULT, default=get_default_licence) modified_at = models.DateTimeField(auto_now=True) tilelayer = models.ForeignKey(TileLayer, blank=True, null=True, related_name="maps", verbose_name=_("background")) owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="owned_maps", verbose_name=_("owner")) editors = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, verbose_name=_("editors")) edit_status = models.SmallIntegerField(choices=EDIT_STATUS, default=OWNER, verbose_name=_("edit status")) share_status = models.SmallIntegerField(choices=SHARE_STATUS, default=PUBLIC, verbose_name=_("share status")) settings = DictField(blank=True, null=True, verbose_name=_("settings")) objects = models.GeoManager() public = PublicManager() def get_absolute_url(self): return reverse("map", kwargs={ 'slug': self.slug or "map", 'pk': self.pk }) def get_anonymous_edit_url(self): signer = Signer() signature = signer.sign(self.pk) return reverse('map_anonymous_edit_url', kwargs={'signature': signature}) def is_anonymous_owner(self, request): if self.owner: # edit cookies are only valid while map hasn't owner return False key, value = self.signed_cookie_elements try: has_anonymous_cookie = int(request.get_signed_cookie( key, False)) == value except ValueError: has_anonymous_cookie = False return has_anonymous_cookie def can_edit(self, user=None, request=None): """ Define if a user can edit or not the instance, according to his account or the request. """ can = False if request and not self.owner: if (getattr(settings, "LEAFLET_STORAGE_ALLOW_ANONYMOUS", False) and self.is_anonymous_owner(request)): can = True if user and user.is_authenticated(): # if user is authenticated, attach as owner self.owner = user self.save() msg = _( "Your anonymous map has been attached to your account %s" % user) messages.info(request, msg) if self.edit_status == self.ANONYMOUS: can = True elif not user.is_authenticated(): pass elif user == self.owner: can = True elif self.edit_status == self.EDITORS and user in self.editors.all(): can = True return can def can_view(self, request): if self.owner is None: can = True elif self.share_status in [self.PUBLIC, self.OPEN]: can = True elif request.user == self.owner: can = True else: can = not (self.share_status == self.PRIVATE and request.user not in self.editors.all()) return can @property def signed_cookie_elements(self): return ('anonymous_owner|%s' % self.pk, self.pk) def get_tilelayer(self): return self.tilelayer or TileLayer.get_default() def clone(self, **kwargs): new = self.__class__.objects.get(pk=self.pk) new.pk = None new.name = u"%s %s" % (_("Clone of"), self.name) if "owner" in kwargs: # can be None in case of anonymous cloning new.owner = kwargs["owner"] new.save() for editor in self.editors.all(): new.editors.add(editor) for datalayer in self.datalayer_set.all(): datalayer.clone(map_inst=new) return new
class Proposal(models.Model): case_number = models.CharField(max_length=64, unique=True, help_text=("The unique case number " "assigned by the city")) address = models.CharField(max_length=128, help_text="Street address") other_addresses = models.CharField( max_length=250, blank=True, help_text="Other addresses covered by this proposal") location = models.PointField(help_text="The latitude and longitude") region_name = models.CharField(max_length=128, default="Somerville, MA", null=True, help_text="") # The time when the proposal was last saved: modified = models.DateTimeField(auto_now=True) # The last time that the source was changed: updated = models.DateTimeField() created = models.DateTimeField(auto_now_add=True) summary = models.CharField(max_length=1024, default="") description = models.TextField(default="") source = models.URLField(null=True, help_text="The data source for the proposal.") status = models.CharField(max_length=64) # A proposal can be associated with a Project: project = models.ForeignKey("project.Project", blank=True, null=True) # A misnomer; if True, indicates that the planning board has issued a # ruling (approval or disapproval): complete = models.BooleanField(default=False) parcel = models.ForeignKey("parcel.Parcel", related_name="proposals", null=True, on_delete=models.SET_NULL) objects = ProposalManager() def get_absolute_url(self): return reverse("view-proposal", kwargs={"pk": self.pk}) def document_for_field(self, field): return self.document_set.filter(field=field) @classmethod def create_or_update_proposal_from_dict(kls, p_dict): """ Constructs a Proposal from a dictionary. If an existing proposal has a matching case number, update it from p_dict.""" try: proposal = kls.objects.get(case_number=p_dict["case_number"]) created = False except kls.DoesNotExist: proposal = kls(case_number=p_dict["case_number"]) created = True changed = not created if changed: prop_changes = [] for p, fn in property_map: old_val = changed and getattr(proposal, p) try: val = fn(p_dict) if changed and val != old_val: prop_changes.append({ "name": p, "new": val, "old": old_val }) setattr(proposal, p, val) except Exception as exc: if old_val: continue raise Exception("Missing required property: %s\n Reason: %s" % (p, exc)) proposal.save() proposal.create_events(p_dict.get("events")) # Create associated documents: proposal.create_documents( (field, val["links"]) for field, val in p_dict.items() if isinstance(val, dict) and val.get("links")) if changed: attr_changes = [] for attr_name, attr_val in p_dict.get("attributes", []): try: handle = utils.normalize(attr_name) attr = proposal.attributes.get(handle=handle) old_val = attr.text_value except Attribute.DoesNotExist: proposal.attributes.create(name=attr_name, handle=handle, text_value=attr_val, published=p_dict["updated_date"]) old_val = None if changed: attr_changes.append({ "name": attr_name, "old": old_val, "new": attr_val }) if changed: changeset = Changeset.from_changes(proposal, { "properties": prop_changes, "attributes": attr_changes }) changeset.save() return (created, proposal) def create_documents(self, docs): for field, links in docs: for link in links: try: doc = proposal.document_set.get(url=link["url"]) except Document.DoesNotExist: doc = Document(proposal=proposal) doc.url = link["url"] doc.title = link["title"] doc.field = field doc.published = p_dict["updated_date"] doc.save() def create_events(self, events_json): if events_json: for event_json in events_json: event_json["cases"] = [self.case_number] Event.make_event(event_json)
class Comment(models.Model): created_at = models.DateTimeField(verbose_name=_('Time of creation'), auto_now_add=True) created_by = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('Created by'), null=True, blank=True, related_name='%(class)s_created', on_delete=models.PROTECT) text = models.TextField(verbose_name=_('Text')) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=get_content_type_choices) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') objects = CommentQuerySet.as_manager() class Meta: ordering = ('id', ) verbose_name = _('Comment') verbose_name_plural = _('Comments') index_together = (('content_type', 'object_id'), ) def __str__(self): author = self.created_by.get_display_name( ) if self.created_by else 'Unknown author' text = self.text if len(self.text) < 40 else self.text[:37] + '...' return '%s %s %s: %s' % (self.content_type.model, self.object_id, author, text) @staticmethod def can_user_comment_object(user, target_object): if not (user and user.is_authenticated): return False target_model = target_object.__class__ if target_model not in COMMENTABLE_MODELS.values(): return False if target_model == Reservation: if user == target_object.user: return True if target_object.resource.can_access_reservation_comments(user): return True elif target_model == CateringOrder: if user == target_object.reservation.user: return True if target_object.reservation.resource.can_view_reservation_catering_orders( user): return True return False def get_notification_context(self, language_code): target_object = self.content_object target_model = target_object.__class__ assert target_model in COMMENTABLE_MODELS.values() target_type = next(api_name for api_name, model in COMMENTABLE_MODELS.items() if model == target_model) context = dict(text=self.text, target_type=target_type, created_at=self.created_at) if self.created_by: context['created_by'] = dict( display_name=self.created_by.get_display_name()) else: context['created_by'] = None if target_model == Reservation: context['reservation'] = target_object.get_notification_context( language_code) tz = target_object.resource.unit.get_tz() elif target_model == CateringOrder: context['catering_order'] = target_object.get_notification_context( language_code) tz = target_object.reservation.resource.unit.get_tz() # Use local timezones by default context['created_at'] = context['created_at'].astimezone(tz) return context def _send_notification(self, request=None): target_object = self.content_object target_model = target_object.__class__ assert target_model in COMMENTABLE_MODELS.values() if target_model == CateringOrder: catering_provider = target_object.get_provider() email = catering_provider.notification_email if catering_provider else None reserver = target_object.reservation.user notification_type = NotificationType.CATERING_ORDER_COMMENT_CREATED elif target_model == Reservation: unit = target_object.resource.unit email = unit.manager_email if unit.manager_email else None reserver = target_object.user notification_type = NotificationType.RESERVATION_COMMENT_CREATED context = self.get_notification_context(DEFAULT_LANG) try: rendered_notification = render_notification_template( notification_type, context, DEFAULT_LANG) except NotificationTemplateException as e: logger.error(e, exc_info=True, extra={'request': request}) return if email: send_respa_mail(email, rendered_notification['subject'], rendered_notification['body'], rendered_notification['html_body']) if self.created_by != reserver and reserver.email: send_respa_mail(reserver.email, rendered_notification['subject'], rendered_notification['body'], rendered_notification['html_body']) def send_created_notification(self, request=None): self._send_notification(request)
class DatedModel(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: abstract = True
class Lot(models.Model): # spatial fields coord = models.PointField(srid=4326, blank=True) bounds = models.MultiPolygonField(srid=4326, blank=True) #area = models.FloatField(db_index=True, blank=True) address = models.CharField(db_index=True, max_length=255, blank=True) city = models.CharField(max_length=255) state = models.CharField(max_length=2) country = models.CharField(max_length=255) code = models.CharField(max_length=10, blank=True) # meta fields is_visible = models.BooleanField(db_index=True, default=True) is_vacant = models.BooleanField(db_index=True, default=False) is_public = models.BooleanField(db_index=True, default=False) #auto-generated fields slug = models.SlugField(max_length=255, editable=False) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) comments = generic.GenericRelation(Comment) objects = PassThroughGeoManager.for_queryset_class(LotQuerySet)() class Meta: pass @property def activity_count(self): activity = self.comments.count() + self.ideas.count() return activity @property def has_project(self): if self.ideas: return True else: return False @property def has_comment(self): if self.comments: return True else: return False def get_sqft(self): """ Returns the area in sq ft. """ # Convert our geographic polygons (in WGS84) # into a local projection for New York (here EPSG:32118) try: return self.bounds.transform(102729, clone=True).area except Exception: return None def get_acres(self): """ Returns the area in sq ft. """ # Convert our geographic polygons (in WGS84) # into a local projection for New York (here EPSG:32118) try: return self.bounds.transform(102729, clone=True).area * 0.00002295684 except Exception: return None def __unicode__(self): if self.address: return u'%s' % (self.address) elif self.coord: return u'(%s,%s)' % (self.coord.x, self.coord.y) else: return u'No Address %s' % (self.pk) def get_absolute_url(self): return reverse('lotxlot_lot_detail', args=[str(self.id)]) def save(self, *args, **kwargs): if not self.pk: if self.address: slug = '%s %s, %s' % (self.address, self.city, self.state) elif self.coord: slug = '(%s,%s) %s, %s' % (self.coord.x, self.coord.y, self.city, self.state) else: slug = 'No Address %s %s, %s' % (self.pk, self.city, self.state) self.slug = slugify(slug) super(Lot, self).save(*args, **kwargs)
class SiteVisit(AbstractAdditionalData): """Site visit model.""" EMBEDDEDNESS_CHOICES = ((EMBEDDEDNESS_1, '0-25'), (EMBEDDEDNESS_2, '26-50'), (EMBEDDEDNESS_3, '51-75'), (EMBEDDEDNESS_4, '76-100')) location_site = models.ForeignKey(LocationSite, on_delete=models.CASCADE, default=None) site_visit_date = models.DateField(default=timezone.now) time = models.DateTimeField(null=True, blank=True) owner = models.ForeignKey( settings.AUTH_USER_MODEL, help_text='Creator/owner of this data from the web', null=True, blank=True, related_name='%(class)s_owner', on_delete=models.SET_NULL) collector = models.ForeignKey( settings.AUTH_USER_MODEL, help_text='Actual capturer/collector of this data', null=True, blank=True, related_name='%(class)s_data_collector', on_delete=models.SET_NULL) water_level = models.CharField(max_length=100, choices=[(status.name, status.value[WATER_LEVEL_NAME]) for status in WaterLevel], blank=True, null=True) channel_type = models.CharField(max_length=200, choices=[(status.name, status.value[CHANNEL_TYPE_NAME]) for status in ChannelType], blank=True, null=True) water_turbidity = models.CharField(max_length=100, choices=[(status.name, status.value) for status in WaterTurbidity], blank=True, null=True) canopy_cover = models.CharField(max_length=100, choices=[(status.name, status.value) for status in CanopyCover], blank=True, null=True) embeddedness = models.CharField(max_length=50, choices=EMBEDDEDNESS_CHOICES, blank=True, default='') average_velocity = models.IntegerField(null=True, blank=True) average_depth = models.IntegerField(null=True, blank=True) discharge = models.IntegerField(null=True, blank=True) sass_version = models.IntegerField(null=True, blank=True) sass_biotope_fraction = models.ManyToManyField('sass.SassBiotopeFraction', blank=True) other_biota = models.TextField(null=True, blank=True) comments_or_observations = models.TextField(null=True, blank=True) data_source = models.ForeignKey('bims.DataSource', null=True, blank=True, on_delete=models.SET_NULL) def __unicode__(self): return self.location_site.name
class Unit(models.Model): id = models.IntegerField(primary_key=True) data_source_url = models.URLField(null=True) name = models.CharField(max_length=200, db_index=True) description = models.TextField(null=True) provider_type = models.IntegerField() location = models.PointField(null=True, srid=PROJECTION_SRID) geometry = models.GeometryField(srid=PROJECTION_SRID, null=True) department = models.ForeignKey(Department, null=True) organization = models.ForeignKey(Organization) street_address = models.CharField(max_length=100, null=True) address_zip = models.CharField(max_length=10, null=True) phone = models.CharField(max_length=50, null=True) email = models.EmailField(max_length=100, null=True) www_url = models.URLField(max_length=400, null=True) address_postal_full = models.CharField(max_length=100, null=True) municipality = models.ForeignKey(Municipality, null=True, db_index=True) data_source = models.CharField(max_length=20, null=True) extensions = HStoreField(null=True) picture_url = models.URLField(max_length=250, null=True) picture_caption = models.CharField(max_length=200, null=True) origin_last_modified_time = models.DateTimeField( db_index=True, help_text='Time of last modification') services = models.ManyToManyField(Service, related_name='units') divisions = models.ManyToManyField(AdministrativeDivision) keywords = models.ManyToManyField(Keyword) connection_hash = models.CharField( max_length=40, null=True, help_text='Automatically generated hash of connection info') accessibility_property_hash = models.CharField( max_length=40, null=True, help_text='Automatically generated hash of accessibility property info' ) identifier_hash = models.CharField( max_length=40, null=True, help_text='Automatically generated hash of other identifiers') # Cached fields for better performance root_services = models.CommaSeparatedIntegerField(max_length=50, null=True) objects = models.GeoManager() search_objects = UnitSearchManager() def __str__(self): return "%s (%s)" % (get_translated(self, 'name'), self.id) def get_root_services(self): tree_ids = self.services.all().values_list('tree_id', flat=True).distinct() qs = Service.objects.filter(level=0).filter(tree_id__in=list(tree_ids)) srv_list = qs.values_list('id', flat=True).distinct() return sorted(srv_list)
class Event(CommonAbstractModel): STATUS_CHOICES = ( ('ACTIVE', _('Active')), ('INACTIVE', _('Inactive')), ) disaster = models.ForeignKey(Disaster, verbose_name=verbose_disaster) point = models.PointField( null=True, blank=True, verbose_name=verbose_point ) created = models.DateTimeField( default=timezone.now, verbose_name=verbose_created ) updated = models.DateTimeField(auto_now=True, verbose_name=verbose_updated) closed = models.DateTimeField( null=True, blank=True, verbose_name=verbose_closed ) status = models.CharField( max_length=50, choices=STATUS_CHOICES, default='ACTIVE', verbose_name=verbose_status ) note = models.TextField(blank=True, verbose_name=verbose_note) height = models.PositiveIntegerField( null=True, blank=True, verbose_name=verbose_height, help_text=_('Unit in cm') ) height_min = models.PositiveIntegerField( null=True, blank=True, verbose_name=verbose_height_min, help_text=_('Unit in cm') ) magnitude = models.DecimalField( null=True, blank=True, max_digits=4, decimal_places=2, validators=[MinValueValidator(Decimal('0.01'))], verbose_name=verbose_magnitude, help_text=_('Unit in Scala Richter') ) depth = models.DecimalField( null=True, blank=True, max_digits=4, decimal_places=2, validators=[MinValueValidator(Decimal('0.01'))], verbose_name=verbose_depth, help_text=_('Unit in km') ) province = models.ForeignKey( Province, null=True, blank=True, verbose_name=verbose_province ) city = models.ForeignKey( City, null=True, blank=True, verbose_name=verbose_city ) subdistrict = models.ForeignKey( Subdistrict, null=True, blank=True, verbose_name=verbose_subdistrict ) village = models.ForeignKey( Village, null=True, blank=True, verbose_name=verbose_village ) rw = models.ForeignKey(RW, null=True, blank=True, verbose_name=verbose_rw) rt = models.ForeignKey(RT, null=True, blank=True, verbose_name=verbose_rt) class Meta: ordering = ['-pk'] get_latest_by = 'pk' verbose_name_plural = _('Events') def get_absolute_url(self): return reverse('reports:event_detail', args=[self.pk]) def __unicode__(self): return '[%s] - %s' % (self.disaster, timezone.localtime(self.created).strftime('%Y-%m-%d %H:%M:%S %Z'))
class HowHearAboutUsItem(models.Model): ''' METADATA ''' class Meta: app_label = 'tenant_foundation' db_table = 'nwapp_how_did_you_hear_about_us_items' verbose_name = _('How Hear About Us Item') verbose_name_plural = _('How Hear About Us Items') default_permissions = () permissions = () ''' MODEL FIELDS ''' objects = HowHearAboutUsItemManager() """ MODEL FUNCTIONS """ text = models.CharField(_("Text"), max_length=127, help_text=_('The text content of this item.'), db_index=True, unique=True) sort_number = models.PositiveSmallIntegerField( _("Sort #"), help_text=_('The number this item will appear when sorted by number.'), blank=True, default=0, db_index=True, ) is_for_associate = models.BooleanField( _("Is for associate"), help_text=_( 'Indicates this option will be visible for the associate.'), default=True, blank=True) is_for_customer = models.BooleanField( _("Is for customer"), help_text=_('Indicates this option will be visible for the customer.'), default=True, blank=True) is_for_staff = models.BooleanField( _("Is for staff"), help_text=_('Indicates this option will be visible for the staff.'), default=True, blank=True) is_for_partner = models.BooleanField( _("Is for partner"), help_text=_('Indicates this option will be visible for the partner.'), default=True, blank=True) is_archived = models.BooleanField( _("Is Archived"), help_text=_('Indicates whether how hear item was archived.'), default=False, blank=True, db_index=True) # AUDITING uuid = models.CharField( _("UUID"), help_text= _('The unique identifier we want to release to the public to identify this unique record.' ), default=uuid.uuid4, editable=False, max_length=63, # Do not change unique=True, db_index=True, ) slug = models.SlugField( _("Slug"), help_text=_('The unique identifier used externally.'), max_length=255, null=False, unique=True, db_index=True, ) created_at = models.DateTimeField(auto_now_add=True, db_index=True) created_by = models.ForeignKey( SharedUser, help_text=_('The user whom created this score point.'), related_name="created_how_did_you_hear_about_us_items", on_delete=models.SET_NULL, blank=True, null=True) created_from = models.GenericIPAddressField( _("Created from"), help_text=_('The IP address of the creator.'), blank=True, null=True) created_from_is_public = models.BooleanField( _("Is the IP "), help_text=_('Is creator a public IP and is routable.'), default=False, blank=True) created_from_position = models.PointField( _("Created from position"), help_text=_('The latitude and longitude coordinates for the creator.'), srid=4326, geography=True, null=True, blank=True, ) last_modified_at = models.DateTimeField(auto_now=True) last_modified_by = models.ForeignKey( SharedUser, help_text=_('The user whom last modified this private image upload.'), related_name="last_modified_how_did_you_hear_about_us_items", on_delete=models.SET_NULL, blank=True, null=True) last_modified_from = models.GenericIPAddressField( _("Last modified from"), help_text=_('The IP address of the modifier.'), blank=True, null=True) last_modified_from_is_public = models.BooleanField( _("Is the IP "), help_text=_('Is modifier a public IP and is routable.'), default=False, blank=True) last_modified_from_position = models.PointField( _("Last modified from position"), help_text=_( 'The latitude and longitude coordinates for the last modified user.' ), srid=4326, geography=True, null=True, blank=True, ) """ MODEL FUNCTIONS """ def __str__(self): return str(self.text) @transaction.atomic def save(self, *args, **kwargs): ''' Override the `save` function to support extra functionality of our model. ''' ''' If we are creating a new row, then we will automatically increment the `id` field instead of relying on Postgres DB. ''' if self.id == None: try: latest_obj = HowHearAboutUsItem.objects.latest('id') self.id = latest_obj.id + 1 except HowHearAboutUsItem.DoesNotExist: self.id = 1 ''' If we are creating a new model, then we will automatically increment the `id`. ''' # The following code will generate a unique slug and if the slug # is not unique in the database, then continue to try generating # a unique slug until it is found. if self.slug == "" or self.slug == None: slug = slugify(self.text) while HowHearAboutUsItem.objects.filter(slug=slug).exists(): slug = slugify(self.text) + "-" + get_referral_code(16) self.slug = slug ''' Finally call the parent function which handles saving so we can carry out the saving operation by Django in our ORM. ''' super(HowHearAboutUsItem, self).save(*args, **kwargs)
class Event(SimpleModel): name = models.CharField(max_length=100) when = models.DateTimeField()
class Instance(models.Model, InstanceBaseClass): """ Model representing a single submission to an XForm """ json = JSONField(default=dict, null=False) xml = models.TextField() user = models.ForeignKey( User, related_name='instances', null=True, on_delete=models.SET_NULL) xform = models.ForeignKey( 'logger.XForm', null=False, related_name='instances', on_delete=models.CASCADE) survey_type = models.ForeignKey( 'logger.SurveyType', on_delete=models.PROTECT) # shows when we first received this instance date_created = models.DateTimeField(auto_now_add=True) # this will end up representing "date last parsed" date_modified = models.DateTimeField(auto_now=True) # this will end up representing "date instance was deleted" deleted_at = models.DateTimeField(null=True, default=None) deleted_by = models.ForeignKey(User, related_name='deleted_instances', null=True, on_delete=models.SET_NULL) # this will be edited when we need to create a new InstanceHistory object last_edited = models.DateTimeField(null=True, default=None) # ODK keeps track of three statuses for an instance: # incomplete, submitted, complete # we add a fourth status: submitted_via_web status = models.CharField(max_length=20, default=u'submitted_via_web') uuid = models.CharField(max_length=249, default=u'', db_index=True) version = models.CharField(max_length=XFORM_TITLE_LENGTH, null=True) # store a geographic objects associated with this instance geom = models.GeometryCollectionField(null=True) # Keep track of whether all media attachments have been received media_all_received = models.NullBooleanField( _("Received All Media Attachemts"), null=True, default=True) total_media = models.PositiveIntegerField(_("Total Media Attachments"), null=True, default=0) media_count = models.PositiveIntegerField(_("Received Media Attachments"), null=True, default=0) checksum = models.CharField(max_length=64, null=True, blank=True, db_index=True) # Keep track of submission reviews, only query reviews if true has_a_review = models.BooleanField(_("has_a_review"), default=False) tags = TaggableManager() class Meta: app_label = 'logger' unique_together = ('xform', 'uuid') @classmethod def set_deleted_at(cls, instance_id, deleted_at=timezone.now(), user=None): try: instance = cls.objects.get(id=instance_id) except cls.DoesNotExist: pass else: instance.set_deleted(deleted_at, user) def _check_active(self, force): """Check that form is active and raise exception if not. :param force: Ignore restrictions on saving. """ # pylint: disable=no-member if not force and self.xform and not self.xform.downloadable: raise FormInactiveError() def _check_is_merged_dataset(self): """Check for merged datasets. Raises an exception to prevent datasubmissions """ # pylint: disable=no-member if self.xform and self.xform.is_merged_dataset: raise FormIsMergedDatasetError() def get_expected_media(self): """ Returns a list of expected media files from the submission data. """ if not hasattr(self, '_expected_media'): # pylint: disable=no-member data = self.get_dict() media_list = [] if 'encryptedXmlFile' in data and self.xform.encrypted: media_list.append(data['encryptedXmlFile']) if 'media' in data: # pylint: disable=no-member media_list.extend([i['media/file'] for i in data['media']]) else: media_xpaths = (self.xform.get_media_survey_xpaths() + self.xform.get_osm_survey_xpaths()) for media_xpath in media_xpaths: media_list.extend( get_values_matching_key(data, media_xpath)) # pylint: disable=attribute-defined-outside-init self._expected_media = list(set(media_list)) return self._expected_media @property def num_of_media(self): """ Returns number of media attachments expected in the submission. """ if not hasattr(self, '_num_of_media'): # pylint: disable=attribute-defined-outside-init self._num_of_media = len(self.get_expected_media()) return self._num_of_media @property def attachments_count(self): return self.attachments.filter( name__in=self.get_expected_media() ).distinct('name').order_by('name').count() def save(self, *args, **kwargs): force = kwargs.get('force') if force: del kwargs['force'] self._check_is_merged_dataset() self._check_active(force) self._set_geom() self._set_json() self._set_survey_type() self._set_uuid() # pylint: disable=no-member self.version = self.json.get(VERSION, self.xform.version) super(Instance, self).save(*args, **kwargs) # pylint: disable=no-member def set_deleted(self, deleted_at=timezone.now(), user=None): if user: self.deleted_by = user self.deleted_at = deleted_at self.save() # force submission count re-calculation self.xform.submission_count(force_update=True) self.parsed_instance.save()
class Occurrence(models.Model): #id = models.AutoField("id",primary_key=True,db_column="id",null=False,blank=True) barcode = models.IntegerField("Barcode", null=True, blank=True) datelastmodified = models.DateTimeField("Date Last Modified", null=True, blank=True) basisofrecord = models.CharField("Basis of Record", null=True, blank=True, max_length=50, choices=basisCHOICES) itemtype = models.CharField("Item Type", null=True, blank=True, max_length=255, choices=itemtypeCHOICES) institutionalcode = models.CharField("Institution", null=True, blank=True, max_length=20) collectioncode = models.CharField("Coll Code", null=True, blank=True, max_length=20, choices=(("DIK", "DIK"), ("ASB", "ASB"))) paleolocalitynumber = models.IntegerField("Locality #", null=True, blank=True) paleosublocality = models.CharField("Sublocality", null=True, blank=True, max_length=50) itemnumber = models.IntegerField("Item #", null=True, blank=True, max_length=50) itempart = models.CharField("Item Part", null=True, blank=True, max_length=10) catalognumber = models.CharField("Catalog Number", null=True, blank=True, max_length=255) remarks = models.TextField("Remarks", null=True, blank=True, max_length=2500) itemscientificname = models.CharField("Scientific Name", null=True, blank=True, max_length=255) itemdescription = models.CharField("Description", null=True, blank=True, max_length=255) continent = models.CharField(null=True, blank=True, max_length=50) country = models.CharField(null=True, blank=True, max_length=50) stateprovince = models.CharField(null=True, blank=True, max_length=50) locality_text = models.CharField(null=True, blank=True, max_length=255, db_column="locality") verbatimcoordinates = models.CharField(null=True, blank=True, max_length=50) verbatimcoordinatesystem = models.CharField(null=True, blank=True, max_length=50) georeferenceremarks = models.CharField(null=True, blank=True, max_length=50) utmzone = models.IntegerField(null=True, blank=True) utmeast = models.FloatField(null=True, blank=True) utmnorth = models.FloatField(null=True, blank=True) geodeticdatum = models.CharField(null=True, blank=True, max_length=20) collectingmethod = models.CharField("Collection Method", null=True, blank=True, max_length=50) relatedcatalogitems = models.CharField(null=True, blank=True, max_length=50) earliestdatecollected = models.DateTimeField(null=True, blank=True) dayofyear = models.IntegerField(null=True, blank=True) collector = models.CharField(null=True, blank=True, max_length=50) finder = models.CharField(null=True, blank=True, max_length=50) disposition = models.CharField(null=True, blank=True, max_length=255) collectionremarks = models.CharField("Remarks", null=True, blank=True, max_length=255) fieldnumber = models.DateTimeField("Field Number", null=True, blank=True) monthcollected = models.CharField(null=True, blank=True, max_length=20) yearcollected = models.IntegerField("Year", null=True, blank=True) individualcount = models.IntegerField(null=True, blank=True) preparationstatus = models.CharField(null=True, blank=True, max_length=50) strat_upper = models.CharField(null=True, blank=True, max_length=255, db_column="stratigraphicmarkerupper") distancefromupper = models.FloatField("Distance Upper", null=True, blank=True) strat_lower = models.CharField(null=True, blank=True, max_length=255, db_column="stratigraphicmarkerlower") distancefromlower = models.FloatField("Distance Lower", null=True, blank=True) strat_found = models.CharField(null=True, blank=True, max_length=255, db_column="stratigraphicmarkerfound") distancefromfound = models.FloatField("Distance Found", null=True, blank=True) strat_likely = models.CharField(null=True, blank=True, max_length=255, db_column="stratigraphicmarkerlikely") distancefromlikely = models.FloatField("Distance Likely", null=True, blank=True) stratigraphicmember = models.CharField("Member", null=True, blank=True, max_length=255) analyticalunit = models.CharField("Submember", null=True, blank=True, max_length=255) analyticalunit2 = models.CharField("Submember2", null=True, blank=True, max_length=255) analyticalunit3 = models.CharField("Submember3", null=True, blank=True, max_length=255) insitu = models.IntegerField("In Situ?", null=True, blank=True) ranked = models.IntegerField("Ranked?", null=True, blank=True) imageurl = models.CharField(null=True, blank=True, max_length=255) relatedinformation = models.CharField(null=True, blank=True, max_length=50) #localityid = models.IntegerField(null=True, blank=True) stratigraphicsection = models.CharField(null=True, blank=True, max_length=50) stratigraphicheightinmeters = models.FloatField(null=True, blank=True) weathering = models.IntegerField(null=True, blank=True) surfacemodification = models.CharField(null=True, blank=True, max_length=255) #point_x = models.FloatField(null=True, blank=True) #point_y = models.FloatField(null=True, blank=True) problem = models.IntegerField(null=True, blank=True, max_length=5) problemcomment = models.CharField(null=True, blank=True, max_length=255) dgupdate2013 = models.IntegerField(null=True, blank=True) dgupdatex = models.FloatField(null=True, blank=True) dgupdatey = models.FloatField(null=True, blank=True) geom = models.PointField(srid=4326) objects = models.GeoManager() locality = models.ForeignKey(Locality) @staticmethod def fields_to_display(): fields = ("id", "barcode") return fields def point_X(self): return self.geom.x def point_Y(self): return self.geom.y def __unicode__(self): niceName = str(self.collectioncode) + "-" + str( self.paleolocalitynumber) + "-" + str(self.itemnumber) + str( self.itempart) + " [" + str( self.itemscientificname) + " " + str( self.itemdescription) + "]" return niceName.replace("None", "").replace("--", "") def save(self, *args, **kwargs): #custom save method for occurrence theCatalogNumber = str(self.collectioncode) + "-" + str( self.paleolocalitynumber) + str(self.paleosublocality) + "-" + str( self.itemnumber) + str(self.itempart) self.catalognumber = theCatalogNumber.replace("None", "") self.datelastmodified = datetime.now( ) # TODO change datelastmodified autonow option to accomplish this #call the normal drp_occurrence save method using alternate database super(Occurrence, self).save(*args, **kwargs) class Meta: verbose_name = "DRP Occurrence" verbose_name_plural = "DRP Occurrences" # The DRP database is in the SDE standard in order to make it compatible with # ArcGIS 10.1. Django does not handle PostGIS DB schemas natively. This is a # work-around to point Django to the right location for the data tables. #db_table='drp_occurrence' ordering = [ "collectioncode", "paleolocalitynumber", "itemnumber", "itempart" ]
class InstanceHistory(models.Model, InstanceBaseClass): class Meta: app_label = 'logger' xform_instance = models.ForeignKey( Instance, related_name='submission_history', on_delete=models.CASCADE) user = models.ForeignKey(User, null=True, on_delete=models.CASCADE) xml = models.TextField() # old instance id uuid = models.CharField(max_length=249, default=u'') date_created = models.DateTimeField(auto_now_add=True) date_modified = models.DateTimeField(auto_now=True) submission_date = models.DateTimeField(null=True, default=None) geom = models.GeometryCollectionField(null=True) checksum = models.CharField(max_length=64, null=True, blank=True) @property def xform(self): return self.xform_instance.xform @property def attachments(self): return self.xform_instance.attachments.all() @property def json(self): return self.get_full_dict(load_existing=False) @property def status(self): return self.xform_instance.status @property def tags(self): return self.xform_instance.tags @property def notes(self): return self.xform_instance.notes.all() @property def reviews(self): return self.xform_instance.reviews.all() @property def version(self): return self.xform_instance.version @property def osm_data(self): return self.xform_instance.osm_data @property def deleted_at(self): return None @property def total_media(self): return self.xform_instance.total_media @property def has_a_review(self): return self.xform_instance.has_a_review @property def media_count(self): return self.xform_instance.media_count @property def media_all_received(self): return self.xform_instance.media_all_received def _set_parser(self): if not hasattr(self, "_parser"): self._parser = XFormInstanceParser( self.xml, self.xform_instance.xform ) @classmethod def set_deleted_at(cls, instance_id, deleted_at=timezone.now()): return None
class Reservation(ModifiableModel): CREATED = 'created' CANCELLED = 'cancelled' CONFIRMED = 'confirmed' DENIED = 'denied' REQUESTED = 'requested' WAITING_FOR_PAYMENT = 'waiting_for_payment' STATE_CHOICES = ( (CREATED, _('created')), (CANCELLED, _('cancelled')), (CONFIRMED, _('confirmed')), (DENIED, _('denied')), (REQUESTED, _('requested')), (WAITING_FOR_PAYMENT, _('waiting for payment')), ) TYPE_NORMAL = 'normal' TYPE_BLOCKED = 'blocked' TYPE_CHOICES = ( (TYPE_NORMAL, _('Normal reservation')), (TYPE_BLOCKED, _('Resource blocked')), ) resource = models.ForeignKey('Resource', verbose_name=_('Resource'), db_index=True, related_name='reservations', on_delete=models.PROTECT) begin = models.DateTimeField(verbose_name=_('Begin time')) end = models.DateTimeField(verbose_name=_('End time')) duration = pgfields.DateTimeRangeField( verbose_name=_('Length of reservation'), null=True, blank=True, db_index=True) comments = models.TextField(null=True, blank=True, verbose_name=_('Comments')) user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('User'), null=True, blank=True, db_index=True, on_delete=models.PROTECT) state = models.CharField(max_length=32, choices=STATE_CHOICES, verbose_name=_('State'), default=CREATED) approver = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_('Approver'), related_name='approved_reservations', null=True, blank=True, on_delete=models.SET_NULL) staff_event = models.BooleanField(verbose_name=_('Is staff event'), default=False) type = models.CharField(blank=False, verbose_name=_('Type'), max_length=32, choices=TYPE_CHOICES, default=TYPE_NORMAL) # access-related fields access_code = models.CharField(verbose_name=_('Access code'), max_length=32, null=True, blank=True) # EXTRA FIELDS START HERE event_subject = models.CharField(max_length=200, verbose_name=_('Event subject'), blank=True) event_description = models.TextField(verbose_name=_('Event description'), blank=True) number_of_participants = models.PositiveSmallIntegerField( verbose_name=_('Number of participants'), blank=True, null=True) participants = models.TextField(verbose_name=_('Participants'), blank=True) host_name = models.CharField(verbose_name=_('Host name'), max_length=100, blank=True) reservation_extra_questions = models.TextField( verbose_name=_('Reservation extra questions'), blank=True) reserver_name = models.CharField(verbose_name=_('Reserver name'), max_length=100, blank=True) reserver_id = models.CharField( verbose_name=_('Reserver ID (business or person)'), max_length=30, blank=True) reserver_email_address = models.EmailField( verbose_name=_('Reserver email address'), blank=True) reserver_phone_number = models.CharField( verbose_name=_('Reserver phone number'), max_length=30, blank=True) reserver_address_street = models.CharField( verbose_name=_('Reserver address street'), max_length=100, blank=True) reserver_address_zip = models.CharField( verbose_name=_('Reserver address zip'), max_length=30, blank=True) reserver_address_city = models.CharField( verbose_name=_('Reserver address city'), max_length=100, blank=True) company = models.CharField(verbose_name=_('Company'), max_length=100, blank=True) billing_first_name = models.CharField(verbose_name=_('Billing first name'), max_length=100, blank=True) billing_last_name = models.CharField(verbose_name=_('Billing last name'), max_length=100, blank=True) billing_email_address = models.EmailField( verbose_name=_('Billing email address'), blank=True) billing_phone_number = models.CharField( verbose_name=_('Billing phone number'), max_length=30, blank=True) billing_address_street = models.CharField( verbose_name=_('Billing address street'), max_length=100, blank=True) billing_address_zip = models.CharField( verbose_name=_('Billing address zip'), max_length=30, blank=True) billing_address_city = models.CharField( verbose_name=_('Billing address city'), max_length=100, blank=True) # If the reservation was imported from another system, you can store the original ID in the field below. origin_id = models.CharField(verbose_name=_('Original ID'), max_length=50, editable=False, null=True) objects = ReservationQuerySet.as_manager() class Meta: verbose_name = _("reservation") verbose_name_plural = _("reservations") ordering = ('id', ) def _save_dt(self, attr, dt): """ Any DateTime object is converted to UTC time zone aware DateTime before save If there is no time zone on the object, resource's time zone will be assumed through its unit's time zone """ save_dt(self, attr, dt, self.resource.unit.time_zone) def _get_dt(self, attr, tz): return get_dt(self, attr, tz) @property def begin_tz(self): return self.begin @begin_tz.setter def begin_tz(self, dt): self._save_dt('begin', dt) def get_begin_tz(self, tz): return self._get_dt("begin", tz) @property def end_tz(self): return self.end @end_tz.setter def end_tz(self, dt): """ Any DateTime object is converted to UTC time zone aware DateTime before save If there is no time zone on the object, resource's time zone will be assumed through its unit's time zone """ self._save_dt('end', dt) def get_end_tz(self, tz): return self._get_dt("end", tz) def is_active(self): return self.end >= timezone.now() and self.state not in ( Reservation.CANCELLED, Reservation.DENIED) def is_own(self, user): if not (user and user.is_authenticated): return False return user == self.user def need_manual_confirmation(self): return self.resource.need_manual_confirmation def are_extra_fields_visible(self, user): # the following logic is used also implemented in ReservationQuerySet # so if this is changed that probably needs to be changed as well if self.is_own(user): return True return self.resource.can_view_reservation_extra_fields(user) def can_view_access_code(self, user): if self.is_own(user): return True return self.resource.can_view_access_codes(user) def set_state(self, new_state, user): # Make sure it is a known state assert new_state in (Reservation.REQUESTED, Reservation.CONFIRMED, Reservation.DENIED, Reservation.CANCELLED, Reservation.WAITING_FOR_PAYMENT) old_state = self.state if new_state == old_state: if old_state == Reservation.CONFIRMED: reservation_modified.send(sender=self.__class__, instance=self, user=user) return if new_state == Reservation.CONFIRMED: self.approver = user reservation_confirmed.send(sender=self.__class__, instance=self, user=user) elif old_state == Reservation.CONFIRMED: self.approver = None user_is_staff = self.user is not None and self.user.is_staff # Notifications if new_state == Reservation.REQUESTED: self.send_reservation_requested_mail() self.send_reservation_requested_mail_to_officials() elif new_state == Reservation.CONFIRMED: if self.need_manual_confirmation(): self.send_reservation_confirmed_mail() elif self.access_code: self.send_reservation_created_with_access_code_mail() else: if not user_is_staff: # notifications are not sent from staff created reservations to avoid spam self.send_reservation_created_mail() elif new_state == Reservation.DENIED: self.send_reservation_denied_mail() elif new_state == Reservation.CANCELLED: order = self.get_order() if order: if order.state == order.CANCELLED: self.send_reservation_cancelled_mail() else: if user != self.user: self.send_reservation_cancelled_mail() reservation_cancelled.send(sender=self.__class__, instance=self, user=user) self.state = new_state self.save() def can_modify(self, user): if not user: return False if self.state == Reservation.WAITING_FOR_PAYMENT: return False if self.get_order(): return self.resource.can_modify_paid_reservations(user) # reservations that need manual confirmation and are confirmed cannot be # modified or cancelled without reservation approve permission cannot_approve = not self.resource.can_approve_reservations(user) if self.need_manual_confirmation( ) and self.state == Reservation.CONFIRMED and cannot_approve: return False return self.user == user or self.resource.can_modify_reservations(user) def can_add_comment(self, user): if self.is_own(user): return True return self.resource.can_access_reservation_comments(user) def can_view_field(self, user, field): if field not in RESERVATION_EXTRA_FIELDS: return True if self.is_own(user): return True return self.resource.can_view_reservation_extra_fields(user) def can_view_catering_orders(self, user): if self.is_own(user): return True return self.resource.can_view_catering_orders(user) def can_add_product_order(self, user): return self.is_own(user) def can_view_product_orders(self, user): if self.is_own(user): return True return self.resource.can_view_product_orders(user) def get_order(self): return getattr(self, 'order', None) def format_time(self): tz = self.resource.unit.get_tz() begin = self.begin.astimezone(tz) end = self.end.astimezone(tz) return format_dt_range(translation.get_language(), begin, end) def __str__(self): if self.state != Reservation.CONFIRMED: state_str = ' (%s)' % self.state else: state_str = '' return "%s: %s%s" % (self.format_time(), self.resource, state_str) def clean(self, **kwargs): """ Check restrictions that are common to all reservations. If this reservation isn't yet saved and it will modify an existing reservation, the original reservation need to be provided in kwargs as 'original_reservation', so that it can be excluded when checking if the resource is available. """ if self.end <= self.begin: raise ValidationError( _("You must end the reservation after it has begun")) # Check that begin and end times are on valid time slots. opening_hours = self.resource.get_opening_hours( self.begin.date(), self.end.date()) for dt in (self.begin, self.end): days = opening_hours.get(dt.date(), []) day = next((day for day in days if day['opens'] is not None and day['opens'] <= dt <= day['closes']), None) if day and not is_valid_time_slot(dt, self.resource.slot_size, day['opens']): raise ValidationError( _("Begin and end time must match time slots"), code='invalid_time_slot') original_reservation = self if self.pk else kwargs.get( 'original_reservation', None) if self.resource.check_reservation_collision(self.begin, self.end, original_reservation): raise ValidationError( _("The resource is already reserved for some of the period")) if (self.end - self.begin) < self.resource.min_period: raise ValidationError( _("The minimum reservation length is %(min_period)s") % {'min_period': humanize_duration(self.resource.min_period)}) if self.access_code: validate_access_code(self.access_code, self.resource.access_code_type) def get_notification_context(self, language_code, user=None, notification_type=None): if not user: user = self.user with translation.override(language_code): reserver_name = self.reserver_name reserver_email_address = self.reserver_email_address if not reserver_name and self.user and self.user.get_display_name( ): reserver_name = self.user.get_display_name() if not reserver_email_address and user and user.email: reserver_email_address = user.email context = { 'resource': self.resource.name, 'begin': localize_datetime(self.begin), 'end': localize_datetime(self.end), 'begin_dt': self.begin, 'end_dt': self.end, 'time_range': self.format_time(), 'reserver_name': reserver_name, 'reserver_email_address': reserver_email_address, } directly_included_fields = ( 'number_of_participants', 'host_name', 'event_subject', 'event_description', 'reserver_phone_number', 'billing_first_name', 'billing_last_name', 'billing_email_address', 'billing_phone_number', 'billing_address_street', 'billing_address_zip', 'billing_address_city', ) for field in directly_included_fields: context[field] = getattr(self, field) if self.resource.unit: context['unit'] = self.resource.unit.name context['unit_id'] = self.resource.unit.id if self.can_view_access_code(user) and self.access_code: context['access_code'] = self.access_code if notification_type == NotificationType.RESERVATION_CONFIRMED: if self.resource.reservation_confirmed_notification_extra: context[ 'extra_content'] = self.resource.reservation_confirmed_notification_extra elif notification_type == NotificationType.RESERVATION_REQUESTED: if self.resource.reservation_requested_notification_extra: context[ 'extra_content'] = self.resource.reservation_requested_notification_extra # Get last main and ground plan images. Normally there shouldn't be more than one of each # of those images. images = self.resource.images.filter( type__in=('main', 'ground_plan')).order_by('-sort_order') main_image = next((i for i in images if i.type == 'main'), None) ground_plan_image = next( (i for i in images if i.type == 'ground_plan'), None) if main_image: main_image_url = main_image.get_full_url() if main_image_url: context['resource_main_image_url'] = main_image_url if ground_plan_image: ground_plan_image_url = ground_plan_image.get_full_url() if ground_plan_image_url: context[ 'resource_ground_plan_image_url'] = ground_plan_image_url order = getattr(self, 'order', None) if order: context['order'] = order.get_notification_context( language_code) return context def send_reservation_mail(self, notification_type, user=None, attachments=None): """ Stuff common to all reservation related mails. If user isn't given use self.user. """ try: notification_template = NotificationTemplate.objects.get( type=notification_type) except NotificationTemplate.DoesNotExist: return if user: email_address = user.email else: if not (self.reserver_email_address or self.user): return email_address = self.reserver_email_address or self.user.email user = self.user language = user.get_preferred_language() if user else DEFAULT_LANG context = self.get_notification_context( language, notification_type=notification_type) try: rendered_notification = notification_template.render( context, language) except NotificationTemplateException as e: logger.error(e, exc_info=True, extra={'user': user.uuid}) return send_respa_mail(email_address, rendered_notification['subject'], rendered_notification['body'], rendered_notification['html_body'], attachments) def send_reservation_requested_mail(self): self.send_reservation_mail(NotificationType.RESERVATION_REQUESTED) def send_reservation_requested_mail_to_officials(self): notify_users = self.resource.get_users_with_perm( 'can_approve_reservation') if len(notify_users) > 100: raise Exception("Refusing to notify more than 100 users (%s)" % self) for user in notify_users: self.send_reservation_mail( NotificationType.RESERVATION_REQUESTED_OFFICIAL, user=user) def send_reservation_denied_mail(self): self.send_reservation_mail(NotificationType.RESERVATION_DENIED) def send_reservation_confirmed_mail(self): reservations = [self] ical_file = build_reservations_ical_file(reservations) attachment = ('reservation.ics', ical_file, 'text/calendar') self.send_reservation_mail(NotificationType.RESERVATION_CONFIRMED, attachments=[attachment]) def send_reservation_cancelled_mail(self): self.send_reservation_mail(NotificationType.RESERVATION_CANCELLED) def send_reservation_created_mail(self): reservations = [self] ical_file = build_reservations_ical_file(reservations) attachment = 'reservation.ics', ical_file, 'text/calendar' self.send_reservation_mail(NotificationType.RESERVATION_CREATED, attachments=[attachment]) def send_reservation_created_with_access_code_mail(self): reservations = [self] ical_file = build_reservations_ical_file(reservations) attachment = 'reservation.ics', ical_file, 'text/calendar' self.send_reservation_mail( NotificationType.RESERVATION_CREATED_WITH_ACCESS_CODE, attachments=[attachment]) def send_access_code_created_mail(self): self.send_reservation_mail( NotificationType.RESERVATION_ACCESS_CODE_CREATED) def save(self, *args, **kwargs): self.duration = DateTimeTZRange(self.begin, self.end, '[)') if not self.access_code: access_code_type = self.resource.access_code_type if self.resource.is_access_code_enabled( ) and self.resource.generate_access_codes: self.access_code = generate_access_code(access_code_type) return super().save(*args, **kwargs)
class TreeAction(TreeItem): key = models.CharField(max_length=256, choices=settings.CHOICES["actions"]) value = models.DateTimeField()