示例#1
0
class RestrictedAreaEdge(Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    restricted_area = models.ForeignKey(RestrictedArea,
                                        verbose_name=_(u"Restricted area"),
                                        db_column='zone')

    # Override default manager
    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'f_t_zonage'
        verbose_name = _(u"Restricted area edge")
        verbose_name_plural = _(u"Restricted area edges")

    def __unicode__(self):
        return _(u"Restricted area edge") + u": %s" % self.restricted_area

    @classmethod
    def path_area_edges(cls, path):
        return cls.objects.select_related('restricted_area')\
                          .select_related('restricted_area__area_type')\
                          .filter(aggregations__path=path).distinct('pk')

    @classmethod
    def topology_area_edges(cls, topology):
        return cls.overlapping(topology).select_related('restricted_area')\
                                        .select_related('restricted_area__area_type')
示例#2
0
class CityEdge(Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')

    city = models.ForeignKey(City,
                             verbose_name=_(u"City"),
                             db_column='commune')

    # Override default manager
    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'f_t_commune'
        verbose_name = _(u"City edge")
        verbose_name_plural = _(u"City edges")

    def __unicode__(self):
        return _("City edge") + u": %s" % self.city

    @classmethod
    def path_city_edges(cls, path):
        return cls.objects.select_related('city').filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_city_edges(cls, topology):
        return cls.overlapping(topology).select_related('city')
示例#3
0
class DistrictEdge(Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    district = models.ForeignKey(District,
                                 verbose_name=_(u"District"),
                                 db_column='secteur')

    # Override default manager
    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'f_t_secteur'
        verbose_name = _(u"District edge")
        verbose_name_plural = _(u"District edges")

    def __unicode__(self):
        return _(u"District edge") + u": %s" % self.district

    @classmethod
    def path_district_edges(cls, path):
        return cls.objects.select_related('district').filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_district_edges(cls, topology):
        return cls.overlapping(topology).select_related('district')
示例#4
0
class POI(PicturesMixin, MapEntityMixin, Topology):

    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    name = models.CharField(verbose_name=_(u"Name"),
                            max_length=128,
                            db_column='nom',
                            help_text=_(u"Official name"))
    description = models.TextField(verbose_name=_(u"Description"),
                                   db_column='description',
                                   help_text=_(u"History, details,  ..."))
    type = models.ForeignKey('POIType',
                             related_name='pois',
                             verbose_name=_(u"Type"),
                             db_column='type')

    class Meta:
        db_table = 'o_t_poi'
        verbose_name = _(u"POI")
        verbose_name_plural = _(u"POI")

    # Override default manager
    objects = Topology.get_manager_cls(POIManager)()

    def __unicode__(self):
        return u"%s (%s)" % (self.name, self.type)

    @property
    def type_display(self):
        return unicode(self.type)

    @property
    def name_display(self):
        return u'<a data-pk="%s" href="%s" >%s</a>' % (
            self.pk, self.get_detail_url(), self.name)

    @property
    def name_csv_display(self):
        return unicode(self.name)

    @property
    def serializable_type(self):
        return {
            'label': self.type.label,
            'pictogram': self.type.serializable_pictogram
        }

    @classmethod
    def path_pois(cls, path):
        return cls.objects.filter(aggregations__path=path).distinct('pk')

    @classmethod
    def topology_pois(cls, topology):
        return cls.overlapping(topology)
示例#5
0
class LandEdge(MapEntityMixin, Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    land_type = models.ForeignKey(LandType,
                                  verbose_name=_(u"Land type"),
                                  db_column='type')
    owner = models.TextField(verbose_name=_(u"Owner"),
                             db_column='proprietaire',
                             blank=True)
    agreement = models.BooleanField(verbose_name=_(u"Agreement"),
                                    db_column='convention',
                                    default=False)
    eid = models.CharField(verbose_name=_(u"External id"),
                           max_length=1024,
                           blank=True,
                           null=True,
                           db_column='id_externe')

    # Override default manager
    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'f_t_foncier'
        verbose_name = _(u"Land edge")
        verbose_name_plural = _(u"Land edges")

    def __unicode__(self):
        return _(u"Land edge") + u": %s" % self.land_type

    @property
    def color_index(self):
        return self.land_type_id

    @property
    def name(self):
        return self.land_type_csv_display

    @property
    def land_type_display(self):
        return u'<a data-pk="%s" href="%s" >%s</a>' % (
            self.pk, self.get_detail_url(), self.land_type)

    @property
    def land_type_csv_display(self):
        return unicode(self.land_type)

    @classmethod
    def path_lands(cls, path):
        return cls.objects.existing().select_related('land_type').filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_lands(cls, topology):
        return cls.overlapping(topology).select_related('land_type')
示例#6
0
class CompetenceEdge(MapEntityMixin, Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    organization = models.ForeignKey(Organism,
                                     verbose_name=_(u"Organism"),
                                     db_column='organisme')
    eid = models.CharField(verbose_name=_(u"External id"),
                           max_length=1024,
                           blank=True,
                           null=True,
                           db_column='id_externe')

    # Override default manager
    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'f_t_competence'
        verbose_name = _(u"Competence edge")
        verbose_name_plural = _(u"Competence edges")

    def __unicode__(self):
        return _(u"Competence edge") + u": %s" % self.organization

    @property
    def color_index(self):
        return self.organization_id

    @property
    def name(self):
        return self.organization_csv_display

    @property
    def organization_display(self):
        return u'<a data-pk="%s" href="%s" >%s</a>' % (
            self.pk, self.get_detail_url(), self.organization)

    @property
    def organization_csv_display(self):
        return unicode(self.organization)

    @classmethod
    def path_competences(cls, path):
        return cls.objects.existing().select_related('organization').filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_competences(cls, topology):
        return cls.overlapping(Topology.objects.get(
            pk=topology.pk)).select_related('organization')
示例#7
0
class PhysicalEdge(MapEntityMixin, Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    physical_type = models.ForeignKey(PhysicalType,
                                      verbose_name=_(u"Physical type"),
                                      db_column='type')

    # Override default manager
    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'f_t_nature'
        verbose_name = _(u"Physical edge")
        verbose_name_plural = _(u"Physical edges")

    def __unicode__(self):
        return _(u"Physical edge") + u": %s" % self.physical_type

    @property
    def color_index(self):
        return self.physical_type_id

    @property
    def name(self):
        return self.physical_type_csv_display

    @property
    def physical_type_display(self):
        return self.display

    @property
    def physical_type_csv_display(self):
        return unicode(self.physical_type)

    @property
    def display(self):
        return u'<a data-pk="%s" href="%s" >%s</a>' % (
            self.pk, self.get_detail_url(), self.physical_type)

    @classmethod
    def path_physicals(cls, path):
        return cls.objects.select_related('physical_type').filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_physicals(cls, topology):
        return cls.overlapping(topology).select_related('physical_type')
示例#8
0
class SignageManagementEdge(MapEntityMixin, Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    organization = models.ForeignKey(Organism,
                                     verbose_name=_(u"Organism"),
                                     db_column='organisme')

    # Override default manager
    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'f_t_gestion_signaletique'
        verbose_name = _(u"Signage management edge")
        verbose_name_plural = _(u"Signage management edges")

    def __unicode__(self):
        return _(u"Signage management edge") + u": %s" % self.organization

    @property
    def color_index(self):
        return self.organization_id

    @property
    def name(self):
        return self.organization_csv_display

    @property
    def organization_display(self):
        return self.display

    @property
    def organization_csv_display(self):
        return unicode(self.organization)

    @property
    def display(self):
        return u'<a data-pk="%s" href="%s" >%s</a>' % (
            self.pk, self.get_detail_url(), self.organization)

    @classmethod
    def path_signages(cls, path):
        return cls.objects.select_related('organization').filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_signages(cls, topology):
        return cls.overlapping(topology).select_related('organization')
示例#9
0
class Trek(PicturesMixin, MapEntityMixin, Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    name = models.CharField(verbose_name=_(u"Name"),
                            max_length=128,
                            help_text=_(u"Public name (Change carefully)"),
                            db_column='nom')
    departure = models.CharField(verbose_name=_(u"Departure"),
                                 max_length=128,
                                 blank=True,
                                 help_text=_(u"Departure description"),
                                 db_column='depart')
    arrival = models.CharField(verbose_name=_(u"Arrival"),
                               max_length=128,
                               blank=True,
                               help_text=_(u"Arrival description"),
                               db_column='arrivee')
    published = models.BooleanField(verbose_name=_(u"Published"),
                                    help_text=_(u"Online"),
                                    db_column='public')
    description_teaser = models.TextField(
        verbose_name=_(u"Description teaser"),
        blank=True,
        help_text=_(u"A brief summary (map pop-ups)"),
        db_column='chapeau')
    description = models.TextField(verbose_name=_(u"Description"),
                                   blank=True,
                                   db_column='description',
                                   help_text=_(u"Complete description"))
    ambiance = models.TextField(verbose_name=_(u"Ambiance"),
                                blank=True,
                                db_column='ambiance',
                                help_text=_(u"Main attraction and interest"))
    access = models.TextField(verbose_name=_(u"Access"),
                              blank=True,
                              db_column='acces',
                              help_text=_(u"Best way to go"))
    disabled_infrastructure = models.TextField(
        verbose_name=_(u"Disabled infrastructure"),
        db_column='handicap',
        blank=True,
        help_text=_(u"Any specific infrastructure"))
    duration = models.FloatField(verbose_name=_(u"Duration"),
                                 default=0,
                                 blank=True,
                                 null=True,
                                 db_column='duree',
                                 help_text=_(u"In hours"))
    is_park_centered = models.BooleanField(
        verbose_name=_(u"Is in the midst of the park"),
        db_column='coeur',
        help_text=_(u"Crosses center of park"))
    advised_parking = models.CharField(verbose_name=_(u"Advised parking"),
                                       max_length=128,
                                       blank=True,
                                       db_column='parking',
                                       help_text=_(u"Where to park"))
    parking_location = models.PointField(verbose_name=_(u"Parking location"),
                                         db_column='geom_parking',
                                         srid=settings.SRID,
                                         spatial_index=False,
                                         blank=True,
                                         null=True)
    public_transport = models.TextField(
        verbose_name=_(u"Public transport"),
        blank=True,
        db_column='transport',
        help_text=_(u"Train, bus (see web links)"))
    advice = models.TextField(verbose_name=_(u"Advice"),
                              blank=True,
                              db_column='recommandation',
                              help_text=_(u"Risks, danger, best period, ..."))
    themes = models.ManyToManyField('Theme',
                                    related_name="treks",
                                    db_table="o_r_itineraire_theme",
                                    blank=True,
                                    null=True,
                                    verbose_name=_(u"Themes"))
    networks = models.ManyToManyField('TrekNetwork',
                                      related_name="treks",
                                      db_table="o_r_itineraire_reseau",
                                      blank=True,
                                      null=True,
                                      verbose_name=_(u"Networks"))
    usages = models.ManyToManyField('Usage',
                                    related_name="treks",
                                    db_table="o_r_itineraire_usage",
                                    blank=True,
                                    null=True,
                                    verbose_name=_(u"Usages"))
    route = models.ForeignKey('Route',
                              related_name='treks',
                              blank=True,
                              null=True,
                              verbose_name=_(u"Route"),
                              db_column='parcours')
    difficulty = models.ForeignKey('DifficultyLevel',
                                   related_name='treks',
                                   blank=True,
                                   null=True,
                                   verbose_name=_(u"Difficulty"),
                                   db_column='difficulte')
    web_links = models.ManyToManyField('WebLink',
                                       related_name="treks",
                                       db_table="o_r_itineraire_web",
                                       blank=True,
                                       null=True,
                                       verbose_name=_(u"Web links"))
    related_treks = models.ManyToManyField(
        'self',
        through='TrekRelationship',
        verbose_name=_(u"Related treks"),
        symmetrical=False,
        related_name='related_treks+')  # Hide reverse attribute
    information_desk = models.ForeignKey('InformationDesk',
                                         related_name='treks',
                                         blank=True,
                                         null=True,
                                         verbose_name=_(u"Information Desk"),
                                         db_column='renseignement')

    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'o_t_itineraire'
        verbose_name = _(u"Trek")
        verbose_name_plural = _(u"Treks")

    @property
    def slug(self):
        return slugify(self.name)

    @models.permalink
    def get_document_public_url(self):
        return ('trekking:trek_document_public', [str(self.pk)])

    @models.permalink
    def get_document_public_poi_url(self):
        return ('trekking:trek_document_public_poi', [str(self.pk)])

    @property
    def related(self):
        return self.related_treks.exclude(deleted=True).exclude(
            pk=self.pk).distinct()

    @property
    def relationships(self):
        # Does not matter if a or b
        return TrekRelationship.objects.filter(trek_a=self)

    @property
    def poi_types(self):
        pks = set(self.pois.values_list('type', flat=True))
        return POIType.objects.filter(pk__in=pks)

    def prepare_map_image(self, rooturl):
        """
        We override the default behaviour of map image preparation :
        if the trek has a attached picture file with *title* ``mapimage``, we use it
        as a screenshot.
        TODO: remove this when screenshots are bullet-proof ?
        """
        attached = None
        for picture in [a for a in self.attachments.all() if a.is_image]:
            if picture.title == 'mapimage':
                attached = picture.attachment_file
                break
        if attached is None:
            super(Trek, self).prepare_map_image(rooturl)
        else:
            # Copy it along other screenshots
            src = os.path.join(settings.MEDIA_ROOT, attached.name)
            dst = self.get_map_image_path()
            shutil.copyfile(src, dst)

    def get_attachment_print(self):
        """
        Look in attachment if there is document to be used as print version
        """
        overriden = self.attachments.filter(title="docprint").get()
        # Must have OpenOffice document mimetype
        if overriden.mimetype != [
                'application', 'vnd.oasis.opendocument.text'
        ]:
            raise overriden.DoesNotExist()
        return os.path.join(settings.MEDIA_ROOT,
                            overriden.attachment_file.name)

    @property
    def serializable_relationships(self):
        return [{
            'has_common_departure': rel.has_common_departure,
            'has_common_edge': rel.has_common_edge,
            'is_circuit_step': rel.is_circuit_step,
            'trek': {
                'pk':
                rel.trek_b.pk,
                'slug':
                rel.trek_b.slug,
                'name':
                rel.trek_b.name,
                'url':
                reverse('trekking:trek_json_detail', args=(rel.trek_b.pk, )),
            },
            'published': rel.trek_b.published
        } for rel in self.relationships]

    @property
    def serializable_cities(self):
        return [{'code': city.code, 'name': city.name} for city in self.cities]

    @property
    def serializable_networks(self):
        return [{
            'id': network.id,
            'name': network.network
        } for network in self.networks.all()]

    @property
    def serializable_difficulty(self):
        if not self.difficulty:
            return None
        return {'id': self.difficulty.pk, 'label': self.difficulty.difficulty}

    @property
    def serializable_information_desk(self):
        if not self.information_desk:
            return None
        return {
            'id': self.information_desk.pk,
            'name': self.information_desk.name,
            'description': self.information_desk.description
        }

    @property
    def serializable_themes(self):
        return [{
            'id': t.pk,
            'pictogram': os.path.join(settings.MEDIA_URL, t.pictogram.name),
            'label': t.label
        } for t in self.themes.all()]

    @property
    def serializable_usages(self):
        return [{
            'id': u.pk,
            'pictogram': os.path.join(settings.MEDIA_URL, u.pictogram.name),
            'label': u.usage
        } for u in self.usages.all()]

    @property
    def serializable_districts(self):
        return [{'id': d.pk, 'name': d.name} for d in self.districts]

    @property
    def serializable_route(self):
        if not self.route:
            return None
        return {'id': self.route.pk, 'label': self.route.route}

    @property
    def serializable_web_links(self):
        return [{
            'id': w.pk,
            'name': w.name,
            'category': w.serializable_category,
            'url': w.url
        } for w in self.web_links.all()]

    @property
    def serializable_parking_location(self):
        if not self.parking_location:
            return None
        return self.parking_location.transform(settings.API_SRID,
                                               clone=True).coords

    @property
    def name_display(self):
        s = u'<a data-pk="%s" href="%s" >%s</a>' % (
            self.pk, self.get_detail_url(), self.name)
        if self.published:
            s = u'<span class="badge badge-success" title="%s">&#x2606;</span> ' % _(
                "Published") + s
        return s

    @property
    def name_csv_display(self):
        return unicode(self.name)

    @property
    def length_kilometer(self):
        return "%.1f" % (self.length / 1000.0)

    @property
    def networks_display(self):
        return ', '.join([unicode(n) for n in self.networks.all()])

    @property
    def districts_display(self):
        return ', '.join([unicode(d) for d in self.districts])

    @property
    def themes_display(self):
        return ', '.join([unicode(n) for n in self.themes.all()])

    @property
    def usages_display(self):
        return ', '.join([unicode(n) for n in self.usages.all()])

    def kml(self):
        """ Exports trek into KML format, add geometry as linestring and POI
        as place marks """
        kml = simplekml.Kml()
        # Main itinerary
        geom = self.geom.transform(4326, clone=True)  # KML uses WGS84
        line = kml.newlinestring(name=self.name,
                                 description=plain_text(self.description),
                                 coords=geom.coords)
        line.style.linestyle.color = simplekml.Color.red  # Red
        line.style.linestyle.width = 4  # pixels
        # Place marks
        for poi in self.pois:
            place = poi.geom_as_point()
            place.transform(settings.API_SRID)
            kml.newpoint(name=poi.name,
                         description=plain_text(poi.description),
                         coords=[place.coords])
        return kml._genkml()

    def is_complete(self):
        """It should also have a description, etc.
        """
        mandatory = ['departure', 'arrival', 'description_teaser']
        for f in mandatory:
            if not getattr(self, f):
                return False
        return True

    def has_geom_valid(self):
        """A trek should be a LineString, even if it's a loop.
        """
        return (self.geom is not None
                and self.geom.geom_type.lower() == 'linestring')

    def is_publishable(self):
        return self.is_complete() and self.has_geom_valid()

    @property
    def duration_pretty(self):
        return trekking_tags.duration(self.duration)

    def __unicode__(self):
        return u"%s (%s - %s)" % (self.name, self.departure, self.arrival)

    @classmethod
    def path_treks(cls, path):
        return cls.objects.existing().filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_treks(cls, topology):
        return cls.overlapping(topology)
示例#10
0
class Service(StructureRelated, MapEntityMixin, Topology):

    topo_object = models.OneToOneField(Topology, parent_link=True,
                                       db_column='evenement')
    type = models.ForeignKey('ServiceType', related_name='services', verbose_name=_(u"Type"), db_column='type')
    eid = models.CharField(verbose_name=_(u"External id"), max_length=128, blank=True, null=True, db_column='id_externe')

    class Meta:
        db_table = 'o_t_service'
        verbose_name = _(u"Service")
        verbose_name_plural = _(u"Services")

    # Override default manager
    objects = Topology.get_manager_cls(ServiceManager)()

    def __unicode__(self):
        return unicode(self.type)

    @property
    def name(self):
        return self.type.name

    @property
    def name_display(self):
        s = u'<a data-pk="%s" href="%s" title="%s">%s</a>' % (self.pk,
                                                              self.get_detail_url(),
                                                              self.name,
                                                              self.name)
        if self.type.published:
            s = u'<span class="badge badge-success" title="%s">&#x2606;</span> ' % _("Published") + s
        elif self.type.review:
            s = u'<span class="badge badge-warning" title="%s">&#x2606;</span> ' % _("Waiting for publication") + s
        return s

    @classproperty
    def name_verbose_name(cls):
        return _("Type")

    @property
    def type_display(self):
        return unicode(self.type)

    @property
    def serializable_type(self):
        return {'label': self.type.label,
                'pictogram': self.type.get_pictogram_url()}

    @classmethod
    def path_services(cls, path):
        return cls.objects.existing().filter(aggregations__path=path).distinct('pk')

    @classmethod
    def topology_services(cls, topology):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            qs = cls.overlapping(topology)
        else:
            area = topology.geom.buffer(settings.TREK_POI_INTERSECTION_MARGIN)
            qs = cls.objects.existing().filter(geom__intersects=area)
        if isinstance(topology, Trek):
            qs = qs.filter(type__practices=topology.practice)
        return qs

    @classmethod
    def published_topology_services(cls, topology):
        return cls.topology_services(topology).filter(type__published=True)

    def distance(self, to_cls):
        return settings.TOURISM_INTERSECTION_MARGIN
示例#11
0
class POI(PicturesMixin, MapEntityMixin, Topology):

    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    name = models.CharField(verbose_name=_(u"Name"),
                            max_length=128,
                            db_column='nom',
                            help_text=_(u"Official name"))
    description = models.TextField(verbose_name=_(u"Description"),
                                   db_column='description',
                                   help_text=_(u"History, details,  ..."))
    type = models.ForeignKey('POIType',
                             related_name='pois',
                             verbose_name=_(u"Type"),
                             db_column='type')

    class Meta:
        db_table = 'o_t_poi'
        verbose_name = _(u"POI")
        verbose_name_plural = _(u"POI")

    # Override default manager
    objects = Topology.get_manager_cls(POIManager)()

    def __unicode__(self):
        return u"%s (%s)" % (self.name, self.type)

    def save(self, *args, **kwargs):
        super(POI, self).save(*args, **kwargs)
        # Invalidate treks map
        for trek in self.treks.all():
            try:
                os.remove(trek.get_map_image_path())
            except OSError:
                pass

    @property
    def type_display(self):
        return unicode(self.type)

    @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 serializable_type(self):
        return {
            'label': self.type.label,
            'pictogram': self.type.serializable_pictogram
        }

    @classmethod
    def path_pois(cls, path):
        return cls.objects.filter(aggregations__path=path).distinct('pk')

    @classmethod
    def topology_pois(cls, topology):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            qs = cls.overlapping(topology)
        else:
            area = topology.geom.buffer(settings.TREK_POI_INTERSECTION_MARGIN)
            qs = cls.objects.filter(geom__intersects=area)
        return qs
示例#12
0
class Trek(StructureRelated, PicturesMixin, PublishableMixin, MapEntityMixin, Topology):
    topo_object = models.OneToOneField(Topology, parent_link=True,
                                       db_column='evenement')
    departure = models.CharField(verbose_name=_(u"Departure"), max_length=128, blank=True,
                                 help_text=_(u"Departure description"), db_column='depart')
    arrival = models.CharField(verbose_name=_(u"Arrival"), max_length=128, blank=True,
                               help_text=_(u"Arrival description"), db_column='arrivee')
    description_teaser = models.TextField(verbose_name=_(u"Description teaser"), blank=True,
                                          help_text=_(u"A brief summary (map pop-ups)"), db_column='chapeau')
    description = models.TextField(verbose_name=_(u"Description"), blank=True, db_column='description',
                                   help_text=_(u"Complete description"))
    ambiance = models.TextField(verbose_name=_(u"Ambiance"), blank=True, db_column='ambiance',
                                help_text=_(u"Main attraction and interest"))
    access = models.TextField(verbose_name=_(u"Access"), blank=True, db_column='acces',
                              help_text=_(u"Best way to go"))
    disabled_infrastructure = models.TextField(verbose_name=_(u"Disabled infrastructure"), db_column='handicap',
                                               blank=True, help_text=_(u"Any specific infrastructure"))
    duration = models.FloatField(verbose_name=_(u"Duration"), default=0, blank=True, db_column='duree',
                                 help_text=_(u"In hours (1.5 = 1 h 30, 24 = 1 day, 48 = 2 days)"),
                                 validators=[MinValueValidator(0)])
    is_park_centered = models.BooleanField(verbose_name=_(u"Is in the midst of the park"), db_column='coeur',
                                           help_text=_(u"Crosses center of park"))
    advised_parking = models.CharField(verbose_name=_(u"Advised parking"), max_length=128, blank=True, db_column='parking',
                                       help_text=_(u"Where to park"))
    parking_location = models.PointField(verbose_name=_(u"Parking location"), db_column='geom_parking',
                                         srid=settings.SRID, spatial_index=False, blank=True, null=True)
    public_transport = models.TextField(verbose_name=_(u"Public transport"), blank=True, db_column='transport',
                                        help_text=_(u"Train, bus (see web links)"))
    advice = models.TextField(verbose_name=_(u"Advice"), blank=True, db_column='recommandation',
                              help_text=_(u"Risks, danger, best period, ..."))
    themes = models.ManyToManyField(Theme, related_name="treks",
                                    db_table="o_r_itineraire_theme", blank=True, null=True, verbose_name=_(u"Themes"),
                                    help_text=_(u"Main theme(s)"))
    networks = models.ManyToManyField('TrekNetwork', related_name="treks",
                                      db_table="o_r_itineraire_reseau", blank=True, null=True, verbose_name=_(u"Networks"),
                                      help_text=_(u"Hiking networks"))
    practice = models.ForeignKey('Practice', related_name="treks",
                                 blank=True, null=True, verbose_name=_(u"Practice"), db_column='pratique')
    accessibilities = models.ManyToManyField('Accessibility', related_name="treks",
                                             db_table="o_r_itineraire_accessibilite", blank=True, null=True,
                                             verbose_name=_(u"Accessibility"))
    route = models.ForeignKey('Route', related_name='treks',
                              blank=True, null=True, verbose_name=_(u"Route"), db_column='parcours')
    difficulty = models.ForeignKey('DifficultyLevel', related_name='treks',
                                   blank=True, null=True, verbose_name=_(u"Difficulty"), db_column='difficulte')
    web_links = models.ManyToManyField('WebLink', related_name="treks",
                                       db_table="o_r_itineraire_web", blank=True, null=True, verbose_name=_(u"Web links"),
                                       help_text=_(u"External resources"))
    related_treks = models.ManyToManyField('self', through='TrekRelationship',
                                           verbose_name=_(u"Related treks"), symmetrical=False,
                                           help_text=_(u"Connections between treks"),
                                           related_name='related_treks+')  # Hide reverse attribute
    information_desks = models.ManyToManyField(tourism_models.InformationDesk, related_name='treks',
                                               db_table="o_r_itineraire_renseignement", blank=True, null=True,
                                               verbose_name=_(u"Information desks"),
                                               help_text=_(u"Where to obtain information"))
    points_reference = models.MultiPointField(verbose_name=_(u"Points of reference"), db_column='geom_points_reference',
                                              srid=settings.SRID, spatial_index=False, blank=True, null=True)
    source = models.ManyToManyField('common.RecordSource',
                                    blank=True, related_name='treks',
                                    verbose_name=_("Source"), db_table='o_r_itineraire_source')
    portal = models.ManyToManyField('common.TargetPortal',
                                    blank=True, related_name='treks',
                                    verbose_name=_("Portal"), db_table='o_r_itineraire_portal')
    eid = models.CharField(verbose_name=_(u"External id"), max_length=128, blank=True, null=True, db_column='id_externe')
    eid2 = models.CharField(verbose_name=_(u"Second external id"), max_length=128, blank=True, null=True, db_column='id_externe2')

    objects = Topology.get_manager_cls(models.GeoManager)()

    category_id_prefix = 'T'
    capture_map_image_waitfor = '.poi_enum_loaded.services_loaded.info_desks_loaded.ref_points_loaded'

    class Meta:
        db_table = 'o_t_itineraire'
        verbose_name = _(u"Trek")
        verbose_name_plural = _(u"Treks")
        ordering = ['name']

    def __unicode__(self):
        return self.name

    @models.permalink
    def get_map_image_url(self):
        return ('trekking:trek_map_image', [], {'pk': str(self.pk), 'lang': get_language()})

    def get_map_image_path(self):
        basefolder = os.path.join(settings.MEDIA_ROOT, 'maps')
        if not os.path.exists(basefolder):
            os.makedirs(basefolder)
        return os.path.join(basefolder, '%s-%s-%s.png' % (self._meta.module_name, self.pk, get_language()))

    @models.permalink
    def get_document_public_url(self):
        """ Override ``geotrek.common.mixins.PublishableMixin``
        """
        return ('trekking:trek_document_public', [], {'lang': get_language(), 'pk': self.pk, 'slug': self.slug})

    @property
    def related(self):
        return self.related_treks.exclude(deleted=True).exclude(pk=self.pk).distinct()

    @classproperty
    def related_verbose_name(cls):
        return _("Related treks")

    @property
    def relationships(self):
        # Does not matter if a or b
        return TrekRelationship.objects.filter(trek_a=self)

    @property
    def published_relationships(self):
        return self.relationships.filter(trek_b__published=True)

    @property
    def poi_types(self):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            # Can't use values_list and must add 'ordering' because of bug:
            # https://code.djangoproject.com/ticket/14930
            values = self.pois.values('ordering', 'type')
        else:
            values = self.pois.values('type')
        pks = [value['type'] for value in values]
        return POIType.objects.filter(pk__in=set(pks))

    @property
    def length_kilometer(self):
        return "%.1f" % (self.length / 1000.0)

    @property
    def networks_display(self):
        return ', '.join([unicode(n) for n in self.networks.all()])

    @property
    def districts_display(self):
        return ', '.join([unicode(d) for d in self.districts])

    @property
    def themes_display(self):
        return ', '.join([unicode(n) for n in self.themes.all()])

    @property
    def city_departure(self):
        cities = self.cities
        return unicode(cities[0]) if len(cities) > 0 else ''

    def kml(self):
        """ Exports trek into KML format, add geometry as linestring and POI
        as place marks """
        kml = simplekml.Kml()
        # Main itinerary
        geom3d = self.geom_3d.transform(4326, clone=True)  # KML uses WGS84
        line = kml.newlinestring(name=self.name,
                                 description=plain_text(self.description),
                                 coords=geom3d.coords)
        line.style.linestyle.color = simplekml.Color.red  # Red
        line.style.linestyle.width = 4  # pixels
        # Place marks
        for poi in self.pois:
            place = poi.geom_3d.transform(settings.API_SRID, clone=True)
            kml.newpoint(name=poi.name,
                         description=plain_text(poi.description),
                         coords=[place.coords])
        return kml.kml()

    def has_geom_valid(self):
        """A trek should be a LineString, even if it's a loop.
        """
        return super(Trek, self).has_geom_valid() and self.geom.geom_type.lower() == 'linestring'

    @property
    def duration_pretty(self):
        return trekking_tags.duration(self.duration)

    @classproperty
    def duration_pretty_verbose_name(cls):
        return _("Formated duration")

    @classmethod
    def path_treks(cls, path):
        treks = cls.objects.existing().filter(aggregations__path=path)
        # The following part prevents conflict with default trek ordering
        # ProgrammingError: SELECT DISTINCT ON expressions must match initial ORDER BY expressions
        return treks.order_by('topo_object').distinct('topo_object')

    @classmethod
    def topology_treks(cls, topology):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            qs = cls.overlapping(topology)
        else:
            area = topology.geom.buffer(settings.TREK_POI_INTERSECTION_MARGIN)
            qs = cls.objects.existing().filter(geom__intersects=area)
        return qs

    @classmethod
    def published_topology_treks(cls, topology):
        return cls.topology_treks(topology).filter(published=True)

    # Rando v1 compat
    @property
    def usages(self):
        return [self.practice] if self.practice else []

    @classmethod
    def get_create_label(cls):
        return _(u"Add a new trek")

    @property
    def parents(self):
        return Trek.objects.filter(trek_children__child=self, deleted=False)

    @property
    def parents_id(self):
        parents = self.trek_parents.values_list('parent__id', flat=True)
        return list(parents)

    @property
    def children(self):
        return Trek.objects.filter(trek_parents__parent=self, deleted=False).order_by('trek_parents__order')

    @property
    def children_id(self):
        """
        Get children IDs
        """
        children = self.trek_children.order_by('order')\
                                     .values_list('child__id',
                                                  flat=True)
        return list(children)

    def previous_id_for(self, parent):
        children_id = parent.children_id
        index = children_id.index(self.id)
        if index == 0:
            return None
        return children_id[index - 1]

    def next_id_for(self, parent):
        children_id = parent.children_id
        index = children_id.index(self.id)
        if index == len(children_id) - 1:
            return None
        return children_id[index + 1]

    @property
    def previous_id(self):
        """
        Dict of parent -> previous child
        """
        return {parent.id: self.previous_id_for(parent) for parent in self.parents.filter(published=True, deleted=False)}

    @property
    def next_id(self):
        """
        Dict of parent -> next child
        """
        return {parent.id: self.next_id_for(parent) for parent in self.parents.filter(published=True, deleted=False)}

    def clean(self):
        """
        Custom model validation
        """
        if self.pk in self.trek_children.values_list('child__id', flat=True):
            raise ValidationError(_(u"Cannot use itself as child trek."))

    @property
    def prefixed_category_id(self):
        if settings.SPLIT_TREKS_CATEGORIES_BY_PRACTICE and self.practice:
            return '{prefix}{id}'.format(prefix=self.category_id_prefix, id=self.practice.id)
        else:
            return self.category_id_prefix

    def distance(self, to_cls):
        if self.practice and self.practice.distance is not None:
            return self.practice.distance
        else:
            return settings.TOURISM_INTERSECTION_MARGIN

    def is_public(self):
        for parent in self.parents:
            if parent.any_published:
                return True
        return self.any_published

    @property
    def picture_print(self):
        picture = super(Trek, self).picture_print
        if picture:
            return picture
        for poi in self.published_pois:
            picture = poi.picture_print
            if picture:
                return picture

    def save(self, *args, **kwargs):
        if self.pk is not None and kwargs.get('update_fields', None) is None:
            field_names = set()
            for field in self._meta.concrete_fields:
                if not field.primary_key and not hasattr(field, 'through'):
                    field_names.add(field.attname)
            old_trek = Trek.objects.get(pk=self.pk)
            if self.geom is not None and old_trek.geom.equals_exact(self.geom, tolerance=0.00001):
                field_names.remove('geom')
            if self.geom_3d is not None and old_trek.geom_3d.equals_exact(self.geom_3d, tolerance=0.00001):
                field_names.remove('geom_3d')
            return super(Trek, self).save(update_fields=field_names, *args, **kwargs)
        super(Trek, self).save(*args, **kwargs)
示例#13
0
class Dive(AddPropertyMixin, PublishableMixin, MapEntityMixin, StructureRelated,
           TimeStampedModelMixin, PicturesMixin, NoDeleteMixin):
    description_teaser = models.TextField(verbose_name=_("Description teaser"), blank=True,
                                          help_text=_("A brief summary"), db_column='chapeau')
    description = models.TextField(verbose_name=_("Description"), blank=True, db_column='description',
                                   help_text=_("Complete description"))
    owner = models.CharField(verbose_name=_("Owner"), max_length=256, blank=True, db_column='proprietaire')
    practice = models.ForeignKey(Practice, related_name="dives",
                                 blank=True, null=True, verbose_name=_("Practice"), db_column='pratique')
    departure = models.CharField(verbose_name=_("Departure area"), max_length=128, blank=True,
                                 db_column='depart')
    disabled_sport = models.TextField(verbose_name=_("Disabled sport accessibility"),
                                      db_column='handicap', blank=True)
    facilities = models.TextField(verbose_name=_("Facilities"), db_column='equipements', blank=True)
    difficulty = models.ForeignKey(Difficulty, related_name='dives', blank=True,
                                   null=True, verbose_name=_("Difficulty level"), db_column='difficulte')
    levels = models.ManyToManyField(Level, related_name='dives', blank=True,
                                    verbose_name=_("Technical levels"), db_table='g_r_plongee_niveau')
    depth = models.PositiveIntegerField(verbose_name=_("Maximum depth"), db_column='profondeur',
                                        blank=True, null=True, help_text=_("meters"))
    advice = models.TextField(verbose_name=_("Advice"), blank=True, db_column='recommandation',
                              help_text=_("Risks, danger, best period, ..."))
    themes = models.ManyToManyField(Theme, related_name="dives",
                                    db_table="g_r_plongee_theme", blank=True, verbose_name=_("Themes"),
                                    help_text=_("Main theme(s)"))
    geom = models.GeometryField(verbose_name=_("Location"), srid=settings.SRID)
    source = models.ManyToManyField('common.RecordSource',
                                    blank=True, related_name='dives',
                                    verbose_name=_("Source"), db_table='g_r_plongee_source')
    portal = models.ManyToManyField('common.TargetPortal',
                                    blank=True, related_name='dives',
                                    verbose_name=_("Portal"), db_table='g_r_plongee_portal')
    eid = models.CharField(verbose_name=_("External id"), max_length=1024, blank=True, null=True, db_column='id_externe')

    objects = Topology.get_manager_cls(models.GeoManager)()

    category_id_prefix = 'D'

    class Meta:
        db_table = 'g_t_plongee'
        verbose_name = _("Dive")
        verbose_name_plural = _("Dives")

    def __str__(self):
        return self.name

    @property
    def rando_url(self):
        if settings.SPLIT_DIVES_CATEGORIES_BY_PRACTICE and self.practice:
            category_slug = self.practice.slug
        else:
            category_slug = _('dive')
        return '{}/{}/'.format(category_slug, self.slug)

    @property
    def display_geom(self):
        return format_coordinates(self.geom)

    def distance(self, to_cls):
        return settings.DIVING_INTERSECTION_MARGIN

    @property
    def prefixed_category_id(self):
        if settings.SPLIT_DIVES_CATEGORIES_BY_PRACTICE and self.practice:
            return '{prefix}{id}'.format(prefix=self.category_id_prefix, id=self.practice.id)
        else:
            return self.category_id_prefix

    def get_map_image_url(self):
        return reverse('diving:dive_map_image', args=[str(self.pk), get_language()])

    @classmethod
    def get_create_label(cls):
        return _("Add a new dive")
示例#14
0
class POI(StructureRelated, PicturesMixin, PublishableMixin, MapEntityMixin,
          Topology):

    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    description = models.TextField(verbose_name=_("Description"),
                                   db_column='description',
                                   blank=True,
                                   help_text=_("History, details,  ..."))
    type = models.ForeignKey('POIType',
                             related_name='pois',
                             verbose_name=_("Type"),
                             db_column='type')
    eid = models.CharField(verbose_name=_("External id"),
                           max_length=1024,
                           blank=True,
                           null=True,
                           db_column='id_externe')

    class Meta:
        db_table = 'o_t_poi'
        verbose_name = _("POI")
        verbose_name_plural = _("POI")

    # Override default manager
    objects = Topology.get_manager_cls(POIManager)()

    # Do no check structure when selecting POIs to exclude
    check_structure_in_forms = False

    def __str__(self):
        return "%s (%s)" % (self.name, self.type)

    def save(self, *args, **kwargs):
        super(POI, self).save(*args, **kwargs)
        # Invalidate treks map
        for trek in self.treks.all():
            try:
                os.remove(trek.get_map_image_path())
            except OSError:
                pass

    @property
    def type_display(self):
        return str(self.type)

    @property
    def serializable_type(self):
        return {
            'label': self.type.label,
            'pictogram': self.type.get_pictogram_url()
        }

    @classmethod
    def path_pois(cls, path):
        return cls.objects.existing().filter(
            aggregations__path=path).distinct('pk')

    @classmethod
    def topology_pois(cls, topology):
        return cls.exclude_pois(cls.topology_all_pois(topology), topology)

    @classmethod
    def topology_all_pois(cls, topology):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            qs = cls.overlapping(topology)
        else:
            object_geom = topology.geom.transform(
                settings.SRID,
                clone=True).buffer(settings.TREK_POI_INTERSECTION_MARGIN)
            qs = cls.objects.existing().filter(geom__intersects=object_geom)
            if topology.geom.geom_type == 'LineString':
                qs = qs.annotate(locate=LineLocatePoint(
                    Transform(
                        Value(topology.geom.ewkt,
                              output_field=models.GeometryField()),
                        settings.SRID), Transform(F('geom'), settings.SRID)))
                qs = qs.order_by('locate')

        return qs

    @classmethod
    def published_topology_pois(cls, topology):
        return cls.topology_pois(topology).filter(published=True)

    def distance(self, to_cls):
        return settings.TOURISM_INTERSECTION_MARGIN

    @classmethod
    def exclude_pois(cls, qs, topology):
        try:
            return qs.exclude(pk__in=topology.trek.pois_excluded.values_list(
                'pk', flat=True))
        except Trek.DoesNotExist:
            return qs

    @property
    def extent(self):
        return self.geom.transform(settings.API_SRID,
                                   clone=True).extent if self.geom else None
示例#15
0
class Project(MapEntityMixin, TimeStampedModel, 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, ..."))
    cost = models.FloatField(verbose_name=_(u"Cost"),
                             default=0,
                             db_column='cout',
                             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 = Topology.get_manager_cls()()

    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():
                if p.trail:
                    s.append(p.trail)
        return Trail.objects.filter(pk__in=[t.pk for t in set(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" >%s</a>' % (
            self.pk, self.get_detail_url(), self.name)

    @property
    def name_csv_display(self):
        return unicode(self.name)

    @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")

    def __unicode__(self):
        deleted_text = u"[" + _(u"Deleted") + u"]" if self.deleted else ""
        return u"%s (%s-%s) %s" % (self.name, self.begin_year, self.end_year,
                                   deleted_text)

    @classmethod
    def path_projects(cls, path):
        return cls.objects.filter(
            interventions__in=path.interventions).distinct()

    @classmethod
    def trail_projects(cls, trail):
        return cls.objects.filter(
            interventions__in=trail.interventions).distinct()

    @classmethod
    def topology_projects(cls, topology):
        return cls.objects.filter(
            interventions__in=topology.interventions).distinct()

    def edges_by_attr(self, interventionattr):
        pks = []
        for i in self.interventions.all():
            pks += getattr(i, interventionattr).values_list('pk', flat=True)
        return Topology.objects.filter(pk__in=pks)
示例#16
0
class Intervention(MapEntityMixin, AltimetryMixin, TimeStampedModel,
                   StructureRelated, NoDeleteMixin):

    in_maintenance = models.BooleanField(
        verbose_name=_(u"Recurrent intervention"),
        db_column='maintenance',
        help_text=_(u"Recurrent"))
    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 ?"))
    comments = models.TextField(blank=True,
                                verbose_name=_(u"Comments"),
                                db_column='commentaire',
                                help_text=_(u"Remarks and notes"))

    ## 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 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.

    stake = models.ForeignKey('core.Stake',
                              null=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"))

    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')

    # Special manager
    objects = Topology.get_manager_cls()()

    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_infrastructure(self, baseinfra):
        self.topology = baseinfra
        if not self.on_infrastructure:
            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, fromdb=None):
        if self.pk:
            fromdb = self.__class__.objects.get(pk=self.pk)
            self.area = fromdb.area
            AltimetryMixin.reload(self, fromdb)
            TimeStampedModel.reload(self, fromdb)
            NoDeleteMixin.reload(self, fromdb)
        return self

    def save(self, *args, **kwargs):
        if self.stake is None:
            self.stake = self.default_stake()
        super(Intervention, self).save(*args, **kwargs)
        self.reload()

    @property
    def on_infrastructure(self):
        return self.is_infrastructure or self.is_signage

    @property
    def infrastructure(self):
        """
        Equivalent of topology attribute, but casted to related type (Infrastructure or Signage)
        """
        if self.on_infrastructure:
            if self.is_signage:
                return self.signages[0]
            if self.is_infrastructure:
                return self.infrastructures[0]
        return None

    @classproperty
    def infrastructure_verbose_name(cls):
        return _("On")

    @property
    def infrastructure_display(self):
        if self.on_infrastructure:
            return '<img src="%simages/%s-16.png" title="%s">' % (
                settings.STATIC_URL, self.topology.kind.lower(),
                unicode(_(self.topology.kind)))
        return ''

    @property
    def infrastructure_csv_display(self):
        if self.on_infrastructure:
            return unicode(self.infrastructure)
        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 []

    @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

    @property
    def total_cost(self):
        total = 0.0
        for md in self.manday_set.all():
            total += md.cost
        return total

    @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" >%s</a>' % (
            self.pk, self.get_detail_url(), 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 trail_interventions(cls, trail):
        """ Interventions of a trail is the union of interventions on all its paths """
        return cls.objects.existing().filter(
            topology__aggregations__path__trail=trail)

    @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')
示例#17
0
class POI(StructureRelated, PicturesMixin, PublishableMixin, MapEntityMixin, Topology):

    topo_object = models.OneToOneField(Topology, parent_link=True,
                                       db_column='evenement')
    description = models.TextField(verbose_name=_(u"Description"), db_column='description',
                                   help_text=_(u"History, details,  ..."))
    type = models.ForeignKey('POIType', related_name='pois', verbose_name=_(u"Type"), db_column='type')
    eid = models.CharField(verbose_name=_(u"External id"), max_length=128, blank=True, null=True, db_column='id_externe')

    class Meta:
        db_table = 'o_t_poi'
        verbose_name = _(u"POI")
        verbose_name_plural = _(u"POI")

    # Override default manager
    objects = Topology.get_manager_cls(POIManager)()

    def __unicode__(self):
        return u"%s (%s)" % (self.name, self.type)

    @models.permalink
    def get_document_public_url(self):
        """ Override ``geotrek.common.mixins.PublishableMixin``
        """
        return ('trekking:poi_document_public', [], {'lang': get_language(), 'pk': self.pk, 'slug': self.slug})

    def save(self, *args, **kwargs):
        super(POI, self).save(*args, **kwargs)
        # Invalidate treks map
        for trek in self.treks.all():
            try:
                os.remove(trek.get_map_image_path())
            except OSError:
                pass

    @property
    def type_display(self):
        return unicode(self.type)

    @property
    def serializable_type(self):
        return {'label': self.type.label,
                'pictogram': self.type.get_pictogram_url()}

    @classmethod
    def path_pois(cls, path):
        return cls.objects.existing().filter(aggregations__path=path).distinct('pk')

    @classmethod
    def topology_pois(cls, topology):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            qs = cls.overlapping(topology)
        else:
            area = topology.geom.buffer(settings.TREK_POI_INTERSECTION_MARGIN)
            qs = cls.objects.existing().filter(geom__intersects=area)
        return qs

    @classmethod
    def published_topology_pois(cls, topology):
        return cls.topology_pois(topology).filter(published=True)

    def distance(self, to_cls):
        return settings.TOURISM_INTERSECTION_MARGIN
示例#18
0
class Trek(PicturesMixin, MapEntityMixin, Topology):
    topo_object = models.OneToOneField(Topology,
                                       parent_link=True,
                                       db_column='evenement')
    name = models.CharField(verbose_name=_(u"Name"),
                            max_length=128,
                            help_text=_(u"Public name (Change carefully)"),
                            db_column='nom')
    departure = models.CharField(verbose_name=_(u"Departure"),
                                 max_length=128,
                                 blank=True,
                                 help_text=_(u"Departure description"),
                                 db_column='depart')
    arrival = models.CharField(verbose_name=_(u"Arrival"),
                               max_length=128,
                               blank=True,
                               help_text=_(u"Arrival description"),
                               db_column='arrivee')
    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_teaser = models.TextField(
        verbose_name=_(u"Description teaser"),
        blank=True,
        help_text=_(u"A brief summary (map pop-ups)"),
        db_column='chapeau')
    description = models.TextField(verbose_name=_(u"Description"),
                                   blank=True,
                                   db_column='description',
                                   help_text=_(u"Complete description"))
    ambiance = models.TextField(verbose_name=_(u"Ambiance"),
                                blank=True,
                                db_column='ambiance',
                                help_text=_(u"Main attraction and interest"))
    access = models.TextField(verbose_name=_(u"Access"),
                              blank=True,
                              db_column='acces',
                              help_text=_(u"Best way to go"))
    disabled_infrastructure = models.TextField(
        verbose_name=_(u"Disabled infrastructure"),
        db_column='handicap',
        blank=True,
        help_text=_(u"Any specific infrastructure"))
    duration = models.FloatField(verbose_name=_(u"Duration"),
                                 default=0,
                                 blank=True,
                                 db_column='duree',
                                 help_text=_(u"In hours"))
    is_park_centered = models.BooleanField(
        verbose_name=_(u"Is in the midst of the park"),
        db_column='coeur',
        help_text=_(u"Crosses center of park"))
    advised_parking = models.CharField(verbose_name=_(u"Advised parking"),
                                       max_length=128,
                                       blank=True,
                                       db_column='parking',
                                       help_text=_(u"Where to park"))
    parking_location = models.PointField(verbose_name=_(u"Parking location"),
                                         db_column='geom_parking',
                                         srid=settings.SRID,
                                         spatial_index=False,
                                         blank=True,
                                         null=True)
    public_transport = models.TextField(
        verbose_name=_(u"Public transport"),
        blank=True,
        db_column='transport',
        help_text=_(u"Train, bus (see web links)"))
    advice = models.TextField(verbose_name=_(u"Advice"),
                              blank=True,
                              db_column='recommandation',
                              help_text=_(u"Risks, danger, best period, ..."))
    themes = models.ManyToManyField('Theme',
                                    related_name="treks",
                                    db_table="o_r_itineraire_theme",
                                    blank=True,
                                    null=True,
                                    verbose_name=_(u"Themes"),
                                    help_text=_(u"Main theme(s)"))
    networks = models.ManyToManyField('TrekNetwork',
                                      related_name="treks",
                                      db_table="o_r_itineraire_reseau",
                                      blank=True,
                                      null=True,
                                      verbose_name=_(u"Networks"),
                                      help_text=_(u"Hiking networks"))
    usages = models.ManyToManyField('Usage',
                                    related_name="treks",
                                    db_table="o_r_itineraire_usage",
                                    blank=True,
                                    null=True,
                                    verbose_name=_(u"Usages"),
                                    help_text=_(u"Practicability"))
    route = models.ForeignKey('Route',
                              related_name='treks',
                              blank=True,
                              null=True,
                              verbose_name=_(u"Route"),
                              db_column='parcours')
    difficulty = models.ForeignKey('DifficultyLevel',
                                   related_name='treks',
                                   blank=True,
                                   null=True,
                                   verbose_name=_(u"Difficulty"),
                                   db_column='difficulte')
    web_links = models.ManyToManyField('WebLink',
                                       related_name="treks",
                                       db_table="o_r_itineraire_web",
                                       blank=True,
                                       null=True,
                                       verbose_name=_(u"Web links"),
                                       help_text=_(u"External resources"))
    related_treks = models.ManyToManyField(
        'self',
        through='TrekRelationship',
        verbose_name=_(u"Related treks"),
        symmetrical=False,
        help_text=_(u"Connections between treks"),
        related_name='related_treks+')  # Hide reverse attribute
    information_desks = models.ManyToManyField(
        'InformationDesk',
        db_table="o_r_itineraire_renseignement",
        blank=True,
        null=True,
        verbose_name=_(u"Information desks"),
        help_text=_(u"Where to obtain information"))
    points_reference = models.MultiPointField(
        verbose_name=_(u"Points of reference"),
        db_column='geom_points_reference',
        srid=settings.SRID,
        spatial_index=False,
        blank=True,
        null=True)

    objects = Topology.get_manager_cls(models.GeoManager)()

    class Meta:
        db_table = 'o_t_itineraire'
        verbose_name = _(u"Trek")
        verbose_name_plural = _(u"Treks")
        ordering = ['name']

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        if self.publication_date is None and self.any_published:
            self.publication_date = datetime.date.today()
        if self.publication_date is not None and not self.any_published:
            self.publication_date = None
        super(Trek, self).save(*args, **kwargs)

    @property
    def slug(self):
        return slugify(self.name)

    @models.permalink
    def get_document_public_url(self):
        return ('trekking:trek_document_public', [str(self.pk)])

    @property
    @models.permalink
    def elevation_area_url(self):
        return ('trekking:trek_elevation_area', [str(self.pk)])

    @property
    def any_published(self):
        """Returns True if the trek is published in at least one of the language
        """
        if not settings.TREK_PUBLISHED_BY_LANG:
            return self.published

        for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:
            if getattr(self, 'published_%s' % l[0], False):
                return True
        return False

    @property
    def published_status(self):
        status = []
        for l in settings.MAPENTITY_CONFIG['TRANSLATED_LANGUAGES']:
            if settings.TREK_PUBLISHED_BY_LANG:
                published = getattr(self, 'published_%s' % l[0], None) or False
            else:
                published = self.published
            status.append({
                'lang': l[0],
                'language': l[1],
                'status': published
            })
        return status

    @property
    def related(self):
        return self.related_treks.exclude(deleted=True).exclude(
            pk=self.pk).distinct()

    @property
    def relationships(self):
        # Does not matter if a or b
        return TrekRelationship.objects.filter(trek_a=self)

    @property
    def poi_types(self):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            # Can't use values_list and must add 'ordering' because of bug:
            # https://code.djangoproject.com/ticket/14930
            values = self.pois.values('ordering', 'type')
        else:
            values = self.pois.values('type')
        pks = [value['type'] for value in values]
        return POIType.objects.filter(pk__in=set(pks))

    def prepare_map_image(self, rooturl):
        """
        We override the default behaviour of map image preparation :
        if the trek has a attached picture file with *title* ``mapimage``, we use it
        as a screenshot.
        TODO: remove this when screenshots are bullet-proof ?
        """
        attached = None
        for picture in [a for a in self.attachments.all() if a.is_image]:
            if picture.title == 'mapimage':
                attached = picture.attachment_file
                break
        if attached is None:
            super(Trek, self).prepare_map_image(rooturl)
        else:
            # Copy it along other screenshots
            src = os.path.join(settings.MEDIA_ROOT, attached.name)
            dst = self.get_map_image_path()
            shutil.copyfile(src, dst)

    def get_geom_aspect_ratio(self):
        """ Force trek aspect ratio to fit height and width of
        image in public document.
        """
        s = settings.TREK_EXPORT_MAP_IMAGE_SIZE
        return float(s[0]) / s[1]

    def get_attachment_print(self):
        """
        Look in attachment if there is document to be used as print version
        """
        overriden = self.attachments.filter(title="docprint").get()
        # Must have OpenOffice document mimetype
        if overriden.mimetype != [
                'application', 'vnd.oasis.opendocument.text'
        ]:
            raise overriden.DoesNotExist()
        return os.path.join(settings.MEDIA_ROOT,
                            overriden.attachment_file.name)

    @property
    def serializable_relationships(self):
        return [{
            'has_common_departure': rel.has_common_departure,
            'has_common_edge': rel.has_common_edge,
            'is_circuit_step': rel.is_circuit_step,
            'trek': {
                'pk':
                rel.trek_b.pk,
                'slug':
                rel.trek_b.slug,
                'name':
                rel.trek_b.name,
                'url':
                reverse('trekking:trek_json_detail', args=(rel.trek_b.pk, )),
            },
            'published': rel.trek_b.published
        } for rel in self.relationships]

    @property
    def serializable_cities(self):
        return [{'code': city.code, 'name': city.name} for city in self.cities]

    @property
    def serializable_networks(self):
        return [{
            'id': network.id,
            'pictogram': network.serializable_pictogram,
            'name': network.network
        } for network in self.networks.all()]

    @property
    def serializable_difficulty(self):
        if not self.difficulty:
            return None
        return {
            'id': self.difficulty.pk,
            'pictogram': self.difficulty.serializable_pictogram,
            'label': self.difficulty.difficulty
        }

    @property
    def serializable_themes(self):
        return [{
            'id': t.pk,
            'pictogram': t.serializable_pictogram,
            'label': t.label
        } for t in self.themes.all()]

    @property
    def serializable_usages(self):
        return [{
            'id': u.pk,
            'pictogram': u.serializable_pictogram,
            'label': u.usage
        } for u in self.usages.all()]

    @property
    def serializable_districts(self):
        return [{'id': d.pk, 'name': d.name} for d in self.districts]

    @property
    def serializable_route(self):
        if not self.route:
            return None
        return {
            'id': self.route.pk,
            'pictogram': self.route.serializable_pictogram,
            'label': self.route.route
        }

    @property
    def serializable_web_links(self):
        return [{
            'id': w.pk,
            'name': w.name,
            'category': w.serializable_category,
            'url': w.url
        } for w in self.web_links.all()]

    @property
    def information_desk(self):
        """Retrocompatibily method for Geotrek-rando.
        """
        try:
            return self.information_desks.first()
        except (ValueError, InformationDesk.DoesNotExist):
            return None

    @property
    def serializable_information_desk(self):
        return self.information_desk.__json__(
        ) if self.information_desk else None

    @property
    def serializable_information_desks(self):
        return [d.__json__() for d in self.information_desks.all()]

    @property
    def serializable_parking_location(self):
        if not self.parking_location:
            return None
        return self.parking_location.transform(settings.API_SRID,
                                               clone=True).coords

    @property
    def serializable_points_reference(self):
        if not self.points_reference:
            return None
        geojson = self.points_reference.transform(settings.API_SRID,
                                                  clone=True).geojson
        return json.loads(geojson)

    @property
    def name_display(self):
        s = u'<a data-pk="%s" href="%s" title="%s">%s</a>' % (
            self.pk, self.get_detail_url(), self.name, self.name)
        if self.published:
            s = u'<span class="badge badge-success" title="%s">&#x2606;</span> ' % _(
                "Published") + s
        return s

    @property
    def name_csv_display(self):
        return unicode(self.name)

    @property
    def length_kilometer(self):
        return "%.1f" % (self.length / 1000.0)

    @property
    def networks_display(self):
        return ', '.join([unicode(n) for n in self.networks.all()])

    @property
    def districts_display(self):
        return ', '.join([unicode(d) for d in self.districts])

    @property
    def themes_display(self):
        return ', '.join([unicode(n) for n in self.themes.all()])

    @property
    def usages_display(self):
        return ', '.join([unicode(n) for n in self.usages.all()])

    @property
    def city_departure(self):
        cities = self.cities
        return unicode(cities[0]) if len(cities) > 0 else ''

    def kml(self):
        """ Exports trek into KML format, add geometry as linestring and POI
        as place marks """
        kml = simplekml.Kml()
        # Main itinerary
        geom3d = self.geom_3d.transform(4326, clone=True)  # KML uses WGS84
        line = kml.newlinestring(name=self.name,
                                 description=plain_text(self.description),
                                 coords=geom3d.coords)
        line.style.linestyle.color = simplekml.Color.red  # Red
        line.style.linestyle.width = 4  # pixels
        # Place marks
        for poi in self.pois:
            place = poi.geom_3d.transform(settings.API_SRID, clone=True)
            kml.newpoint(name=poi.name,
                         description=plain_text(poi.description),
                         coords=[place.coords])
        return kml._genkml()

    def is_complete(self):
        """It should also have a description, etc.
        """
        mandatory = settings.TREK_COMPLETENESS_FIELDS
        for f in mandatory:
            if not getattr(self, f):
                return False
        return True

    def has_geom_valid(self):
        """A trek should be a LineString, even if it's a loop.
        """
        return (self.geom is not None
                and self.geom.geom_type.lower() == 'linestring')

    def is_publishable(self):
        return self.is_complete() and self.has_geom_valid()

    @property
    def duration_pretty(self):
        return trekking_tags.duration(self.duration)

    @classmethod
    def path_treks(cls, path):
        treks = cls.objects.existing().filter(aggregations__path=path)
        # The following part prevents conflict with default trek ordering
        # ProgrammingError: SELECT DISTINCT ON expressions must match initial ORDER BY expressions
        return treks.order_by('topo_object').distinct('topo_object')

    @classmethod
    def topology_treks(cls, topology):
        if settings.TREKKING_TOPOLOGY_ENABLED:
            qs = cls.overlapping(topology)
        else:
            area = topology.geom.buffer(settings.TREK_POI_INTERSECTION_MARGIN)
            qs = cls.objects.filter(geom__intersects=area)
        return qs