def reload(self): if self.pk: fromdb = self.__class__.objects.get(pk=self.pk) self.area = fromdb.area AltimetryMixin.reload(self, fromdb) TimeStampedModelMixin.reload(self, fromdb) NoDeleteMixin.reload(self, fromdb) if self.topology: self.topology.reload() return self
def reload(self, fromdb=None): """ Reload into instance all computed attributes in triggers. """ if self.pk: # Update computed values fromdb = self.__class__.objects.get(pk=self.pk) self.geom = fromdb.geom # /!\ offset may be set by a trigger OR in # the django code, reload() will override # any unsaved value self.offset = fromdb.offset AltimetryMixin.reload(self, fromdb) TimeStampedModelMixin.reload(self, fromdb) NoDeleteMixin.reload(self, fromdb) return self
class Project(AddPropertyMixin, MapEntityMixin, TimeStampedModelMixin, StructureRelated, NoDeleteMixin): name = models.CharField(verbose_name=_(u"Name"), max_length=128, db_column='nom') begin_year = models.IntegerField(verbose_name=_(u"Begin year"), db_column='annee_debut') end_year = models.IntegerField(verbose_name=_(u"End year"), db_column='annee_fin') constraint = models.TextField(verbose_name=_(u"Constraint"), blank=True, db_column='contraintes', help_text=_(u"Specific conditions, ...")) global_cost = models.FloatField(verbose_name=_(u"Global cost"), default=0, db_column='cout_global', help_text=_(u"€")) comments = models.TextField(verbose_name=_(u"Comments"), blank=True, db_column='commentaires', help_text=_(u"Remarks and notes")) type = models.ForeignKey('ProjectType', null=True, blank=True, verbose_name=_(u"Type"), db_column='type') domain = models.ForeignKey('ProjectDomain', null=True, blank=True, verbose_name=_(u"Domain"), db_column='domaine') contractors = models.ManyToManyField('Contractor', related_name="projects", db_table="m_r_chantier_prestataire", verbose_name=_(u"Contractors")) project_owner = models.ForeignKey(Organism, related_name='own', verbose_name=_(u"Project owner"), db_column='maitre_oeuvre') project_manager = models.ForeignKey(Organism, related_name='manage', verbose_name=_(u"Project manager"), db_column='maitre_ouvrage') founders = models.ManyToManyField(Organism, through='Funding', verbose_name=_(u"Founders")) objects = NoDeleteMixin.get_manager_cls(ProjectManager)() class Meta: db_table = 'm_t_chantier' verbose_name = _(u"Project") verbose_name_plural = _(u"Projects") ordering = ['-begin_year', 'name'] def __init__(self, *args, **kwargs): super(Project, self).__init__(*args, **kwargs) self._geom = None @property def paths(self): s = [] for i in self.interventions.existing(): s += i.paths return Path.objects.filter(pk__in=[p.pk for p in set(s)]) @property def trails(self): s = [] for i in self.interventions.existing(): for p in i.paths.all(): for t in p.trails.all(): s.append(t.pk) return Trail.objects.filter(pk__in=s) @property def signages(self): s = [] for i in self.interventions.existing(): s += i.signages return list(set(s)) @property def infrastructures(self): s = [] for i in self.interventions.existing(): s += i.infrastructures return list(set(s)) @classproperty def geomfield(cls): from django.contrib.gis.geos import LineString # Fake field, TODO: still better than overkill code in views, but can do neater. c = GeometryCollection([LineString((0, 0), (1, 1))], srid=settings.SRID) c.name = 'geom' return c @property def geom(self): """ Merge all interventions geometry into a collection """ if self._geom is None: interventions = Intervention.objects.existing().filter( project=self) geoms = [i.geom for i in interventions if i.geom is not None] if geoms: self._geom = GeometryCollection(*geoms, srid=settings.SRID) return self._geom @geom.setter def geom(self, value): self._geom = value @property def name_display(self): return u'<a data-pk="%s" href="%s" title="%s">%s</a>' % ( self.pk, self.get_detail_url(), self.name, self.name) @property def name_csv_display(self): return unicode(self.name) @property def interventions_csv_display(self): return [unicode(i) for i in self.interventions.existing()] @property def contractors_display(self): return [unicode(c) for c in self.contractors.all()] @property def founders_display(self): return [unicode(f) for f in self.founders.all()] @property def period(self): return "%s - %s" % (self.begin_year, self.end_year) @property def period_display(self): return self.period @classproperty def period_verbose_name(cls): return _("Period") @property def interventions_total_cost(self): total = 0 qs = self.interventions.existing() for i in qs.prefetch_related('manday_set', 'manday_set__job'): total += i.total_cost return total @classproperty def interventions_total_cost_verbose_name(cls): return _("Interventions total cost") def __unicode__(self): return u"%s (%s-%s)" % (self.name, self.begin_year, self.end_year) @classmethod def path_projects(cls, path): return cls.objects.existing().filter( interventions__in=path.interventions).distinct() @classmethod def topology_projects(cls, topology): return cls.objects.existing().filter( interventions__in=topology.interventions).distinct() def edges_by_attr(self, interventionattr): """ Return related topology objects of project, by aggregating the same attribute on its interventions. (See geotrek.land.models) """ pks = [] modelclass = Topology for i in self.interventions.all(): attr_value = getattr(i, interventionattr) if isinstance(attr_value, list): pks += [o.pk for o in attr_value] else: modelclass = attr_value.model topologies = attr_value.values('ordering', 'id') for topology in topologies: pks.append(topology['id']) return modelclass.objects.filter(pk__in=pks) @classmethod def get_create_label(cls): return _(u"Add a new project")
class Intervention(AddPropertyMixin, MapEntityMixin, AltimetryMixin, TimeStampedModelMixin, StructureRelated, NoDeleteMixin): name = models.CharField(verbose_name=_(u"Name"), max_length=128, db_column='nom', help_text=_(u"Brief summary")) date = models.DateField(default=datetime.now, verbose_name=_(u"Date"), db_column='date', help_text=_(u"When ?")) subcontracting = models.BooleanField(verbose_name=_(u"Subcontracting"), default=False, db_column='sous_traitance') # Technical information width = models.FloatField(default=0.0, verbose_name=_(u"Width"), db_column='largeur') height = models.FloatField(default=0.0, verbose_name=_(u"Height"), db_column='hauteur') area = models.FloatField(editable=False, default=0, verbose_name=_(u"Area"), db_column='surface') # Costs material_cost = models.FloatField(default=0.0, verbose_name=_(u"Material cost"), db_column='cout_materiel') heliport_cost = models.FloatField(default=0.0, verbose_name=_(u"Heliport cost"), db_column='cout_heliport') subcontract_cost = models.FloatField(default=0.0, verbose_name=_(u"Subcontract cost"), db_column='cout_soustraitant') """ Topology can be of type Infrastructure, Signage or of own type Intervention """ topology = models.ForeignKey( Topology, null=True, # TODO: why null ? related_name="interventions_set", verbose_name=_(u"Interventions")) # AltimetyMixin for denormalized fields from related topology, updated via trigger. length = models.FloatField(editable=True, default=0.0, null=True, blank=True, db_column='longueur', verbose_name=_(u"3D Length")) stake = models.ForeignKey('core.Stake', null=True, blank=True, related_name='interventions', verbose_name=_("Stake"), db_column='enjeu') status = models.ForeignKey('InterventionStatus', verbose_name=_("Status"), db_column='status') type = models.ForeignKey('InterventionType', null=True, blank=True, verbose_name=_(u"Type"), db_column='type') disorders = models.ManyToManyField('InterventionDisorder', related_name="interventions", db_table="m_r_intervention_desordre", verbose_name=_(u"Disorders"), blank=True) jobs = models.ManyToManyField('InterventionJob', through='ManDay', verbose_name=_(u"Jobs")) project = models.ForeignKey('Project', null=True, blank=True, related_name="interventions", verbose_name=_(u"Project"), db_column='chantier') description = models.TextField(blank=True, verbose_name=_(u"Description"), db_column='descriptif', help_text=_(u"Remarks and notes")) objects = NoDeleteMixin.get_manager_cls(InterventionManager)() class Meta: db_table = 'm_t_intervention' verbose_name = _(u"Intervention") verbose_name_plural = _(u"Interventions") def __init__(self, *args, **kwargs): super(Intervention, self).__init__(*args, **kwargs) self._geom = None def set_topology(self, topology): self.topology = topology if not self.on_existing_topology: raise ValueError("Expecting an infrastructure or signage") def default_stake(self): stake = None if self.topology: for path in self.topology.paths.all(): if path.stake > stake: stake = path.stake return stake def reload(self): if self.pk: fromdb = self.__class__.objects.get(pk=self.pk) self.area = fromdb.area AltimetryMixin.reload(self, fromdb) TimeStampedModelMixin.reload(self, fromdb) NoDeleteMixin.reload(self, fromdb) if self.topology: self.topology.reload() return self def save(self, *args, **kwargs): if self.stake is None: self.stake = self.default_stake() super(Intervention, self).save(*args, **kwargs) # Set kind of Intervention topology if self.topology and not self.on_existing_topology: topology_kind = self._meta.object_name.upper() self.topology.kind = topology_kind self.topology.save(update_fields=['kind']) # Invalidate project map if self.project: try: os.remove(self.project.get_map_image_path()) except OSError: pass self.reload() @property def on_existing_topology(self): return self.is_infrastructure or self.is_signage @property def infrastructure(self): """ Equivalent of topology attribute, but casted to related type (Infrastructure) """ if self.is_infrastructure: return self.infrastructures[0] return None @property def signage(self): """ Equivalent of topology attribute, but casted to related type (Signage) """ if self.is_signage: return self.signages[0] return None @classproperty def infrastructure_verbose_name(cls): return _("On") @classproperty def signage_verbose_name(cls): return _("On") @property def infrastructure_display(self): icon = 'path' title = _('Path') if self.on_existing_topology: icon = self.topology.kind.lower() if self.infrastructure: title = u'%s: %s' % (_( self.topology.kind.capitalize()), self.infrastructure) elif self.signage: title = u'%s: %s' % (_( self.topology.kind.capitalize()), self.signage) return u'<img src="%simages/%s-16.png" title="%s">' % ( settings.STATIC_URL, icon, title) @property def infrastructure_csv_display(self): if self.on_existing_topology: if self.infrastructure: return u"%s: %s (%s)" % (_( self.topology.kind.capitalize()), self.infrastructure, self.infrastructure.pk) elif self.signage: return u"%s: %s (%s)" % (_(self.topology.kind.capitalize()), self.signage, self.signage.pk) return '' @property def is_infrastructure(self): if self.topology: return self.topology.kind == Infrastructure.KIND return False @property def is_signage(self): if self.topology: return self.topology.kind == Signage.KIND return False @property def in_project(self): return self.project is not None @property def paths(self): if self.topology: return self.topology.paths.all() return Path.objects.none() @property def trails(self): s = [] for p in self.paths.all(): for t in p.trails.all(): s.append(t.pk) return Trail.objects.filter(pk__in=s) @property def signages(self): if self.is_signage: return [Signage.objects.existing().get(pk=self.topology.pk)] return [] @property def infrastructures(self): if self.is_infrastructure: return [Infrastructure.objects.existing().get(pk=self.topology.pk)] return [] @property def total_manday(self): total = 0.0 for md in self.manday_set.all(): total += float(md.nb_days) return total @classproperty def total_manday_verbose_name(cls): return _("Mandays") @property def total_cost_mandays(self): total = 0.0 for md in self.manday_set.all(): total += md.cost return total @classproperty def total_cost_mandays_verbose_name(cls): return _("Mandays cost") @property def total_cost(self): return self.total_cost_mandays + \ self.material_cost + \ self.heliport_cost + \ self.subcontract_cost @classproperty def total_cost_verbose_name(cls): return _("Total cost") @classproperty def geomfield(cls): return Topology._meta.get_field('geom') @property def geom(self): if self._geom is None: if self.topology: self._geom = self.topology.geom return self._geom @geom.setter def geom(self, value): self._geom = value @property def name_display(self): return u'<a data-pk="%s" href="%s" title="%s" >%s</a>' % ( self.pk, self.get_detail_url(), self.name, self.name) @property def name_csv_display(self): return unicode(self.name) def __unicode__(self): return u"%s (%s)" % (self.name, self.date) @classmethod def path_interventions(cls, path): return cls.objects.existing().filter(topology__aggregations__path=path) @classmethod def topology_interventions(cls, topology): topos = Topology.overlapping(topology).values_list('pk', flat=True) return cls.objects.existing().filter(topology__in=topos).distinct('pk')
class Topology(AddPropertyMixin, AltimetryMixin, TimeStampedModelMixin, NoDeleteMixin): paths = models.ManyToManyField(Path, db_column='troncons', through='PathAggregation', verbose_name=_("Path")) offset = models.FloatField(default=0.0, db_column='decallage', verbose_name=_("Offset")) # in SRID units kind = models.CharField(editable=False, verbose_name=_("Kind"), max_length=32) # Override default manager objects = NoDeleteMixin.get_manager_cls(models.GeoManager)() geom = models.GeometryField( editable=(not settings.TREKKING_TOPOLOGY_ENABLED), srid=settings.SRID, null=True, default=None, spatial_index=False) """ Fake srid attribute, that prevents transform() calls when using Django map widgets. """ srid = settings.API_SRID class Meta: db_table = 'e_t_evenement' verbose_name = _("Topology") verbose_name_plural = _("Topologies") def __init__(self, *args, **kwargs): super(Topology, self).__init__(*args, **kwargs) if not self.pk: self.kind = self.__class__.KIND @property def length_2d(self): if self.geom and not self.ispoint(): return round(self.geom.length, 1) else: return None @classproperty def length_2d_verbose_name(cls): return _("2D Length") @property def length_2d_display(self): return self.length_2d @classproperty def KIND(cls): return cls._meta.object_name.upper() def __str__(self): return "%s (%s)" % (_("Topology"), self.pk) def ispoint(self): if not settings.TREKKING_TOPOLOGY_ENABLED or not self.pk: return self.geom and self.geom.geom_type == 'Point' return all([ a.start_position == a.end_position for a in self.aggregations.all() ]) def add_path(self, path, start=0.0, end=1.0, order=0, reload=True): """ Shortcut function to add paths into this topology. """ from .factories import PathAggregationFactory aggr = PathAggregationFactory.create(topo_object=self, path=path, start_position=start, end_position=end, order=order) if self.deleted: self.deleted = False self.save(update_fields=['deleted']) # Since a trigger modifies geom, we reload the object if reload: self.reload() return aggr @classmethod def overlapping(cls, topologies): """ Return a Topology queryset overlapping specified topologies. """ return TopologyHelper.overlapping(cls, topologies) def mutate(self, other, delete=True): """ Take alls attributes of the other topology specified and save them into this one. Optionnally deletes the other. """ self.offset = other.offset self.save(update_fields=['offset']) PathAggregation.objects.filter(topo_object=self).delete() # The previous operation has put deleted = True (in triggers) # and NULL in geom (see update_geometry_of_evenement:: IF t_count = 0) self.deleted = False self.geom = other.geom self.save(update_fields=['deleted', 'geom']) # Now copy all agregations from other to self aggrs = other.aggregations.all() # A point has only one aggregation, except if it is on an intersection. # In this case, the trigger will create them, so ignore them here. if other.ispoint(): aggrs = aggrs[:1] for aggr in aggrs: self.add_path(aggr.path, aggr.start_position, aggr.end_position, aggr.order, reload=False) self.reload() if delete: other.delete(force=True) # Really delete it from database return self def reload(self): """ Reload into instance all computed attributes in triggers. """ if self.pk: # Update computed values fromdb = self.__class__.objects.get(pk=self.pk) self.geom = fromdb.geom # /!\ offset may be set by a trigger OR in # the django code, reload() will override # any unsaved value self.offset = fromdb.offset AltimetryMixin.reload(self, fromdb) TimeStampedModelMixin.reload(self, fromdb) NoDeleteMixin.reload(self, fromdb) return self @debug_pg_notices def save(self, *args, **kwargs): # HACK: these fields are readonly from the Django point of view # but they can be changed at DB level. Since Django write all fields # to DB anyway, it is important to update it before writting if self.pk and settings.TREKKING_TOPOLOGY_ENABLED: existing = self.__class__.objects.get(pk=self.pk) self.length = existing.length # In the case of points, the geom can be set by Django. Don't override. point_geom_not_set = self.ispoint() and self.geom is None geom_already_in_db = not self.ispoint( ) and existing.geom is not None if (point_geom_not_set or geom_already_in_db): self.geom = existing.geom else: if not self.deleted and self.geom is None: # We cannot have NULL geometry. So we use an empty one, # it will be computed or overwritten by triggers. self.geom = fromstr('POINT (0 0)') if not self.kind: if self.KIND == "TOPOLOGYMIXIN": raise Exception("Cannot save abstract topologies") self.kind = self.__class__.KIND # Static value for Topology offset, if any shortmodelname = self._meta.object_name.lower().replace('edge', '') self.offset = settings.TOPOLOGY_STATIC_OFFSETS.get( shortmodelname, self.offset) # Save into db super(Topology, self).save(*args, **kwargs) self.reload() def serialize(self, **kwargs): return TopologyHelper.serialize(self, **kwargs) @classmethod def deserialize(cls, serialized): return TopologyHelper.deserialize(serialized) def distance(self, to_cls): """Distance to associate this topology to another topology class""" return None
class TouristicEvent(AddPropertyMixin, PublishableMixin, MapEntityMixin, StructureRelated, PicturesMixin, TimeStampedModelMixin, NoDeleteMixin): """ A touristic event (conference, workshop, etc.) in the park """ description_teaser = models.TextField( verbose_name=_(u"Description teaser"), blank=True, help_text=_(u"A brief summary"), db_column='chapeau') description = models.TextField(verbose_name=_(u"Description"), blank=True, db_column='description', help_text=_(u"Complete description")) themes = models.ManyToManyField(Theme, related_name="touristic_events", db_table="t_r_evenement_touristique_theme", blank=True, null=True, verbose_name=_(u"Themes"), help_text=_(u"Main theme(s)")) geom = models.PointField(verbose_name=_(u"Location"), srid=settings.SRID) begin_date = models.DateField(blank=True, null=True, verbose_name=_(u"Begin date"), db_column='date_debut') end_date = models.DateField(blank=True, null=True, verbose_name=_(u"End date"), db_column='date_fin') duration = models.CharField(verbose_name=_(u"Duration"), max_length=64, blank=True, db_column='duree', help_text=_(u"3 days, season, ...")) meeting_point = models.CharField(verbose_name=_(u"Meeting point"), max_length=256, blank=True, db_column='point_rdv', help_text=_(u"Where exactly ?")) meeting_time = models.TimeField(verbose_name=_(u"Meeting time"), blank=True, null=True, db_column='heure_rdv', help_text=_(u"11:00, 23:30")) contact = models.TextField(verbose_name=_(u"Contact"), blank=True, db_column='contact') email = models.EmailField(verbose_name=_(u"Email"), max_length=256, db_column='email', blank=True, null=True) website = models.URLField(verbose_name=_(u"Website"), max_length=256, db_column='website', blank=True, null=True) organizer = models.CharField(verbose_name=_(u"Organizer"), max_length=256, blank=True, db_column='organisateur') speaker = models.CharField(verbose_name=_(u"Speaker"), max_length=256, blank=True, db_column='intervenant') type = models.ForeignKey(TouristicEventType, verbose_name=_(u"Type"), blank=True, null=True, db_column='type') accessibility = models.CharField(verbose_name=_(u"Accessibility"), max_length=256, blank=True, db_column='accessibilite') participant_number = models.CharField( verbose_name=_(u"Number of participants"), max_length=256, blank=True, db_column='nb_places') booking = models.TextField(verbose_name=_(u"Booking"), blank=True, db_column='reservation') target_audience = models.CharField(verbose_name=_(u"Target audience"), max_length=128, blank=True, null=True, db_column='public_vise') practical_info = models.TextField( verbose_name=_(u"Practical info"), blank=True, db_column='infos_pratiques', help_text=_(u"Recommandations / To plan / Advices")) source = models.ManyToManyField( 'common.RecordSource', null=True, blank=True, related_name='touristicevents', verbose_name=_("Source"), db_table='t_r_evenement_touristique_source') eid = models.CharField(verbose_name=_(u"External id"), max_length=128, blank=True, db_column='id_externe') approved = models.BooleanField(verbose_name=_(u"Approved"), default=False, db_column='labellise') objects = NoDeleteMixin.get_manager_cls(models.GeoManager)() category_id_prefix = 'E' class Meta: db_table = 't_t_evenement_touristique' verbose_name = _(u"Touristic event") verbose_name_plural = _(u"Touristic events") ordering = ['-begin_date'] def __unicode__(self): return self.name @models.permalink def get_document_public_url(self): """ Override ``geotrek.common.mixins.PublishableMixin`` """ return ('tourism:touristicevent_document_public', [], { 'lang': get_language(), 'pk': self.pk, 'slug': self.slug }) @property def type1(self): return [self.type] if self.type else [] @property def type2(self): return [] @property def districts_display(self): return ', '.join([unicode(d) for d in self.districts]) @property def dates_display(self): if not self.begin_date and not self.end_date: return u"" elif not self.end_date: return _(u"starting from {begin}").format( begin=date_format(self.begin_date, 'SHORT_DATE_FORMAT')) elif not self.begin_date: return _(u"up to {end}").format( end=date_format(self.end_date, 'SHORT_DATE_FORMAT')) elif self.begin_date == self.end_date: return date_format(self.begin_date, 'SHORT_DATE_FORMAT') else: return _(u"from {begin} to {end}").format( begin=date_format(self.begin_date, 'SHORT_DATE_FORMAT'), end=date_format(self.end_date, 'SHORT_DATE_FORMAT')) @property def prefixed_category_id(self): return self.category_id_prefix def distance(self, to_cls): return settings.TOURISM_INTERSECTION_MARGIN
class TouristicContent(AddPropertyMixin, PublishableMixin, MapEntityMixin, StructureRelated, TimeStampedModelMixin, PicturesMixin, NoDeleteMixin): """ A generic touristic content (accomodation, museum, etc.) in the park """ description_teaser = models.TextField( verbose_name=_(u"Description teaser"), blank=True, help_text=_(u"A brief summary"), db_column='chapeau') description = models.TextField(verbose_name=_(u"Description"), blank=True, db_column='description', help_text=_(u"Complete description")) themes = models.ManyToManyField(Theme, related_name="touristiccontents", db_table="t_r_contenu_touristique_theme", blank=True, null=True, verbose_name=_(u"Themes"), help_text=_(u"Main theme(s)")) geom = models.GeometryField(verbose_name=_(u"Location"), srid=settings.SRID) category = models.ForeignKey(TouristicContentCategory, related_name='contents', verbose_name=_(u"Category"), db_column='categorie') contact = models.TextField(verbose_name=_(u"Contact"), blank=True, db_column='contact', help_text=_(u"Address, phone, etc.")) email = models.EmailField(verbose_name=_(u"Email"), max_length=256, db_column='email', blank=True, null=True) website = models.URLField(verbose_name=_(u"Website"), max_length=256, db_column='website', blank=True, null=True) practical_info = models.TextField(verbose_name=_(u"Practical info"), blank=True, db_column='infos_pratiques', help_text=_(u"Anything worth to know")) type1 = models.ManyToManyField(TouristicContentType, related_name='contents1', verbose_name=_(u"Type 1"), db_table="t_r_contenu_touristique_type1", blank=True) type2 = models.ManyToManyField(TouristicContentType, related_name='contents2', verbose_name=_(u"Type 2"), db_table="t_r_contenu_touristique_type2", blank=True) source = models.ManyToManyField('common.RecordSource', null=True, blank=True, related_name='touristiccontents', verbose_name=_("Source"), db_table='t_r_contenu_touristique_source') eid = models.CharField(verbose_name=_(u"External id"), max_length=128, blank=True, db_column='id_externe') reservation_system = models.ForeignKey( ReservationSystem, verbose_name=_(u"Reservation system"), blank=True, null=True) reservation_id = models.CharField(verbose_name=_(u"Reservation ID"), max_length=128, blank=True, db_column='id_reservation') approved = models.BooleanField(verbose_name=_(u"Approved"), default=False, db_column='labellise') objects = NoDeleteMixin.get_manager_cls(models.GeoManager)() class Meta: db_table = 't_t_contenu_touristique' verbose_name = _(u"Touristic content") verbose_name_plural = _(u"Touristic contents") def __unicode__(self): return self.name @models.permalink def get_document_public_url(self): """ Override ``geotrek.common.mixins.PublishableMixin`` """ return ('tourism:touristiccontent_document_public', [], { 'lang': get_language(), 'pk': self.pk, 'slug': self.slug }) @property def districts_display(self): return ', '.join([unicode(d) for d in self.districts]) @property def type1_label(self): return self.category.type1_label @property def type2_label(self): return self.category.type2_label @property def types1_display(self): return ', '.join([unicode(n) for n in self.type1.all()]) @property def types2_display(self): return ', '.join([unicode(n) for n in self.type2.all()]) @property def prefixed_category_id(self): return self.category.prefixed_id def distance(self, to_cls): return settings.TOURISM_INTERSECTION_MARGIN @property def type(self): """Fake type to simulate POI for mobile app v1""" return self.category @property def min_elevation(self): return 0 @property def max_elevation(self): return 0
class Blade(NoDeleteMixin, MapEntityMixin, StructureRelated): signage = models.ForeignKey(Signage, db_column='signaletique', verbose_name=_("Signage"), on_delete=models.PROTECT) number = models.CharField(verbose_name=_("Number"), max_length=250, db_column='numero') direction = models.ForeignKey(Direction, verbose_name=_("Direction"), db_column='direction', on_delete=models.PROTECT) type = models.ForeignKey(BladeType, db_column='type', verbose_name=_("Type")) color = models.ForeignKey(Color, db_column='couleur', on_delete=models.PROTECT, null=True, blank=True, verbose_name=_("Color")) condition = models.ForeignKey(InfrastructureCondition, db_column='etat', verbose_name=_("Condition"), null=True, blank=True, on_delete=models.PROTECT) topology = models.ForeignKey(Topology, related_name="blades_set", verbose_name=_("Blades")) objects = NoDeleteMixin.get_manager_cls(BladeManager)() class Meta: db_table = 's_t_lame' verbose_name = _("Blade") verbose_name_plural = _("Blades") def __str__(self): return settings.BLADE_CODE_FORMAT.format(signagecode=self.signage.code, bladenumber=self.number) def set_topology(self, topology): self.topology = topology if not self.is_signage: raise ValueError("Expecting a signage") @property def is_signage(self): if self.topology: return self.topology.kind == Signage.KIND return False @property def geom(self): return self.topology.geom @geom.setter def geom(self, value): self._geom = value @property def signage_display(self): return '<img src="%simages/signage-16.png" title="Signage">' % settings.STATIC_URL @property def order_lines(self): return self.lines.order_by('number') @property def number_display(self): s = '<a data-pk="%s" href="%s" title="%s" >%s</a>' % ( self.pk, self.get_detail_url(), self, self) return s
class SensitiveArea(MapEntityMixin, StructureRelated, TimeStampedModelMixin, NoDeleteMixin, AddPropertyMixin): geom = models.GeometryField(srid=settings.SRID) species = models.ForeignKey(Species, verbose_name=_(u"Sensitive area"), db_column='espece', on_delete=models.PROTECT) published = models.BooleanField(verbose_name=_(u"Published"), default=False, help_text=_(u"Online"), db_column='public') publication_date = models.DateField(verbose_name=_(u"Publication date"), null=True, blank=True, editable=False, db_column='date_publication') description = models.TextField(verbose_name=_("Description"), blank=True) contact = models.TextField(verbose_name=_("Contact"), blank=True) eid = models.CharField(verbose_name=_(u"External id"), max_length=128, blank=True, null=True, db_column='id_externe') objects = NoDeleteMixin.get_manager_cls(models.GeoManager)() class Meta: db_table = 's_t_zone_sensible' verbose_name = _(u"Sensitive area") verbose_name_plural = _(u"Sensitive areas") permissions = ( ("import_sensitivearea", "Can import Sensitive area"), ) def __unicode__(self): return self.species.name @property def radius(self): if self.species.radius is None: return settings.SENSITIVITY_DEFAULT_RADIUS return self.species.radius @classproperty def radius_verbose_name(cls): return _("Radius") @property def category_display(self): return self.species.get_category_display() @classproperty def category_verbose_name(cls): return _("Category") def save(self, *args, **kwargs): if self.publication_date is None and self.published: self.publication_date = datetime.date.today() if self.publication_date is not None and not self.published: self.publication_date = None super(SensitiveArea, self).save(*args, **kwargs) @property def any_published(self): return self.published @property def published_status(self): """Returns the publication status by language. """ status = [] for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']: status.append({ 'lang': l[0], 'language': l[1], 'status': self.published }) return status @property def published_langs(self): """Returns languages in which the object is published. """ if self.published: return [l[0] for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']] else: return [] @property def species_display(self): s = u'<a data-pk="%s" href="%s" title="%s">%s</a>' % (self.pk, self.get_detail_url(), self.species.name, self.species.name) if self.published: s = u'<span class="badge badge-success" title="%s">☆</span> ' % _("Published") + s return s @property def extent(self): return self.geom.transform(settings.API_SRID, clone=True).extent if self.geom else None def kml(self): """Exports sensitivearea into KML format""" kml = simplekml.Kml() geom = self.geom if geom.geom_type == 'Point': geom = geom.buffer(self.species.radius or settings.SENSITIVITY_DEFAULT_RADIUS, 4) geom = geom.transform(4326, clone=True) # KML uses WGS84 line = kml.newpolygon(name=self.species.name, description=plain_text(self.description), outerboundaryis=geom.coords[0]) line.style.linestyle.color = simplekml.Color.red # Red line.style.linestyle.width = 4 # pixels return kml.kml() def is_public(self): return self.published @property def pretty_period(self): return self.species.pretty_period() pretty_period_verbose_name = _("Period") @property def pretty_practices(self): return self.species.pretty_practices() pretty_practices_verbose_name = _("Practices")
class TouristicContent(AddPropertyMixin, PublishableMixin, MapEntityMixin, StructureRelated, TimeStampedModelMixin, PicturesMixin, NoDeleteMixin): """ A generic touristic content (accomodation, museum, etc.) in the park """ description_teaser = models.TextField( verbose_name=_(u"Description teaser"), blank=True, help_text=_(u"A brief summary"), db_column='chapeau') description = models.TextField(verbose_name=_(u"Description"), blank=True, db_column='description', help_text=_(u"Complete description")) themes = models.ManyToManyField(Theme, related_name="touristiccontents", db_table="t_r_contenu_touristique_theme", blank=True, verbose_name=_(u"Themes"), help_text=_(u"Main theme(s)")) geom = models.GeometryField(verbose_name=_(u"Location"), srid=settings.SRID) category = models.ForeignKey(TouristicContentCategory, related_name='contents', verbose_name=_(u"Category"), db_column='categorie') contact = models.TextField(verbose_name=_(u"Contact"), blank=True, db_column='contact', help_text=_(u"Address, phone, etc.")) email = models.EmailField(verbose_name=_(u"Email"), max_length=256, db_column='email', blank=True, null=True) website = models.URLField(verbose_name=_(u"Website"), max_length=256, db_column='website', blank=True, null=True) practical_info = models.TextField(verbose_name=_(u"Practical info"), blank=True, db_column='infos_pratiques', help_text=_(u"Anything worth to know")) type1 = models.ManyToManyField(TouristicContentType, related_name='contents1', verbose_name=_(u"Type 1"), db_table="t_r_contenu_touristique_type1", blank=True) type2 = models.ManyToManyField(TouristicContentType, related_name='contents2', verbose_name=_(u"Type 2"), db_table="t_r_contenu_touristique_type2", blank=True) source = models.ManyToManyField('common.RecordSource', blank=True, related_name='touristiccontents', verbose_name=_("Source"), db_table='t_r_contenu_touristique_source') portal = models.ManyToManyField('common.TargetPortal', blank=True, related_name='touristiccontents', verbose_name=_("Portal"), db_table='t_r_contenu_touristique_portal') eid = models.CharField(verbose_name=_(u"External id"), max_length=1024, blank=True, null=True, db_column='id_externe') reservation_system = models.ForeignKey( ReservationSystem, verbose_name=_(u"Reservation system"), blank=True, null=True) reservation_id = models.CharField(verbose_name=_(u"Reservation ID"), max_length=1024, blank=True, db_column='id_reservation') approved = models.BooleanField(verbose_name=_(u"Approved"), default=False, db_column='labellise') objects = NoDeleteMixin.get_manager_cls(models.GeoManager)() class Meta: db_table = 't_t_contenu_touristique' verbose_name = _(u"Touristic content") verbose_name_plural = _(u"Touristic contents") def __unicode__(self): return self.name @property def districts_display(self): return ', '.join([unicode(d) for d in self.districts]) @property def type1_label(self): return self.category.type1_label @property def type2_label(self): return self.category.type2_label @property def type1_display(self): return ', '.join([unicode(n) for n in self.type1.all()]) @property def type2_display(self): return ', '.join([unicode(n) for n in self.type2.all()]) @property def prefixed_category_id(self): return self.category.prefixed_id def distance(self, to_cls): return settings.TOURISM_INTERSECTION_MARGIN @property def type(self): """Fake type to simulate POI for mobile app v1""" return self.category @property def min_elevation(self): return 0 @property def max_elevation(self): return 0 @property def portal_display(self): return ', '.join([unicode(portal) for portal in self.portal.all()]) @property def source_display(self): return ','.join([unicode(source) for source in self.source.all()]) @property def themes_display(self): return ','.join([unicode(source) for source in self.themes.all()]) @property def extent(self): return self.geom.buffer(10).transform(settings.API_SRID, clone=True).extent @property def rando_url(self): category_slug = _(u'touristic-content') return '{}/{}/'.format(category_slug, self.slug) @property def meta_description(self): return plain_text(self.description_teaser or self.description)[:500]