Example #1
0
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]
class DocumentLinksMixin(models.Model):

    documents = models.ManyToManyField(Document, blank=True)

    class Meta:
        abstract = True
Example #3
0
class SiteVisit(AbstractAdditionalData):
    """Site visit model."""

    location_site = models.ForeignKey(LocationSite,
                                      on_delete=models.CASCADE,
                                      default=None)

    site_visit_date = models.DateField(default=timezone.now)

    time = models.DateTimeField(null=True, blank=True)

    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              null=True,
                              blank=True,
                              related_name='%(class)s_owner')

    assessor = models.ForeignKey(settings.AUTH_USER_MODEL,
                                 null=True,
                                 blank=True,
                                 related_name='%(class)s_assessor')

    water_level = models.CharField(max_length=100,
                                   choices=[(status.name,
                                             status.value[WATER_LEVEL_NAME])
                                            for status in WaterLevel],
                                   blank=True,
                                   null=True)

    channel_type = models.CharField(max_length=200,
                                    choices=[(status.name,
                                              status.value[CHANNEL_TYPE_NAME])
                                             for status in ChannelType],
                                    blank=True,
                                    null=True)

    water_turbidity = models.CharField(max_length=100,
                                       choices=[(status.name, status.value)
                                                for status in WaterTurbidity],
                                       blank=True,
                                       null=True)

    canopy_cover = models.CharField(max_length=100,
                                    choices=[(status.name, status.value)
                                             for status in CanopyCover],
                                    blank=True,
                                    null=True)

    average_velocity = models.IntegerField(null=True, blank=True)

    average_depth = models.IntegerField(null=True, blank=True)

    discharge = models.IntegerField(null=True, blank=True)

    sass_version = models.IntegerField(null=True, blank=True)

    sass_biotope_fraction = models.ManyToManyField('sass.SassBiotopeFraction',
                                                   null=True,
                                                   blank=True)

    other_biota = models.TextField(null=True, blank=True)

    comments_or_observations = models.TextField(null=True, blank=True)

    data_source = models.ForeignKey('bims.DataSource', null=True, blank=True)

    def __unicode__(self):
        return self.location_site.name
Example #4
0
class Site(ZoningPropertiesMixin, AddPropertyMixin, PublishableMixin,
           MapEntityMixin, StructureRelated, TimeStampedModelMixin, MPTTModel):
    ORIENTATION_CHOICES = (
        ('N', _("↑ N")),
        ('NE', _("↗ NE")),
        ('E', _("→ E")),
        ('SE', _("↘ SE")),
        ('S', _("↓ S")),
        ('SW', _("↙ SW")),
        ('W', _("← W")),
        ('NW', _("↖ NW")),
    )

    geom = models.GeometryCollectionField(verbose_name=_("Location"),
                                          srid=settings.SRID)
    parent = TreeForeignKey('Site',
                            related_name="children",
                            on_delete=models.PROTECT,
                            verbose_name=_("Parent"),
                            null=True,
                            blank=True)
    practice = models.ForeignKey('Practice',
                                 related_name="sites",
                                 on_delete=models.PROTECT,
                                 verbose_name=_("Practice"),
                                 null=True,
                                 blank=True)
    description = models.TextField(verbose_name=_("Description"),
                                   blank=True,
                                   help_text=_("Complete description"))
    description_teaser = models.TextField(
        verbose_name=_("Description teaser"),
        blank=True,
        help_text=_("A brief summary (map pop-ups)"))
    ambiance = models.TextField(verbose_name=_("Ambiance"),
                                blank=True,
                                help_text=_("Main attraction and interest"))
    advice = models.TextField(verbose_name=_("Advice"),
                              blank=True,
                              help_text=_("Risks, danger, best period, ..."))
    ratings_min = models.ManyToManyField(Rating,
                                         related_name='sites_min',
                                         blank=True)
    ratings_max = models.ManyToManyField(Rating,
                                         related_name='sites_max',
                                         blank=True)
    period = models.CharField(verbose_name=_("Period"),
                              max_length=1024,
                              blank=True)
    orientation = MultiSelectField(verbose_name=_("Orientation"),
                                   blank=True,
                                   max_length=20,
                                   choices=ORIENTATION_CHOICES)
    wind = MultiSelectField(verbose_name=_("Wind"),
                            blank=True,
                            max_length=20,
                            choices=ORIENTATION_CHOICES)
    labels = models.ManyToManyField('common.Label',
                                    related_name='sites',
                                    blank=True,
                                    verbose_name=_("Labels"))
    themes = models.ManyToManyField('common.Theme',
                                    related_name="sites",
                                    blank=True,
                                    verbose_name=_("Themes"),
                                    help_text=_("Main theme(s)"))
    information_desks = models.ManyToManyField(
        'tourism.InformationDesk',
        related_name='sites',
        blank=True,
        verbose_name=_("Information desks"),
        help_text=_("Where to obtain information"))
    portal = models.ManyToManyField('common.TargetPortal',
                                    blank=True,
                                    related_name='sites',
                                    verbose_name=_("Portal"))
    source = models.ManyToManyField('common.RecordSource',
                                    blank=True,
                                    related_name='sites',
                                    verbose_name=_("Source"))
    web_links = models.ManyToManyField('trekking.WebLink',
                                       related_name="sites",
                                       blank=True,
                                       verbose_name=_("Web links"),
                                       help_text=_("External resources"))
    type = models.ForeignKey(SiteType,
                             related_name="sites",
                             on_delete=models.PROTECT,
                             verbose_name=_("Type"),
                             null=True,
                             blank=True)
    eid = models.CharField(verbose_name=_("External id"),
                           max_length=1024,
                           blank=True,
                           null=True)

    class Meta:
        verbose_name = _("Outdoor site")
        verbose_name_plural = _("Outdoor sites")
        ordering = ('name', )

    class MPTTMeta:
        order_insertion_by = ['name']

    def __str__(self):
        return self.name

    @property
    def name_display(self):
        return "- " * self.level + super().name_display

    def distance(self, to_cls):
        """Distance to associate this site to another class"""
        return None

    @classmethod
    def get_create_label(cls):
        return _("Add a new outdoor site")

    @property
    def published_children(self):
        if not settings.PUBLISHED_BY_LANG:
            return self.children.filter(published=True)
        q = Q()
        for lang in settings.MODELTRANSLATION_LANGUAGES:
            q |= Q(**{'published_{}'.format(lang): True})
        return self.children.filter(q)

    @property
    def super_practices(self):
        "Return practices of itself and its descendants"
        practices_id = self.get_descendants(include_self=True) \
            .exclude(practice=None) \
            .values_list('practice_id', flat=True)
        return Practice.objects.filter(
            id__in=practices_id)  # Sorted and unique

    @property
    def super_practices_display(self):
        practices = self.super_practices
        if not practices:
            return ""
        verbose = [
            str(practice) if practice == self.practice else "<i>{}</i>".format(
                escape(practice)) for practice in practices
        ]
        return ", ".join(verbose)

    super_practices_verbose_name = _('Practices')

    @property
    def super_sectors(self):
        "Return sectors of itself and its descendants"
        sectors_id = self.get_descendants(include_self=True) \
            .exclude(practice=None) \
            .values_list('practice__sector_id', flat=True)
        return Sector.objects.filter(id__in=sectors_id)  # Sorted and unique

    @property
    def super_orientation(self):
        "Return orientation of itself and its descendants"
        orientation = set(
            sum(
                self.get_descendants(include_self=True).values_list(
                    'orientation', flat=True), []))
        return [o for o, _o in self.ORIENTATION_CHOICES
                if o in orientation]  # Sorting

    @property
    def super_wind(self):
        "Return wind of itself and its descendants"
        wind = set(
            sum(
                self.get_descendants(include_self=True).values_list('wind',
                                                                    flat=True),
                []))
        return [o for o, _o in self.ORIENTATION_CHOICES
                if o in wind]  # Sorting
Example #5
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"),
        default=False)
    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,
                                    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,
                                      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,
        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,
                                       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,
        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")

    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.model_name, self.pk, get_language()))

    def get_map_image_extent(self, srid=settings.API_SRID):
        extent = list(super(Trek, self).get_map_image_extent(srid))
        if self.parking_location:
            self.parking_location.transform(srid)
            extent[0] = min(extent[0], self.parking_location.x)
            extent[1] = min(extent[1], self.parking_location.y)
            extent[2] = max(extent[2], self.parking_location.x)
            extent[3] = max(extent[3], self.parking_location.y)
        if self.points_reference:
            self.points_reference.transform(srid)
            prextent = self.points_reference.extent
            extent[0] = min(extent[0], prextent[0])
            extent[1] = min(extent[1], prextent[1])
            extent[2] = max(extent[2], prextent[2])
            extent[3] = max(extent[3], prextent[3])
        for poi in self.published_pois:
            poi.geom.transform(srid)
            extent[0] = min(extent[0], poi.geom.x)
            extent[1] = min(extent[1], poi.geom.y)
            extent[2] = max(extent[2], poi.geom.x)
            extent[3] = max(extent[3], poi.geom.y)
        return extent

    @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 information_desks_display(self):
        return ', '.join([unicode(n) for n in self.information_desks.all()])

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

    @property
    def web_links_display(self):
        return ', '.join([unicode(n) for n in self.web_links.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.published_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 children

    def previous_id_for(self, parent):
        children_id = list(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 = list(parent.children_id)
        index = children_id.index(self.id)
        if index == len(children_id) - 1:
            return None
        return children_id[index + 1]

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

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

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

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

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

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

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

    def save(self, *args, **kwargs):
        if self.pk is not None and kwargs.get('update_fields', None) is None:
            field_names = set()
            for field in self._meta.concrete_fields:
                if not field.primary_key and not hasattr(field, 'through'):
                    field_names.add(field.attname)
            old_trek = Trek.objects.get(pk=self.pk)
            if self.geom is not None and old_trek.geom.equals_exact(
                    self.geom, tolerance=0.00001):
                field_names.remove('geom')
            if self.geom_3d is not None and old_trek.geom_3d.equals_exact(
                    self.geom_3d, tolerance=0.00001):
                field_names.remove('geom_3d')
            return super(Trek, self).save(update_fields=field_names,
                                          *args,
                                          **kwargs)
        super(Trek, self).save(*args, **kwargs)

    @property
    def portal_display(self):
        return ', '.join([unicode(portal) for portal in self.portal.all()])

    @property
    def source_display(self):
        return ','.join([unicode(source) for source in self.source.all()])

    @property
    def extent(self):
        return self.geom.transform(
            settings.API_SRID, clone=True).extent if self.geom.extent else None

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

    @property
    def meta_description(self):
        return plain_text(self.ambiance or self.description_teaser
                          or self.description)[:500]
Example #6
0
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_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
Example #7
0
class Device(BaseAccessLevel):
    """
    Device Model
    Represents a network device
    eg: an outdoor point-to-point wifi device, a BGP router, a server, and so on
    """
    name = models.CharField(_('name'), max_length=50)
    node = models.ForeignKey('nodes.Node', verbose_name=_('node'))
    type = models.CharField(_('type'),
                            max_length=50,
                            choices=DEVICE_TYPES_CHOICES)
    status = models.SmallIntegerField(_('status'),
                                      max_length=2,
                                      choices=DEVICE_STATUS_CHOICES,
                                      default=DEVICE_STATUS.get('unknown'))

    # geographic data
    location = models.PointField(
        _('location'),
        blank=True,
        null=True,
        help_text=_("""specify device coordinates (if different from node);
                                    defaults to node coordinates if node is a point,
                                    otherwise if node is a geometry it will default to che centroid of the geometry"""
                    ))
    elev = models.FloatField(_('elevation'), blank=True, null=True)

    # device specific
    routing_protocols = models.ManyToManyField('net.RoutingProtocol',
                                               blank=True)

    os = models.CharField(_('operating system'),
                          max_length=128,
                          blank=True,
                          null=True)
    os_version = models.CharField(_('operating system version'),
                                  max_length=128,
                                  blank=True,
                                  null=True)

    first_seen = models.DateTimeField(_('first time seen on'),
                                      blank=True,
                                      null=True,
                                      default=None)
    last_seen = models.DateTimeField(_('last time seen on'),
                                     blank=True,
                                     null=True,
                                     default=None)

    # text
    description = models.CharField(_('description'),
                                   max_length=255,
                                   blank=True,
                                   null=True)
    notes = models.TextField(_('notes'), blank=True, null=True)

    # extra data
    data = DictionaryField(
        _('extra data'),
        null=True,
        blank=True,
        help_text=_('store extra attributes in JSON string'))
    shortcuts = ReferencesField(null=True, blank=True)

    objects = DeviceManager()

    # list indicating if any other module has extended this model
    extended_by = []

    class Meta:
        app_label = 'net'

    def __unicode__(self):
        return '%s' % self.name

    def save(self, *args, **kwargs):
        """
        Custom save method does the following:
            * automatically inherit node coordinates and elevation
            * save shortcuts if HSTORE is enabled
        """
        custom_checks = kwargs.pop('custom_checks', True)

        super(Device, self).save(*args, **kwargs)

        if custom_checks is False:
            return

        changed = False

        if not self.location:
            self.location = self.node.point
            changed = True

        if not self.elev and self.node.elev:
            self.elev = self.node.elev
            changed = True

        original_user = self.shortcuts.get('user')

        if self.node.user:
            self.shortcuts['user'] = self.node.user

        if original_user != self.shortcuts.get('user'):
            changed = True

        if 'nodeshot.core.layers' in settings.INSTALLED_APPS:
            original_layer = self.shortcuts.get('layer')
            self.shortcuts['layer'] = self.node.layer

            if original_layer != self.shortcuts.get('layer'):
                changed = True

        if changed:
            self.save(custom_checks=False)

    @property
    def owner(self):
        if 'user' not in self.shortcuts:
            if self.node or self.node_id:
                self.save()
            else:
                raise Exception('Instance does not have a node set yet')
        return self.shortcuts['user']

    @property
    def layer(self):
        if 'nodeshot.core.layers' not in settings.INSTALLED_APPS:
            return False
        if 'layer' not in self.shortcuts:
            if self.node or self.node_id:
                self.save()
            else:
                raise Exception('Instance does not have a node set yet')
        return self.shortcuts['layer']

    if 'grappelli' in settings.INSTALLED_APPS:

        @staticmethod
        def autocomplete_search_fields():
            return ('name__icontains', )
Example #8
0
class FoiLaw(models.Model):
    name = models.CharField(_("Name"), max_length=255)
    slug = models.SlugField(_("Slug"), max_length=255)
    description = models.TextField(_("Description"), blank=True)
    long_description = models.TextField(_("Website Text"), blank=True)
    created = models.DateField(_("Creation Date"), blank=True, null=True)
    updated = models.DateField(_("Updated Date"), blank=True, null=True)
    request_note = models.TextField(_("request note"), blank=True)
    meta = models.BooleanField(_("Meta Law"), default=False)
    law_type = models.CharField(_('law type'), max_length=255, blank=True)
    combined = models.ManyToManyField(
        'FoiLaw',
        verbose_name=_("Combined Laws"), blank=True
    )
    letter_start = models.TextField(_("Start of Letter"), blank=True)
    letter_end = models.TextField(_("End of Letter"), blank=True)
    jurisdiction = models.ForeignKey(
            Jurisdiction, verbose_name=_('Jurisdiction'),
            null=True, on_delete=models.SET_NULL, blank=True)
    priority = models.SmallIntegerField(_("Priority"), default=3)
    url = models.CharField(_("URL"), max_length=255, blank=True)
    max_response_time = models.IntegerField(_("Maximal Response Time"),
            null=True, blank=True, default=30)
    max_response_time_unit = models.CharField(_("Unit of Response Time"),
            blank=True, max_length=32, default='day',
            choices=(('day', _('Day(s)')),
                ('working_day', _('Working Day(s)')),
                ('month_de', _('Month(s) (DE)')),
            ))
    refusal_reasons = models.TextField(
        _("Possible Refusal Reasons, one per line, e.g §X.Y: Privacy Concerns"),
        blank=True)
    mediator = models.ForeignKey('PublicBody', verbose_name=_("Mediator"),
            null=True, blank=True,
            default=None, on_delete=models.SET_NULL,
            related_name="mediating_laws")
    email_only = models.BooleanField(_('E-Mail only'), default=False)
    requires_signature = models.BooleanField(_('Requires signature'), default=False)
    site = models.ForeignKey(Site, verbose_name=_("Site"),
            null=True, on_delete=models.SET_NULL,
            default=settings.SITE_ID)

    class Meta:
        verbose_name = _("Freedom of Information Law")
        verbose_name_plural = _("Freedom of Information Laws")

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

    def get_absolute_url(self):
        return reverse('publicbody-foilaw-show', kwargs={'slug': self.slug})

    def get_absolute_domain_url(self):
        return "%s%s" % (settings.SITE_URL, self.get_absolute_url())

    @property
    def request_note_html(self):
        return markdown(self.request_note)

    @property
    def description_html(self):
        return markdown(self.description)

    @property
    def address_required(self):
        return not self.email_only

    def get_refusal_reason_choices(self):
        not_applicable = [('n/a', _("No law can be applied"))]
        if self.meta:
            return (not_applicable + [
                    (l[0], "%s: %s" % (law.name, l[1]))
                    for law in self.combined.all()
                    for l in law.get_refusal_reason_choices()[1:]])
        else:
            return (not_applicable + [
                    (x, Truncator(x).words(12))
                    for x in self.refusal_reasons.splitlines()])

    def as_data(self, request=None):
        from .api_views import FoiLawSerializer
        if request is None:
            ctx = get_fake_api_context()
        else:
            ctx = {
                'request': request
            }
        return FoiLawSerializer(self, context=ctx).data

    def calculate_due_date(self, date=None, value=None):
        if date is None:
            date = timezone.now()
        if value is None:
            value = self.max_response_time
        if self.max_response_time_unit == "month_de":
            return calculate_month_range_de(date, value)
        elif self.max_response_time_unit == "day":
            return date + timedelta(days=value)
        elif self.max_response_time_unit == "working_day":
            return calculate_workingday_range(date, value)
Example #9
0
class PublicBody(models.Model):
    name = models.CharField(_("Name"), max_length=255)
    other_names = models.TextField(_("Other names"), default="", blank=True)
    slug = models.SlugField(_("Slug"), max_length=255)
    description = models.TextField(_("Description"), blank=True)
    url = models.URLField(_("URL"), null=True, blank=True, max_length=500)

    parent = models.ForeignKey('PublicBody', null=True, blank=True,
            default=None, on_delete=models.SET_NULL,
            related_name="children")
    root = models.ForeignKey('PublicBody', null=True, blank=True,
            default=None, on_delete=models.SET_NULL,
            related_name="descendants")
    depth = models.SmallIntegerField(default=0)

    classification = models.ForeignKey(Classification, null=True, blank=True,
        on_delete=models.SET_NULL)

    email = models.EmailField(_("Email"), blank=True, default='')
    fax = models.CharField(max_length=50, blank=True)
    contact = models.TextField(_("Contact"), blank=True)
    address = models.TextField(_("Address"), blank=True)
    website_dump = models.TextField(_("Website Dump"), null=True, blank=True)
    request_note = models.TextField(_("request note"), blank=True)

    file_index = models.CharField(_("file index"), max_length=1024, blank=True)
    org_chart = models.CharField(_("organisational chart"), max_length=1024, blank=True)

    _created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
            verbose_name=_("Created by"),
            blank=True, null=True, related_name='public_body_creators',
            on_delete=models.SET_NULL)
    _updated_by = models.ForeignKey(settings.AUTH_USER_MODEL,
            verbose_name=_("Updated by"),
            blank=True, null=True, related_name='public_body_updaters',
            on_delete=models.SET_NULL)
    created_at = models.DateTimeField(_("Created at"), default=timezone.now)
    updated_at = models.DateTimeField(_("Updated at"), default=timezone.now)
    confirmed = models.BooleanField(_("confirmed"), default=True)

    number_of_requests = models.IntegerField(_("Number of requests"),
            default=0)
    site = models.ForeignKey(Site, verbose_name=_("Site"),
            null=True, on_delete=models.SET_NULL, default=settings.SITE_ID)

    wikidata_item = models.CharField(max_length=50, blank=True)

    jurisdiction = models.ForeignKey(Jurisdiction, verbose_name=_('Jurisdiction'),
            blank=True, null=True, on_delete=models.SET_NULL)

    geo = models.PointField(null=True, blank=True, geography=True)
    regions = models.ManyToManyField(GeoRegion, blank=True)

    laws = models.ManyToManyField(FoiLaw,
            verbose_name=_("Freedom of Information Laws"))
    tags = TaggableManager(through=TaggedPublicBody, blank=True)
    categories = TaggableManager(
        through=CategorizedPublicBody,
        verbose_name=_("categories"),
        blank=True
    )

    non_filtered_objects = models.Manager()
    objects = PublicBodyManager()
    published = objects

    class Meta:
        ordering = ('name',)
        verbose_name = _("Public Body")
        verbose_name_plural = _("Public Bodies")

    serializable_fields = ('id', 'name', 'slug', 'request_note_html',
            'description', 'url', 'email', 'contact',
            'address', 'domain', 'number_of_requests')

    def __str__(self):
        return self.name

    @property
    def created_by(self):
        return self._created_by

    @property
    def updated_by(self):
        return self._updated_by

    @property
    def domain(self):
        if self.url and self.url.count('/') > 1:
            return self.url.split("/")[2]
        return None

    @property
    def all_names(self):
        names = [self.name, self.other_names]
        if self.jurisdiction:
            names.extend([self.jurisdiction.name, self.jurisdiction.slug])
        return ' '.join(names)

    @property
    def request_note_html(self):
        return markdown(self.request_note)

    @property
    def tag_list(self):
        return edit_string_for_tags(self.tags.all())

    @property
    def default_law(self):
        # FIXME: Materialize this?
        return self.get_applicable_law()

    def get_applicable_law(self, law_type=None):
        return get_applicable_law(pb=self, law_type=law_type)

    def get_absolute_url(self):
        return reverse('publicbody-show', kwargs={"slug": self.slug})

    def get_absolute_short_url(self):
        return reverse('publicbody-publicbody_shortlink', kwargs={
            'obj_id': self.pk
        })

    def get_absolute_domain_url(self):
        return "%s%s" % (settings.SITE_URL, self.get_absolute_url())

    def get_absolute_domain_short_url(self):
        return "%s%s" % (settings.SITE_URL, self.get_absolute_short_url())

    def get_mediator(self):
        law = self.default_law
        if law is None:
            return None
        return law.mediator

    def get_label(self):
        return mark_safe(
            '%(name)s - <a href="%(url)s" target="_blank" '
            'class="info-link">%(detail)s</a>' % {
                "name": escape(self.name),
                "url": self.get_absolute_url(),
                "detail": _("More Info")
            }
        )

    def as_data(self, request=None):
        from .api_views import PublicBodyListSerializer
        if request is None:
            ctx = get_fake_api_context()
        else:
            ctx = {
                'request': request
            }
        return PublicBodyListSerializer(self, context=ctx).data

    @property
    def children_count(self):
        return len(PublicBody.objects.filter(parent=self))

    @classmethod
    def export_csv(cls, queryset):

        fields = (
            "id", "name", "email", "fax", "contact",
            "address", "url",
            ('classification', lambda x: x.classification.name if x.classification else None),
            "jurisdiction__slug",
            ("categories", lambda x: edit_string_for_tags(x.categories.all())),
            "other_names", "website_dump", "description",
            "request_note", "parent__id",
            ('regions', lambda obj: ','.join(str(x.id) for x in obj.regions.all()))
        )

        return export_csv(queryset, fields)
Example #10
0
class Invoice(BaseModel):
    """
    Issue Date, Last Payment Date, Invoice ID, PO Number, Client, Subject,
    Invoice Amount, Paid Amount, Balance, Subtotal, Discount, Tax, Tax2,
    Currency, Currency Symbol, Document Type
    """
    subject = models.CharField(max_length=300, blank=True, null=True)
    issue_date = models.DateField("Issue Date",
                                  blank=True,
                                  default=timezone.now,
                                  null=True)
    due_date = models.DateField("Due", blank=True, null=True)
    last_payment_date = models.DateField(blank=True, null=True)
    start_date = models.DateField("Start Date",
                                  blank=True,
                                  default=timezone.now,
                                  null=True)
    end_date = models.DateField("End Date",
                                blank=True,
                                default=timezone.now,
                                null=True)
    po_number = models.CharField("PO Number",
                                 max_length=300,
                                 blank=True,
                                 null=True)
    sa_number = models.CharField("Subcontractor Agreement Number",
                                 max_length=300,
                                 blank=True,
                                 null=True)
    client = models.ForeignKey(
        Client,
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        limit_choices_to={'active': True},
    )
    amount = models.DecimalField("Invoice Amount",
                                 blank=True,
                                 null=True,
                                 max_digits=12,
                                 decimal_places=2)
    paid_amount = models.DecimalField(blank=True,
                                      null=True,
                                      max_digits=12,
                                      decimal_places=2)
    balance = models.DecimalField(blank=True,
                                  null=True,
                                  max_digits=12,
                                  decimal_places=2)
    subtotal = models.DecimalField(blank=True,
                                   null=True,
                                   max_digits=12,
                                   decimal_places=2)
    discount = models.IntegerField(blank=True, null=True)
    tax = models.IntegerField(blank=True, null=True)
    tax2 = models.IntegerField(blank=True, null=True)
    project = models.ForeignKey(
        "Project",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        limit_choices_to={'active': True},
    )
    currency = models.CharField(default="United States Dollar - USD",
                                max_length=300,
                                blank=True,
                                null=True)
    currency_symbol = models.CharField(default="$",
                                       max_length=300,
                                       blank=True,
                                       null=True)
    note = models.ManyToManyField(
        'Note',
        blank=True,
        limit_choices_to={'active': True},
    )

    def __str__(self):
        if self.subject:
            return self.subject
        else:
            return 'invoice-%s' % self.pk

    # https://stackoverflow.com/a/6062320/185820
    class Meta:
        ordering = ["subject"]
Example #11
0
class Project(BaseModel):
    """
    Client, Project, Project Code, Start Date, End Date, Project Notes,
    Total Hours, Billable Hours, Billable Amount, Budget, Budget Spent,
    Budget Remaining, Total Costs, Team Costs, Expenses
    """
    client = models.ForeignKey(
        Client,
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        limit_choices_to={'active': True},
    )
    name = models.CharField("Project Name",
                            max_length=300,
                            blank=True,
                            null=True)
    task = models.ForeignKey(
        "Task",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
        limit_choices_to={'active': True},
    )
    team = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                  blank=True,
                                  limit_choices_to={'profile__active': True})
    start_date = models.DateField(blank=True, null=True)
    end_date = models.DateField(blank=True, null=True)
    code = models.IntegerField("Project Code", blank=True, null=True)
    notes = models.TextField(blank=True, null=True)
    total_hours = models.FloatField(blank=True, null=True)
    billable_hours = models.FloatField(blank=True, null=True)
    amount = models.DecimalField(blank=True,
                                 null=True,
                                 max_digits=12,
                                 decimal_places=2)
    budget = models.DecimalField(blank=True,
                                 null=True,
                                 max_digits=12,
                                 decimal_places=2)
    budget_spent = models.DecimalField(blank=True,
                                       null=True,
                                       max_digits=12,
                                       decimal_places=2)
    budget_remaining = models.DecimalField(blank=True,
                                           null=True,
                                           max_digits=12,
                                           decimal_places=2)
    total_costs = models.DecimalField(blank=True,
                                      null=True,
                                      max_digits=12,
                                      decimal_places=2)
    team_costs = models.DecimalField(blank=True,
                                     null=True,
                                     max_digits=12,
                                     decimal_places=2)
    cost = models.DecimalField(blank=True,
                               null=True,
                               max_digits=12,
                               decimal_places=2)
    expenses = models.DecimalField(blank=True,
                                   null=True,
                                   max_digits=12,
                                   decimal_places=2)

    def __str__(self):
        if self.name:
            return self.name
        else:
            return '-'.join([self._meta.verbose_name, str(self.pk)])

    # https://stackoverflow.com/a/6062320/185820
    class Meta:
        ordering = ["name"]
Example #12
0
class LandManager(models.Model):
    class Meta:
        verbose_name = "Land Manager"
        verbose_name_plural = "Land Managers"

    user = models.OneToOneField(User,
                                related_name="landmanager",
                                on_delete=models.CASCADE)
    management_agency = models.ForeignKey("ManagementAgency",
                                          null=True,
                                          blank=True,
                                          on_delete=models.CASCADE)
    full_access = models.BooleanField(
        default=False,
        help_text="Give this user access to all Archaeological Sites.",
    )
    apply_agency_filter = models.BooleanField(
        default=False,
        help_text="Give this user access to all Archaeological Sites managed"\
            "by their Agency (as defined above).",
        blank=True,
        null=True,
    )
    apply_area_filter = models.BooleanField(
        default=False,
        help_text="Give this user access to all Archaeological Sites within "\
            "any of the specified areas or groups of areas below.",
        blank=True,
        null=True,
    )
    individual_areas = models.ManyToManyField("ManagementArea", blank=True)
    grouped_areas = models.ManyToManyField("ManagementAreaGroup", blank=True)

    def __str__(self):
        return self.user.username

    @property
    def all_areas(self):
        areas = self.individual_areas.all()
        for ga in self.grouped_areas.all():
            areas = areas.union(ga.areas.all())
        return areas

    @property
    def areas_as_multipolygon(self):
        poly_agg = list()
        for area in self.all_areas:
            # each area is a MultiPolygon so iterate the Polygons within it
            for poly in area.geom:
                poly_agg.append(poly)
        full_multi = MultiPolygon(poly_agg, srid=4326)
        return full_multi

    @property
    def filter_rules(self):

        rules = {"access_level": "", "": ""}

    def set_allowed_resources(self):
        """very confusingly, this method must be called from admin.LandManagerAdmin.save_related().
        This is because self.save() and post_save here do not yet have the updated versions
        of the ManyToManyFields (individual_areas and grouped_areas)"""

        from hms.utils import update_hms_permissions_table
        update_hms_permissions_table(user=self.user)
Example #13
0
class Resource(ModifiableModel, AutoIdentifiedModel):
    AUTHENTICATION_TYPES = (
        ('none', _('None')),
        ('weak', _('Weak')),
        ('strong', _('Strong'))
    )
    ACCESS_CODE_TYPE_NONE = 'none'
    ACCESS_CODE_TYPE_PIN4 = 'pin4'
    ACCESS_CODE_TYPE_PIN6 = 'pin6'
    ACCESS_CODE_TYPES = (
        (ACCESS_CODE_TYPE_NONE, _('None')),
        (ACCESS_CODE_TYPE_PIN4, _('4-digit PIN code')),
        (ACCESS_CODE_TYPE_PIN6, _('6-digit PIN code')),
    )
    id = models.CharField(primary_key=True, max_length=100)
    public = models.BooleanField(default=True, verbose_name=_('Public'))
    unit = models.ForeignKey('Unit', verbose_name=_('Unit'), db_index=True, null=True, blank=True,
                             related_name="resources", on_delete=models.PROTECT)
    type = models.ForeignKey(ResourceType, verbose_name=_('Resource type'), db_index=True,
                             on_delete=models.PROTECT)
    purposes = models.ManyToManyField(Purpose, verbose_name=_('Purposes'))
    name = models.CharField(verbose_name=_('Name'), max_length=200)
    description = models.TextField(verbose_name=_('Description'), null=True, blank=True)
    need_manual_confirmation = models.BooleanField(verbose_name=_('Need manual confirmation'), default=False)
    authentication = models.CharField(blank=False, verbose_name=_('Authentication'),
                                      max_length=20, choices=AUTHENTICATION_TYPES)
    people_capacity = models.PositiveIntegerField(verbose_name=_('People capacity'), null=True, blank=True)
    area = models.PositiveIntegerField(verbose_name=_('Area (m2)'), null=True, blank=True)

    # if not set, location is inherited from unit
    location = models.PointField(verbose_name=_('Location'), null=True, blank=True, srid=settings.DEFAULT_SRID)

    min_period = models.DurationField(verbose_name=_('Minimum reservation time'),
                                      default=datetime.timedelta(minutes=30))
    max_period = models.DurationField(verbose_name=_('Maximum reservation time'), null=True, blank=True)
    slot_size = models.DurationField(verbose_name=_('Slot size for reservation time'),
                                     default=datetime.timedelta(minutes=30))

    equipment = EquipmentField(Equipment, through='ResourceEquipment', verbose_name=_('Equipment'))
    max_reservations_per_user = models.PositiveIntegerField(verbose_name=_('Maximum number of active reservations per user'),
                                                            null=True, blank=True)
    reservable = models.BooleanField(verbose_name=_('Reservable'), default=False)
    reservation_info = models.TextField(verbose_name=_('Reservation info'), null=True, blank=True)
    responsible_contact_info = models.TextField(verbose_name=_('Responsible contact info'), blank=True)
    generic_terms = models.ForeignKey(TermsOfUse, verbose_name=_('Generic terms'), null=True, blank=True,
                                      on_delete=models.SET_NULL)
    specific_terms = models.TextField(verbose_name=_('Specific terms'), blank=True)
    reservation_requested_notification_extra = models.TextField(verbose_name=_(
        'Extra content to "reservation requested" notification'), blank=True)
    reservation_confirmed_notification_extra = models.TextField(verbose_name=_(
        'Extra content to "reservation confirmed" notification'), blank=True)
    min_price_per_hour = models.DecimalField(verbose_name=_('Min price per hour'), max_digits=8, decimal_places=2,
                                             blank=True, null=True, validators=[MinValueValidator(Decimal('0.00'))])
    max_price_per_hour = models.DecimalField(verbose_name=_('Max price per hour'), max_digits=8, decimal_places=2,
                                             blank=True, null=True, validators=[MinValueValidator(Decimal('0.00'))])

    access_code_type = models.CharField(verbose_name=_('Access code type'), max_length=20, choices=ACCESS_CODE_TYPES,
                                        default=ACCESS_CODE_TYPE_NONE)
    # Access codes can be generated either by the general Respa code or
    # the Kulkunen app. Kulkunen will set the `generate_access_codes`
    # attribute by itself if special access code considerations are
    # needed.
    generate_access_codes = models.BooleanField(
        verbose_name=_('Generate access codes'), default=True, editable=False,
        help_text=_('Should access codes generated by the general system')
    )
    reservable_max_days_in_advance = models.PositiveSmallIntegerField(verbose_name=_('Reservable max. days in advance'),
                                                                      null=True, blank=True)
    reservable_min_days_in_advance = models.PositiveSmallIntegerField(verbose_name=_('Reservable min. days in advance'),
                                                                      null=True, blank=True)
    reservation_metadata_set = models.ForeignKey(
        'resources.ReservationMetadataSet', verbose_name=_('Reservation metadata set'),
        null=True, blank=True, on_delete=models.SET_NULL
    )
    external_reservation_url = models.URLField(
        verbose_name=_('External reservation URL'),
        help_text=_('A link to an external reservation system if this resource is managed elsewhere'),
        null=True, blank=True)
    reservation_extra_questions = models.TextField(verbose_name=_('Reservation extra questions'), blank=True)

    objects = ResourceQuerySet.as_manager()

    class Meta:
        verbose_name = _("resource")
        verbose_name_plural = _("resources")
        ordering = ('unit', 'name',)

    def __str__(self):
        return "%s (%s)/%s" % (get_translated(self, 'name'), self.id, self.unit)

    @cached_property
    def main_image(self):
        resource_image = next(
            (image for image in self.images.all() if image.type == 'main'),
            None)

        return resource_image.image if resource_image else None

    def validate_reservation_period(self, reservation, user, data=None):
        """
        Check that given reservation if valid for given user.

        Reservation may be provided as Reservation or as a data dict.
        When providing the data dict from a serializer, reservation
        argument must be present to indicate the reservation being edited,
        or None if we are creating a new reservation.
        If the reservation is not valid raises a ValidationError.

        Staff members have no restrictions at least for now.

        Normal users cannot make multi day reservations or reservations
        outside opening hours.

        :type reservation: Reservation
        :type user: User
        :type data: dict[str, Object]
        """

        # no restrictions for staff
        if self.is_admin(user):
            return

        tz = self.unit.get_tz()
        # check if data from serializer is present:
        if data:
            begin = data['begin']
            end = data['end']
        else:
            # if data is not provided, the reservation object has the desired data:
            begin = reservation.begin
            end = reservation.end

        if begin.tzinfo:
            begin = begin.astimezone(tz)
        else:
            begin = tz.localize(begin)
        if end.tzinfo:
            end = end.astimezone(tz)
        else:
            end = tz.localize(end)

        if begin.date() != end.date():
            raise ValidationError(_("You cannot make a multi day reservation"))

        opening_hours = self.get_opening_hours(begin.date(), end.date())
        days = opening_hours.get(begin.date(), None)
        if days is None or not any(day['opens'] and begin >= day['opens'] and end <= day['closes'] for day in days):
            if not self._has_perm(user, 'can_ignore_opening_hours'):
                raise ValidationError(_("You must start and end the reservation during opening hours"))

        if self.max_period and (end - begin) > self.max_period:
            raise ValidationError(_("The maximum reservation length is %(max_period)s") %
                                  {'max_period': humanize_duration(self.max_period)})

    def validate_max_reservations_per_user(self, user):
        """
        Check maximum number of active reservations per user per resource.
        If the user has too many reservations raises ValidationError.

        Staff members have no reservation limits.

        :type user: User
        """
        if self.is_admin(user):
            return

        max_count = self.max_reservations_per_user
        if max_count is not None:
            reservation_count = self.reservations.filter(user=user).active().count()
            if reservation_count >= max_count:
                raise ValidationError(_("Maximum number of active reservations for this resource exceeded."))

    def check_reservation_collision(self, begin, end, reservation):
        overlapping = self.reservations.filter(end__gt=begin, begin__lt=end).active()
        if reservation:
            overlapping = overlapping.exclude(pk=reservation.pk)
        return overlapping.exists()

    def get_available_hours(self, start=None, end=None, duration=None, reservation=None, during_closing=False):
        """
        Returns hours that the resource is not reserved for a given date range

        If include_closed=True, will also return hours when the resource is closed, if it is not reserved.
        This is so that admins can book resources during closing hours. Returns
        the available hours as a list of dicts. The optional reservation argument
        is for disregarding a given reservation during checking, if we wish to
        move an existing reservation. The optional duration argument specifies
        minimum length for periods to be returned.

        :rtype: list[dict[str, datetime.datetime]]
        :type start: datetime.datetime
        :type end: datetime.datetime
        :type duration: datetime.timedelta
        :type reservation: Reservation
        :type during_closing: bool
        """
        today = arrow.get(timezone.now())
        if start is None:
            start = today.floor('day').naive
        if end is None:
            end = today.replace(days=+1).floor('day').naive
        if not start.tzinfo and not end.tzinfo:
            """
            Only try to localize naive dates
            """
            tz = timezone.get_current_timezone()
            start = tz.localize(start)
            end = tz.localize(end)

        if not during_closing:
            """
            Check open hours only
            """
            open_hours = self.get_opening_hours(start, end)
            hours_list = []
            for date, open_during_date in open_hours.items():
                for period in open_during_date:
                    if period['opens']:
                        # if the start or end straddle opening hours
                        opens = period['opens'] if period['opens'] > start else start
                        closes = period['closes'] if period['closes'] < end else end
                        # include_closed to prevent recursion, opening hours need not be rechecked
                        hours_list.extend(self.get_available_hours(start=opens,
                                                                   end=closes,
                                                                   duration=duration,
                                                                   reservation=reservation,
                                                                   during_closing=True))
            return hours_list

        reservations = self.reservations.filter(
            end__gte=start, begin__lte=end).order_by('begin')
        hours_list = [({'starts': start})]
        first_checked = False
        for res in reservations:
            # skip the reservation that is being edited
            if res == reservation:
                continue
            # check if the reservation spans the beginning
            if not first_checked:
                first_checked = True
                if res.begin < start:
                    if res.end > end:
                        return []
                    hours_list[0]['starts'] = res.end
                    # proceed to the next reservation
                    continue
            if duration:
                if res.begin - hours_list[-1]['starts'] < duration:
                    # the free period is too short, discard this period
                    hours_list[-1]['starts'] = res.end
                    continue
            hours_list[-1]['ends'] = timezone.localtime(res.begin)
            # check if the reservation spans the end
            if res.end > end:
                return hours_list
            hours_list.append({'starts': timezone.localtime(res.end)})
        # after the last reservation, we must check if the remaining free period is too short
        if duration:
            if end - hours_list[-1]['starts'] < duration:
                hours_list.pop()
                return hours_list
        # otherwise add the remaining free period
        hours_list[-1]['ends'] = end
        return hours_list

    def get_opening_hours(self, begin=None, end=None, opening_hours_cache=None):
        """
        :rtype : dict[str, datetime.datetime]
        :type begin: datetime.date
        :type end: datetime.date
        """
        tz = pytz.timezone(self.unit.time_zone)
        begin, end = determine_hours_time_range(begin, end, tz)

        if opening_hours_cache is None:
            hours_objs = self.opening_hours.filter(open_between__overlap=(begin, end, '[)'))
        else:
            hours_objs = opening_hours_cache

        opening_hours = dict()
        for h in hours_objs:
            opens = h.open_between.lower.astimezone(tz)
            closes = h.open_between.upper.astimezone(tz)
            date = opens.date()
            hours_item = OrderedDict(opens=opens, closes=closes)
            date_item = opening_hours.setdefault(date, [])
            date_item.append(hours_item)

        # Set the dates when the resource is closed.
        date = begin.date()
        end = end.date()
        while date < end:
            if date not in opening_hours:
                opening_hours[date] = [OrderedDict(opens=None, closes=None)]
            date += datetime.timedelta(days=1)

        return opening_hours

    def update_opening_hours(self):
        hours = self.opening_hours.order_by('open_between')
        existing_hours = {}
        for h in hours:
            assert h.open_between.lower not in existing_hours
            existing_hours[h.open_between.lower] = h.open_between.upper

        unit_periods = list(self.unit.periods.all())
        resource_periods = list(self.periods.all())

        # Periods set for the resource always carry a higher priority. If
        # nothing is defined for the resource for a given day, use the
        # periods configured for the unit.
        for period in unit_periods:
            period.priority = 0
        for period in resource_periods:
            period.priority = 1

        earliest_date = None
        latest_date = None
        all_periods = unit_periods + resource_periods
        for period in all_periods:
            if earliest_date is None or period.start < earliest_date:
                earliest_date = period.start
            if latest_date is None or period.end > latest_date:
                latest_date = period.end

        # Assume we delete everything, but remove items from the delete
        # list if the hours are identical.
        to_delete = existing_hours
        to_add = {}
        if all_periods:
            hours = get_opening_hours(self.unit.time_zone, all_periods,
                                      earliest_date, latest_date)
            for hours_items in hours.values():
                for h in hours_items:
                    if not h['opens'] or not h['closes']:
                        continue
                    if h['opens'] in to_delete and h['closes'] == to_delete[h['opens']]:
                            del to_delete[h['opens']]
                            continue
                    to_add[h['opens']] = h['closes']

        if to_delete:
            ret = ResourceDailyOpeningHours.objects.filter(
                open_between__in=[(opens, closes, '[)') for opens, closes in to_delete.items()],
                resource=self
            ).delete()
            assert ret[0] == len(to_delete)

        add_objs = [
            ResourceDailyOpeningHours(resource=self, open_between=(opens, closes, '[)'))
            for opens, closes in to_add.items()
        ]
        if add_objs:
            ResourceDailyOpeningHours.objects.bulk_create(add_objs)

    def is_admin(self, user):
        """
        Check if the given user is an administrator of this resource.

        :type user: users.models.User
        :rtype: bool
        """
        # UserFilterBackend and ReservationFilterSet in resources.api.reservation assume the same behaviour,
        # so if this is changed those need to be changed as well.
        if not self.unit:
            return is_general_admin(user)
        return self.unit.is_admin(user)

    def is_manager(self, user):
        """
        Check if the given user is a manager of this resource.

        :type user: users.models.User
        :rtype: bool
        """
        if not self.unit:
            return is_general_admin(user)
        return self.unit.is_manager(user)

    def _has_perm(self, user, perm, allow_admin=True):
        if not is_authenticated_user(user):
            return False
        # Admins are almighty.
        if self.is_admin(user) and allow_admin:
            return True
        if hasattr(self, '_permission_checker'):
            checker = self._permission_checker
        else:
            checker = ObjectPermissionChecker(user)

        # Permissions can be given per-unit
        if checker.has_perm('unit:%s' % perm, self.unit):
            return True
        # ... or through Resource Groups
        resource_group_perms = [checker.has_perm('group:%s' % perm, rg) for rg in self.groups.all()]
        return any(resource_group_perms)

    def get_users_with_perm(self, perm):
        users = {u for u in get_users_with_perms(self.unit) if u.has_perm('unit:%s' % perm, self.unit)}
        for rg in self.groups.all():
            users |= {u for u in get_users_with_perms(rg) if u.has_perm('group:%s' % perm, rg)}
        return users

    def can_make_reservations(self, user):
        return self.reservable or self._has_perm(user, 'can_make_reservations')

    def can_modify_reservations(self, user):
        return self._has_perm(user, 'can_modify_reservations')

    def can_ignore_opening_hours(self, user):
        return self._has_perm(user, 'can_ignore_opening_hours')

    def can_view_reservation_extra_fields(self, user):
        return self._has_perm(user, 'can_view_reservation_extra_fields')

    def can_access_reservation_comments(self, user):
        return self._has_perm(user, 'can_access_reservation_comments')

    def can_view_catering_orders(self, user):
        return self._has_perm(user, 'can_view_reservation_catering_orders')

    def can_modify_catering_orders(self, user):
        return self._has_perm(user, 'can_modify_reservation_catering_orders')

    def can_view_product_orders(self, user):
        return self._has_perm(user, 'can_view_reservation_product_orders', allow_admin=False)

    def can_modify_paid_reservations(self, user):
        return self._has_perm(user, 'can_modify_paid_reservations', allow_admin=False)

    def can_approve_reservations(self, user):
        return self._has_perm(user, 'can_approve_reservation', allow_admin=False)

    def can_view_access_codes(self, user):
        return self._has_perm(user, 'can_view_reservation_access_code')

    def is_access_code_enabled(self):
        return self.access_code_type != Resource.ACCESS_CODE_TYPE_NONE

    def get_reservable_max_days_in_advance(self):
        return self.reservable_max_days_in_advance or self.unit.reservable_max_days_in_advance

    def get_reservable_before(self):
        return create_datetime_days_from_now(self.get_reservable_max_days_in_advance())

    def get_reservable_min_days_in_advance(self):
        return self.reservable_min_days_in_advance or self.unit.reservable_min_days_in_advance

    def get_reservable_after(self):
        return create_datetime_days_from_now(self.get_reservable_min_days_in_advance())

    def has_rent(self):
        return self.products.current().rents().exists()

    def get_supported_reservation_extra_field_names(self, cache=None):
        if not self.reservation_metadata_set_id:
            return []
        if cache:
            metadata_set = cache[self.reservation_metadata_set_id]
        else:
            metadata_set = self.reservation_metadata_set
        return [x.field_name for x in metadata_set.supported_fields.all()]

    def get_required_reservation_extra_field_names(self, cache=None):
        if not self.reservation_metadata_set:
            return []
        if cache:
            metadata_set = cache[self.reservation_metadata_set_id]
        else:
            metadata_set = self.reservation_metadata_set
        return [x.field_name for x in metadata_set.required_fields.all()]

    def clean(self):
        if self.min_price_per_hour is not None and self.max_price_per_hour is not None:
            if self.min_price_per_hour > self.max_price_per_hour:
                raise ValidationError(
                    {'min_price_per_hour': _('This value cannot be greater than max price per hour')}
                )
        if self.min_period % self.slot_size != datetime.timedelta(0):
            raise ValidationError({'min_period': _('This value must be a multiple of slot_size')})

        if self.need_manual_confirmation and self.products.current().exists():
            raise ValidationError(
                {'need_manual_confirmation': _('This cannot be enabled because the resource has product(s).')}
            )
Example #14
0
class Guidebook(models.Model):
    unique_id = models.UUIDField(default=uuid.uuid4,
                                 editable=False,
                                 unique=True)
    user = models.ForeignKey(UserModel, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    description = models.TextField(null=True)
    category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
    cover_image = models.ImageField(upload_to=image_directory_path, null=True)
    tag = models.ManyToManyField(Tag)
    is_published = models.BooleanField(default=False)
    is_approved = models.BooleanField(default=True)
    created_at = models.DateTimeField(default=datetime.now, blank=True)
    updated_at = models.DateTimeField(default=datetime.now, blank=True)

    def get_absolute_url(self):
        return reverse('guidebook.guidebook_detail',
                       kwargs={'unique_id': str(self.unique_id)})

    def getScenes(self):
        scenes = Scene.objects.filter(guidebook=self).order_by('sort')
        return scenes

    def getScenePositions(self):
        scenes = Scene.objects.filter(guidebook=self)
        positions = []
        if scenes and scenes.count() > 0:
            for scene in scenes:
                position = [scene.lat, scene.lng]
                positions.append(position)
        return positions

    def getFirstScene(self):
        scenes = Scene.objects.filter(guidebook=self).order_by('sort')
        if scenes and scenes.count() > 0:
            firstScene = scenes[0]
            return firstScene
        else:
            return ''

    def getSceneCount(self):
        scenes = Scene.objects.filter(guidebook=self)
        return scenes.count()

    def getLikeCount(self):
        liked_guidebook = GuidebookLike.objects.filter(guidebook=self)
        if not liked_guidebook:
            return 0
        else:
            return liked_guidebook.count()

    def getShortDescription(self):
        description = self.description
        if len(description) > 100:
            return description[0:100] + '...'
        else:
            return description

    def getTagStr(self):
        tags = []
        if self.tag is None:
            return ''
        for tag in self.tag.all():
            if tag and tag.is_actived:
                tags.append(tag.name)

        if len(tags) > 0:
            return ', '.join(tags)
        else:
            return ''

    def getTags(self):
        tags = []
        if self.tag is None:
            return []
        for tag in self.tag.all():
            if tag and tag.is_actived:
                tags.append(tag.name)
        return tags

    def getCoverImage(self):
        scenes = Scene.objects.filter(guidebook=self)
        if scenes.count() > 0:
            return scenes[0].image_key
        else:
            return None
Example #15
0
class AOI(GeoQBase, Assignment):
    """
    Low-level organizational object. Now (6/1/14) referred to as a 'Workcell'
    """

    STATUS_VALUES = STATUS_VALUES_LIST
    STATUS_CHOICES = [(choice, choice) for choice in STATUS_VALUES]

    PRIORITIES = [(n, n) for n in range(1, 6)]

    analyst = models.ForeignKey(User, blank=True, null=True, help_text="User assigned to work the workcell.")
    job = models.ForeignKey(Job, related_name="aois")
    reviewers = models.ManyToManyField(User, blank=True, null=True, related_name="aoi_reviewers",
                                       help_text='Users that actually reviewed this work.')
    objects = AOIManager()
    polygon = models.MultiPolygonField()
    priority = models.SmallIntegerField(choices=PRIORITIES, max_length=1, default=5)
    status = models.CharField(max_length=15, choices=STATUS_CHOICES, default='Unassigned')

    class Meta:
        verbose_name = 'Area of Interest'
        verbose_name_plural = 'Areas of Interest'
        permissions = (
            ('assign_workcells', 'Assign Workcells'), ('certify_workcells', 'Certify Workcells'),
        )

    def __unicode__(self):
        aoi_obj = '%s - AOI %s' % (self.name, self.id)
        return aoi_obj

    @property
    def log(self):
        return Comment.objects.filter(aoi=self).order_by('created_at')

    @property
    def assignee_name(self):
        if self.assignee_id is None:
            return 'Unknown'
        else:
            if self.assignee_type_id == AssigneeType.USER:
                return User.objects.get(id=self.assignee_id).username
            else:
                return Group.objects.get(id=self.assignee_id).name


    #def save(self):
    # if analyst or reviewer updated, then create policy to give them permission to edit this object.....
    # -- Afterwards -- check how this will work with the views.

    def get_absolute_url(self):
        if self.job.editable_layer_id is None:
            return reverse('aoi-work', args=[self.id])
        else:
            return reverse('aoi-mapedit', args=[self.id])

    def geoJSON(self):
        """
        Returns geoJSON of the feature.
        """

        if self.id is None:
            self.id = 1

        geojson = SortedDict()
        geojson["type"] = "Feature"
        geojson["properties"] = dict(
            id=self.id,
            status=self.status,
            analyst=(self.analyst.username if self.analyst is not None else 'None'),
            assignee=self.assignee_name,
            priority=self.priority,
            delete_url=reverse('aoi-deleter', args=[self.id]))
        geojson["geometry"] = json.loads(self.polygon.json)

        geojson["properties"]["absolute_url"] = self.get_absolute_url()

        return clean_dumps(geojson)

    def logJSON(self):
        return [ob.to_dict() for ob in self.log]

    def properties_json(self):
        """
        Returns json of the feature properties.
        """

        if self.id is None:
            self.id = 1

        properties_main = self.properties or {}
        properties_built = dict(
            status=self.status,
            analyst=(self.analyst.username if self.analyst is not None else 'Unassigned'),
            priority=self.priority)
        prop_json = dict(properties_built.items() + properties_main.items())

        return clean_dumps(prop_json)


    def map_detail(self):
        """
        Get map coordinates for MapEdit
        """
        center = self.polygon.centroid
        return "15/%f/%f" % (center.y, center.x)

    def grid_geoJSON(self):
        """
        Return geoJSON of workcells for export
        """

        if self.id is None:
            self.id = 1

        geojson = SortedDict()
        geojson["type"] = "Feature"
        geojson["properties"] = dict(
            id=self.id,
            priority=self.priority,
            status=self.status)
        geojson["geometry"] = json.loads(self.polygon.json)

        return clean_dumps(geojson)

    def user_can_complete(self, user):
        """
        Returns whether the user can update the AOI as complete.
        """
        return user == self.analyst or user in self.job.reviewers.all()
Example #16
0
class BaseContent(BaseClass):
    """
    Merengue managed content types use relational database inheritance to have
    a non abstract base managed model to be able to selecting all objects with
    only one SQL.

    If you want to create a new content type you should inherits from this model.
    """
    __metaclass__ = BaseContentMeta

    contact_info = models.ForeignKey(ContactInfo,
                                     verbose_name=_('contact info'),
                                     null=True, blank=True, editable=False,
                                     on_delete=models.SET_NULL)
    contact_info.delete_cascade = False
    related_items = models.ManyToManyField('BaseContent',
                                           verbose_name=_('related items'),
                                           null=True, blank=True, editable=False)
    creation_date = models.DateTimeField(verbose_name=_('creation date'),
                                         blank=True, null=True, auto_now_add=True)

    modification_date = models.DateTimeField(verbose_name=_('modification date'),
                                             blank=True, null=True, auto_now=True)

    # this is handled in admin forms
    user_modification_date = models.DateTimeField(verbose_name=_('modification date'),
                                                  blank=True, null=True, editable=False)
    cached_plain_text = models.TextField(verbose_name=_('cached plain text'),
                                   null=True, blank=True, editable=False)

    last_editor = models.ForeignKey(User, null=True, blank=True, editable=False,
                                    related_name='last_edited_content',
                                    verbose_name=_('last editor'),
                                    on_delete=models.SET_NULL)

    # permission global
    adquire_global_permissions = models.BooleanField(_('Adquire global permissions'), default=True)

    # tagging info
    tags = TagField(verbose_name=_('Tags'),
                    help_text=_('Tags will be splitted by commas.'))

    # meta info
    meta_desc = models.TextField(verbose_name=_('meta description'),
                                 null=True, blank=True)

    commentable = models.CharField(_('comments'), max_length=20, default=settings.CONTENT_COMMENTABLE_BY_DEFAULT,
                                   choices=COMMENTABLE_CHOICES, editable=True,
                                   help_text=_('Is that content commentable'))

    # multimedia resources
    multimedia = models.ManyToManyField(BaseMultimedia, blank=True,
                                        verbose_name=_('multimedia'),
                                        through='MultimediaRelation')

    # cached class name from this content
    # this should has null=False, blank=False but in practice this
    # is not possible due to the way the class_name is computed
    # (the object need to get its child class and this is not
    # available until the parent register is saved)
    class_name = models.CharField(verbose_name=_('class name'),
                                  max_length=100, db_index=True,
                                  editable=False, null=True)

    # ranking system
    rank = models.FloatField(verbose_name=_('rank value'),
                             default=100.0, db_index=True,
                             editable=False, blank=False)

    # access control
    owners = models.ManyToManyField(User,
                                    verbose_name=_('owners'),
                                    null=True, blank=True,
                                    related_name='contents_owned')

    participants = models.ManyToManyField(User,
                                          verbose_name=_('participants'),
                                          null=True, blank=True,
                                          related_name='contents_participated')

    position = models.PositiveIntegerField(verbose_name=_('position'),
                                           null=True,
                                           editable=False)

    # structural contents
    no_changeable = models.BooleanField(default=False,
                                        editable=False)
    no_deletable = models.BooleanField(default=False,
                                       editable=False)

    # block control. cached value for controlling if content has special blocks attached
    has_related_blocks = models.BooleanField(default=False, editable=False,
                                             db_index=True)

    # structural fields
    no_changeable_fields = JSONField(null=True, blank=True,
                                     editable=False)

    objects = BaseContentManager()
    store_plain = ('name', 'description', )

    def __init__(self, *args, **kwargs):
        super(BaseContent, self).__init__(*args, **kwargs)
        self._original_status = self.status

    def _generate_cached_plain_text(self):
        if self.id:
            instance = self.get_real_instance()
        else:
            instance = self
        to_store = instance.store_plain
        translated_fields = get_all_translatable_fields(instance.__class__)

        for lang in settings.LANGUAGES:
            setattr(self, 'cached_plain_text_%s' % lang[0], '')

        for field_name in to_store:
            for lang_code, lang_name in settings.LANGUAGES:
                cached = getattr(self, 'cached_plain_text_%s' % lang_code, '')
                if field_name in translated_fields:
                    cached += self._convert_to_plain(getattr(self, '%s_%s' % (field_name, lang_code), ''))
                else:
                    cached += self._convert_to_plain(getattr(self, field_name, ''))
                if cached:
                    cached += " "
                setattr(self, 'cached_plain_text_%s' % lang_code, cached)

    def _convert_to_plain(self, value):
        if value:
            value = force_unicode(value)
            text = re.sub('<br[^>]*>', u'\n', value)
            text = unescape_entities(text)
            text = strip_tags(text)
            text = text.strip()
            text = unicodedata.normalize('NFKD', text.lower()).encode('ascii', 'ignore')
            return text
        return ''

    def _plain_text(self):
        if not self.cached_plain_text:
            self._generate_cached_plain_text()
            self.save()
        return self.cached_plain_text

    plain_text = property(_plain_text)

    class Meta:
        verbose_name = _('base content')
        verbose_name_plural = _('base contents')
        translate = ('cached_plain_text', )
        abstract = False
        #content_view_template = 'content_view.html' # default definition by BaseContentMeta metaclass
        ordering = ('position', get_fallback_fieldname('name'), )
        check_slug_uniqueness = True

    def admin_link_markup(self):
        return '<a href="%s">%s</a>' % (self.get_real_instance().get_admin_absolute_url(), self.name)
    admin_link_markup.allow_tags = True
    admin_link_markup.short_description = _('Name')

    def save(self, update_rank=False, **kwargs):
        """ Do extra logic like setting ordering, update ranking if needed remove some tags """
        # new objects should be added in last place
        if not self.id:
            try:
                ordered = self.__class__.objects.filter(position__isnull=False).order_by('-position')
                last = ordered[0]
                self.position = last.position + 1
            except IndexError:
                pass

        self._generate_cached_plain_text()
        super(BaseContent, self).save(**kwargs)
        object_update_again = False

        if update_rank:
            self.rank = self.calculate_rank()
            object_update_again = True

        tags_field = self._meta.get_field('tags')
        tags_field._save(instance=self)
        # updating the tags may leave some without related items, so we'll delete them
        for tag in Tag.objects.filter(items__isnull=True):
            if hasattr(tag, 'itag'):
                # itag deletion gets rid of the original tag object
                tag.itag.delete()
            else:
                tag.delete()

        # Save thumbnail of main_image in model inherited
        main_image_field = self._meta.get_field('main_image')
        try:
            main_image_field._rename_resize_image(instance=self)
        except OSError:
            pass  # this may fail if the image file does not exist

        if object_update_again:
            non_pks = [f for f in self._meta.local_fields if not f.primary_key]
            if non_pks:
                # we force an update since we already did an insert
                super(BaseContent, self).save(force_update=True)

    def validate_unique(self, exclude=None):
        """ Check the slug uniqueness """
        errors = {}
        try:
            super(BaseContent, self).validate_unique(exclude)
        except ValidationError, validation_errors:
            errors = validation_errors.update_error_dict(errors)
        if self._meta.check_slug_uniqueness:
            # validate that slug is unique in the model
            content_with_same_slug = self.__class__.objects.filter(slug=self.slug).exclude(pk=self.pk).exists()
            if content_with_same_slug:
                errors.setdefault('slug', []).append(ugettext(u'Please set other slug. This slug has been assigned'))
        if errors:
            raise ValidationError(errors)
Example #17
0
class Project(GeoQBase):
    """
    Top-level organizational object.
    """

    PROJECT_TYPES = [
        ("Hurricane/Cyclone", "Hurricane/Cyclone"),
        ("Tornado", "Tornado"),
        ("Earthquake", "Earthquake"),
        ("Extreme Weather", "Extreme Weather"),
        ("Fire", "Fire"),
        ("Flood", "Flood"),
        ("Tsunami", "Tsunami"),
        ("Volcano", "Volcano"),
        ("Pandemic", "Pandemic"),
        ("Exercise", "Exercise"),
        ("Special Event", "Special Event"),
        ("Training", "Training"),
    ]

    project_type = models.CharField(max_length=50, choices=PROJECT_TYPES)
    private = models.BooleanField(default=False, help_text="Check this to make this project 'Private' and available only to users assigned to it.")
    project_admins = models.ManyToManyField(
        User, blank=True, null=True,
        related_name="project_admins", help_text='User that has admin rights to project.')
    contributors = models.ManyToManyField(
        User, blank=True, null=True,
        related_name="contributors", help_text='User that will be able to take on jobs.')

    class Meta:
        permissions = (
            ('open_project', 'Open Project'), ('close_project', 'Close Project'),
            ('archive_project', 'Archive Project'),
        )
        ordering = ('-created_at',)

    @property
    def jobs(self):
        return Job.objects.filter(project=self)

    @property
    def job_count(self):
        return self.jobs.count()

    @property
    def user_count(self):
        return User.objects.filter(analysts__project__id=self.id).distinct().count()

    @property
    def aois(self):
        return AOI.objects.filter(job__project__id=self.id)

    @property
    def aoi_count(self):
        return self.aois.count()

    @property
    def aois_envelope(self):
        return MultiPolygon([n.aois_envelope() for n in self.jobs if n.aois.count()])

    @property
    def aois_envelope_by_job(self):
        jobs = []
        for job in self.jobs:
            if job.aois.count():
                job_envelope = job.aois_envelope()
                envelope_string = job_envelope.json
                if envelope_string:
                    job_poly = json.loads(envelope_string)
                    job_poly['properties'] = {"job_id": str(job.id), "link": str(job.get_absolute_url()),
                                              "name": str(job.name)}
                    jobs.append(job_poly)
        return clean_dumps(jobs, ensure_ascii=True)

    def get_absolute_url(self):
        return reverse('project-detail', args=[self.id])

    def get_update_url(self):
        return reverse('project-update', args=[self.id])
Example #18
0
class Polygon(models.Model):
    """
    Polygon represent geo object,
    and could refer to collection of lower polygons
    """

    # Polygon as polygon
    polygon_id = models.CharField(max_length=50, primary_key=True)
    organizations = models.ManyToManyField(Organization, blank=True)
    shape = models.PolygonField(null=True, blank=True)
    centroid = models.PointField(null=True, blank=True)
    address = models.CharField(max_length=800, null=True, blank=True)
    layer = models.ForeignKey('self', blank=True, null=True)

    # Polygon as layer
    country = 0
    region = 1
    area = 2
    district = 3
    building = 4
    LEVEL = ((country, _("Root polygon")), (region, _("Regions of country")),
             (area, _("Subregions or big sities")),
             (district, _("Towns or districts of city")), (building,
                                                           _("Houses")))
    level = models.IntegerField(choices=LEVEL, default=building)

    is_verified = models.BooleanField(default=True)
    updated = models.DateTimeField(auto_now=True)

    objects = models.GeoManager()

    # moderation_filter
    @property
    def total_claims(self):
        claims = 0
        if self.level == self.building:
            # claims += sum([x.claims for x in self.organizations.all()])
            # claims =  self.organizations.filter(claim__moderation__in=Moderator.allowed_statuses()).count()
            claims = get_claims_for_poly(self.polygon_id)
        else:
            # cached = cache.get('claims_for::%s' % self.polygon_id)
            cached = None
            if cached is not None:
                claims = cached
            else:
                # childs = self.polygon_set.all()
                # for child in childs:
                # claims += child.total_claims
                try:
                    claims = get_sum_for_layers([self.polygon_id],
                                                self.level)[self.polygon_id]
                except KeyError:
                    claims = 0

                # cache.set('claims_for::%s' % self.polygon_id, claims, 300)
        return claims

    @staticmethod
    def color_spot(value, max_value):
        if max_value:
            percent = value * 100 / max_value
        else:
            percent = 0

        if percent <= 20:
            return 'green'
        elif percent <= 70:
            return 'yellow'
        else:
            return 'red'

    def first_organization(self):
        orgs = self.organizations.all()
        if orgs:
            return orgs[0]
        else:
            return None

    def __str__(self):
        return 'Polygon ' + str(self.polygon_id)
Example #19
0
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
Example #20
0
class RouteSegment(models.Model):
    start = models.ForeignKey('stops.Stop', related_name='segments_starting')
    end = models.ForeignKey('stops.Stop', related_name='segments_ending')
    route = models.ForeignKey(Route, related_name="segments")
    stops = models.ManyToManyField('stops.Stop', through='SegmentStop')
Example #21
0
class Place(Entity):
    chain = models.ForeignKey(Chain, blank=True, null=True, on_delete=models.CASCADE)
    location = models.PointField()
    features = models.ManyToManyField("Feature")
Example #22
0
class UnidadeProducao(Propriedade):

    QUALIDADE_AGUA = (
        ('Boa', 'Boa'),
        ('Regular', 'Regular'),
        ('Ruim', 'Ruim'),
    )

    beneficiario = ChainedForeignKey(Beneficiario,
                                     chained_field="municipio",
                                     chained_model_field="municipio",
                                     verbose_name=u'Beneficiário')
    participacao = models.DecimalField(u'Participação %',
                                       max_digits=8,
                                       decimal_places=2,
                                       blank=True,
                                       null=True)
    tituloDominio = models.CharField(u'Título de Domínio',
                                     max_length=16,
                                     blank=True)
    dataRegistro = models.DateField(u'Data de Registro', blank=True, null=True)
    registro = models.CharField(u'Registro', max_length=16, blank=True)
    receitaFederal = models.CharField(u'Nº Receita Federal (ITR)',
                                      max_length=16)
    qualidadeAgua = models.CharField(u'Qualidade da Água',
                                     max_length=8,
                                     choices=QUALIDADE_AGUA,
                                     blank=True)
    destinoLixo = models.ManyToManyField(DestinoLixo,
                                         verbose_name=u'Destino do Lixo',
                                         blank=True)
    utilizacaoAgrotoxico = models.ManyToManyField(
        Agrotoxico, verbose_name=u'Utilização de Agrotóxicos', blank=True)
    destinoEmbalagemAgrotoxico = models.ManyToManyField(
        DestinoEmbalagemAgrotoxico,
        verbose_name=u'Destino da Embalagem de Agrotóxico',
        blank=True)
    preparoSolo = models.ManyToManyField(PreparoSolo,
                                         verbose_name=u'Preparo do Solo',
                                         blank=True)
    areaErosao = models.DecimalField(u'Área com Erosão',
                                     max_digits=8,
                                     decimal_places=2,
                                     blank=True,
                                     null=True,
                                     default='0.0')
    praticaConservacaoSolo = models.ManyToManyField(
        PraticaConservacaoSolo,
        verbose_name=u'Pratica de Conservação do Solo',
        blank=True)
    insumosOrganicos = models.ManyToManyField(
        InsumosOrganicos, verbose_name=u'Insumos Orgânicos', blank=True)
    rotacaoCultura = models.BooleanField(u'Rotação de Cultura', blank=True)
    utilizacaoArvores = models.ManyToManyField(
        UtilizacaoArvores, verbose_name=u'Utilização de Árvores', blank=True)

    class Meta:
        verbose_name = u'Unidade de Produção Familiar'
        verbose_name_plural = u'Unidades de Produção Familiar'
        ordering = ['beneficiario__denominacao']

    def get_absolute_url(self):
        return '/mapagro/unidade/%i/' % self.id

    def get_beneficiario_url(self):
        return '/mapagro/beneficiario/%i/' % self.beneficiario.id

    def get_renda_url(self):
        return '/mapagro/renda/%i/%i/' % (self.id, self.beneficiario.id)
Example #23
0
class Route(DescriptionedModel):
    points = models.ManyToManyField('Point',
                                    through='RoutePointM2M',
                                    verbose_name=_('points'))
    center = LocationField(verbose_name=_('center'),
                           based_fields=['name'],
                           default='POINT(0.0 0.0)')
    saved_by = models.ManyToManyField(Tourist,
                                      through='SavedRoutes',
                                      blank=True,
                                      related_name='saved_by',
                                      verbose_name=_('saved_by'))
    tourist = models.ForeignKey(Tourist,
                                on_delete=models.CASCADE,
                                verbose_name=_('tourist'))
    popularity = models.PositiveIntegerField(_('popularity'),
                                             blank=True,
                                             null=True)
    cost = models.PositiveIntegerField(_('cost'), blank=True, null=True)
    extremality = models.PositiveSmallIntegerField(_('extremality'),
                                                   blank=True,
                                                   null=True)

    objects = models.GeoManager()

    @cached_property
    def formatted_points(self):
        data = dict(points=[])
        for p in self.tourist_points:
            point = dict(location=p.location.get_coords())
            point['name'] = p.name
            point['description'] = p.description
            data['points'].append(point)
        return data

    @cached_property
    def length(self):
        # километры
        length = sum(
            a.location.distance(b.location)
            for (a, b) in self._pairwise(self.tourist_points)) * 100
        return round(length)

    @cached_property
    def pairwised_points(self):
        """route.points.all() -> (point0, points1), (point1, point2), (point2, point3), ..."""
        return self._pairwise(self.tourist_points)

    @staticmethod
    def _pairwise(iterable):
        """iterable -> (iterable[0], iterable[1]), (iterable[1], iterable[2]), ...)"""
        a, b = tee(iterable)
        next(b, None)
        return zip(a, b)

    @cached_property
    def tourist_points(self):
        return self.points.filter(companypoint__isnull=True,
                                  adminpoint__isnull=True)

    @cached_property
    def company_points(self):
        return self.points.filter(companypoint__isnull=False,
                                  adminpoint__isnull=True)

    @cached_property
    def admin_points(self):
        return self.points.filter(adminpoint__isnull=False,
                                  companypoint__isnull=True)

    def __str__(self):
        return self.name
Example #24
0
class Trip(models.Model):
    route = models.ForeignKey(Route, models.CASCADE)
    inbound = models.BooleanField(default=False)
    journey_pattern = models.CharField(max_length=255, blank=True)
    ticket_machine_code = models.CharField(max_length=255, blank=True, db_index=True)
    block = models.CharField(max_length=255, blank=True, db_index=True)
    destination = models.ForeignKey('busstops.StopPoint', models.SET_NULL, null=True, blank=True)
    calendar = models.ForeignKey(Calendar, models.CASCADE)
    sequence = models.PositiveSmallIntegerField(null=True, blank=True)
    notes = models.ManyToManyField(Note, blank=True)
    start = SecondsField()
    end = SecondsField()
    garage = models.ForeignKey('Garage', models.SET_NULL, null=True, blank=True)

    def __str__(self):
        return format_timedelta(self.start)

    def start_time(self):
        return format_timedelta(self.start)

    def end_time(self):
        return format_timedelta(self.end)

    class Meta:
        index_together = (
            ('route', 'start', 'end'),
        )

    def __cmp__(a, b):
        """Compare two journeys"""
        if a.sequence is not None and a.sequence is not None:
            a_time = a.sequence
            b_time = b.sequence
        else:
            a_time = a.start
            b_time = b.start
            a_times = a.stoptime_set.all()
            b_times = b.stoptime_set.all()
            if a_times and b_times and a_times[0].get_key() != b_times[0].get_key():
                if a.destination_id == b.destination_id:
                    a_time = a.end
                    b_time = b.end
                else:
                    times = {time.get_key(): time.arrival_or_departure() for time in a_times}
                    for time in b_times:
                        key = time.get_key()
                        if key in times:
                            a_time = times[key]
                            b_time = time.arrival_or_departure()
                            break
        if a_time > b_time:
            return 1
        if a_time < b_time:
            return -1
        return 0

    def copy(self, start):
        difference = start - self.start
        new_trip = Trip.objects.get(id=self.id)
        times = list(new_trip.stoptime_set.all())
        new_trip.id = None
        new_trip.start += difference
        new_trip.end += difference
        new_trip.save()
        for time in times:
            time.id = None
            time.arrival += difference
            time.departure += difference
            time.trip = new_trip
            time.save()

    def __repr__(self):
        return str(self.start)

    def get_absolute_url(self):
        return reverse('trip_detail', args=(self.id,))
Example #25
0
class App(models.Model):
    def only_filename(instance, filename):
        return filename

    name = models.CharField(max_length=200, null=True, blank=True)
    title = models.CharField(max_length=200, null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    short_description = models.TextField(null=True, blank=True)
    app_url = models.URLField(null=True, blank=True)
    author = models.CharField(max_length=200, null=True, blank=True)
    author_website = models.URLField(null=True, blank=True)
    license = models.CharField(max_length=200, null=True, blank=True)
    tags = TaggableManager()
    date_installed = models.DateTimeField(
        'Date Installed', auto_now_add=True, null=True)
    installed_by = models.ForeignKey(
        geonode_settings.AUTH_USER_MODEL, null=True, blank=True)
    single_instance = models.BooleanField(
        default=False, null=False, blank=False)
    category = models.ManyToManyField(
        AppType, related_name='apps')
    license = models.CharField(max_length=100, null=True, blank=True)
    status = models.CharField(
        max_length=100, blank=False, null=False, default='Alpha')
    owner_url = models.URLField(null=True, blank=True)
    help_url = models.URLField(null=True, blank=True)
    app_img_url = models.TextField(max_length=1000, blank=True, null=True)
    rating = models.IntegerField(default=0, null=True, blank=True)
    contact_name = models.CharField(max_length=200, null=True, blank=True)
    contact_email = models.EmailField(null=True, blank=True)
    version = models.CharField(max_length=10)
    store = models.ForeignKey(AppStore, null=True)
    order = models.IntegerField(null=True, default=0)
    default_config = JSONField(default={})

    class meta(object):
        ordering = ['order']

    def __str__(self):
        return self.title

    def __unicode__(self):
        return self.title

    @property
    def settings_url(self):
        try:
            return reverse("%s_settings" % self.name)
        except BaseException as e:
            logger.error(e.message)
            return None

    @property
    def new_url(self):
        try:
            return reverse("%s.new" % self.name)
        except BaseException as e:
            logger.error(e.message)
            return None

    _apps_config = None

    @property
    def apps_config(self):
        if App._apps_config is None:
            App._apps_config = AppsConfig()
        return App._apps_config

    @property
    def config(self):
        return self.apps_config.get_by_name(self.name)
Example #26
0
class Species(OptionalPictogramMixin):
    SPECIES = 1
    REGULATORY = 2

    name = models.CharField(max_length=250,
                            db_column='nom',
                            verbose_name=_(u"Name"))
    period01 = models.BooleanField(default=False,
                                   db_column='periode01',
                                   verbose_name=_(u"January"))
    period02 = models.BooleanField(default=False,
                                   db_column='periode02',
                                   verbose_name=_(u"February"))
    period03 = models.BooleanField(default=False,
                                   db_column='periode03',
                                   verbose_name=_(u"March"))
    period04 = models.BooleanField(default=False,
                                   db_column='periode04',
                                   verbose_name=_(u"April"))
    period05 = models.BooleanField(default=False,
                                   db_column='periode05',
                                   verbose_name=_(u"May"))
    period06 = models.BooleanField(default=False,
                                   db_column='periode06',
                                   verbose_name=_(u"June"))
    period07 = models.BooleanField(default=False,
                                   db_column='periode07',
                                   verbose_name=_(u"July"))
    period08 = models.BooleanField(default=False,
                                   db_column='periode08',
                                   verbose_name=_(u"August"))
    period09 = models.BooleanField(default=False,
                                   db_column='periode09',
                                   verbose_name=_(u"September"))
    period10 = models.BooleanField(default=False,
                                   db_column='periode10',
                                   verbose_name=_(u"October"))
    period11 = models.BooleanField(default=False,
                                   db_column='periode11',
                                   verbose_name=_(u"November"))
    period12 = models.BooleanField(default=False,
                                   db_column='periode12',
                                   verbose_name=_(u"Decembre"))
    practices = models.ManyToManyField(SportPractice,
                                       db_table='s_r_espece_pratique_sportive',
                                       verbose_name=_(u"Sport practices"))
    url = models.URLField(blank=True, verbose_name="URL")
    radius = models.IntegerField(db_column='rayon',
                                 blank=True,
                                 null=True,
                                 verbose_name=_(u"Bubble radius"),
                                 help_text=_(u"meters"))
    category = models.IntegerField(
        verbose_name=_(u"Category"),
        db_column='categorie',
        editable=False,
        default=SPECIES,
        choices=((SPECIES, pgettext_lazy(u"Singular",
                                         u"Species")), (REGULATORY,
                                                        _(u"Regulatory"))))
    eid = models.CharField(verbose_name=_(u"External id"),
                           max_length=1024,
                           blank=True,
                           null=True,
                           db_column='id_externe')

    class Meta:
        ordering = ['name']
        db_table = 's_b_espece_ou_suite_zone_regl'
        verbose_name = pgettext_lazy(u"Singular", u"Species")
        verbose_name_plural = _(u"Species")

    def __unicode__(self):
        return self.name

    def pretty_period(self):
        return u", ".join([
            unicode(
                self._meta.get_field('period{:02}'.format(p)).verbose_name)
            for p in range(1, 13) if getattr(self, 'period{:02}'.format(p))
        ])

    def pretty_practices(self):
        return u", ".join(
            [unicode(practice) for practice in self.practices.all()])
Example #27
0
class Site(BaseModel):

    # Formerly the SculptureSite model.

    # QAZ: How, when, by whom and why are site_ids assigned?
    site_id = models.CharField(max_length=16, blank=True)
    status = models.ForeignKey('SiteStatus')
    visit_date = models.CharField(max_length=216,
                                  blank=True,
                                  help_text='Eg: 01 Jan 2013',
                                  verbose_name='visit date(s)')
    authors = models.ManyToManyField(Contributor,
                                     blank=True,
                                     related_name='sites')
    fieldworker_may_2017 = models.CharField(
        max_length=2048,
        blank=True,
        help_text="Fieldworkers as of May 2018",
        verbose_name="May 2018 Fieldworkers")  # I know 2017 != 2018
    name = models.CharField(max_length=256)
    country = models.ForeignKey(Country)
    grid_reference = models.CharField('national grid reference',
                                      max_length=25,
                                      blank=True,
                                      help_text='E.g. SO 123 321 or N 31 22')
    latitude = models.FloatField(blank=True, null=True)
    longitude = models.FloatField(blank=True, null=True)
    location = models.PointField(blank=True, null=True)
    regions = models.ManyToManyField(Region,
                                     blank=True,
                                     related_name='sites',
                                     through='SiteRegion')
    dioceses = models.ManyToManyField(Diocese,
                                      blank=True,
                                      related_name='sites',
                                      through='SiteDiocese')
    dedications = models.ManyToManyField(Dedication,
                                         blank=True,
                                         related_name='sites',
                                         through='SiteDedication')
    settlement = models.ForeignKey(Settlement,
                                   blank=True,
                                   null=True,
                                   help_text='Type of building/monument',
                                   related_name='sites')
    description = mezzanine.core.fields.RichTextField(blank=True)
    history = mezzanine.core.fields.RichTextField(blank=True)
    comments = mezzanine.core.fields.RichTextField(blank=True)
    fieldworker_notes = models.TextField(
        blank=True,
        help_text=
        'Admin use only; not published. Please add date and initials to all comments.'
    )
    editor_notes = models.TextField(
        blank=True,
        help_text=
        'Admin use only; not published. Please add date and initials to all comments.'
    )
    glossary_terms = models.ManyToManyField(GlossaryTerm,
                                            blank=True,
                                            editable=False,
                                            related_name='sites')

    objects = models.GeoManager()
    published = PublishedSiteManager()

    bng_proj = pyproj.Proj(init='epsg:27700')
    ing_proj = pyproj.Proj(init='epsg:29901')
    geo_proj = pyproj.Proj(init='epsg:4326')

    title = models.CharField(blank=True, null=True, max_length=256)

    class Meta:
        app_label = 'sculpture'
        ordering = ['name']

    def __str__(self):
        return self.name

    def _convert_refs(self):
        for field in ('description', 'history', 'comments'):
            content = sculpture.utils.convert_refs_to_links(
                getattr(self, field))
            setattr(self, field, content)

    def _derive_gis_data(self):
        """Sets latitude, longitude and point fields, derived from
        grid_reference field value."""
        result = re.search(sculpture.constants.GRID_PATTERN,
                           self.grid_reference)
        if result is not None:
            if result.group('bng'):
                proj = self.bng_proj
                grid_group = 2
                easting_group = 3
                northing_group = 4
            else:
                proj = self.ing_proj
                grid_group = 6
                easting_group = 7
                northing_group = 8
            easting, northing = self._expand_grid_reference(
                result.group(grid_group), result.group(easting_group),
                result.group(northing_group))
            longitude, latitude = pyproj.transform(proj, self.geo_proj,
                                                   easting, northing)
            self.location = Point(longitude, latitude, srid=4326)
            self.latitude = latitude
            self.longitude = longitude

    def _expand_grid_reference(self, grid, easting, northing):
        """Returns full easting and northing values (in metres) by
        joining `easting` and `northing` to `grid`.

        :param grid: BNG or ING grid label
        :type grid: `str`
        :param easting: easting value relative to `grid`
        :type easting: `str`
        :param northing: northing value relative to `grid`
        :type northing: `str`
        :rtype: `tuple` of `int`

        """
        base = sculpture.constants.NG_TILES.get(grid, '')
        # easting and northing may be a variable number of digits, so
        # convert them to the appropriate number of metres. 23 =
        # 23000m, 234 = 23400m, etc.
        easting = int(easting) * 10**(5 - len(easting))
        northing = int(northing) * 10**(5 - len(northing))
        # Each grid is 100km x 100km.
        full_easting = base[0] * 100000 + easting
        full_northing = base[1] * 100000 + northing
        return full_easting, full_northing

    def feature_sets(self):
        """Returns the FeatureSets that are related to this Site.

        Used to generate search facets, since relations of relations
        are not handled by Haystack, apparently.

        """
        from .feature_set import FeatureSet
        return FeatureSet.objects.filter_by_site(self.id)

    @models.permalink
    def get_absolute_url(self):
        return ('site_display', (), {'site_id': str(self.id)})

    def get_dedication_by_period(self, period_name):
        """Returns this site's dedications for the period
        `period_name`.

        :rtype: `QuerySet`

        """
        try:
            period = Period.objects.get(name=period_name)
        except Period.DoesNotExist:
            return Dedication.objects.none()
        return self.sitededication_set.filter(period=period)

    def get_dedication_now(self):
        """Returns this site's dedications associated with the period
        "now".

        :rtype: `QuerySet`

        """
        return self.get_dedication_by_period(sculpture.constants.DATE_NOW)

    def get_dedications(self):
        """Returns this site's dedications for all periods, grouped by
        period.

        :rtype: `dict`

        """
        dedications = {}
        for period in (sculpture.constants.DATE_MEDIEVAL,
                       sculpture.constants.DATE_NOW):
            dedications[period] = self.get_dedication_by_period(period)
        return dedications

    def get_features(self):
        """Returns this site's features organised into nested
        SortedDicts by feature set."""
        features = SortedDict()
        features['features'] = []
        # Iterate over Features, which are in the desired final order,
        # and group them according to the FeatureSet hierarchy. This
        # grouping removes duplication (ie, each FeatureSet will occur
        # only once), which may change the specified order. The first
        # occurrence of a FeatureSet determines its placement in the
        # order.
        for feature in self.features.all():
            fs_rank = features
            feature_sets = feature.get_feature_set_hierarchy()
            i = 1
            for feature_set in feature_sets:
                container = SortedDict()
                fs_rank = fs_rank.setdefault(feature_set, container)
                if 'features' not in fs_rank:
                    fs_rank['features'] = []
                i += 1
            fs_rank['features'].append(feature)
        return features

    def get_images(self):
        from .feature_image import FeatureImage
        return list(self.images.all()) + list(
            FeatureImage.objects.filter(feature__site=self))

    def get_images_features_first(self):
        from .feature_image import FeatureImage
        return list(FeatureImage.objects.filter(feature__site=self)) + list(
            self.images.all())

    def get_region_by_period(self, period_name):
        """Returns this site's regions of period `period_name`.

        :param period_name: name of period
        :type period_name: `str`
        :rtype: `QuerySet`

        """
        try:
            period = Period.objects.get(name=period_name)
        except Period.DoesNotExist:
            return Region.objects.none()
        return self.siteregion_set.filter(period=period)

    def get_region_traditional(self):
        """Returns this site's regions associated with the "traditional"
        period.

        :rtype: `QuerySet`

        """
        counties = self.get_region_by_period(
            sculpture.constants.DATE_TRADITIONAL)
        return counties

    def get_tags(self, user):
        """Returns a QuerySet of SiteTags associated with this site
        and belonging to `user`.

        :param user: user profile
        :type user: `UserProfile`

        """
        return self.tags.filter(user=user)

    def get_title(self):
        """Returns the title of the site.

        The title is composed of the dedication, name and region.

        :rtype: `unicode`

        """
        region = sculpture.utils.get_first_name(self.get_region_traditional(),
                                                'region')
        dedication = sculpture.utils.get_first_name(self.get_dedication_now(),
                                                    'dedication')
        title = ', '.join([dedication, self.name, region])
        return title.strip(' ,')

    def _link_glossary(self):
        """Recreates links to the glossary within the rich text fields
        for this site.

        Changes the content of some fields on self, but does not save
        self.

        Changes, and saves, the Features of this site.

        """
        terms = GlossaryTerm.objects.get_regexps()
        all_used_terms = []
        for field in ('description', 'history', 'comments'):
            text, used_terms = sculpture.utils.add_glossary_terms(
                getattr(self, field), terms)
            setattr(self, field, text)
            all_used_terms.extend(used_terms)
        for feature in self.features.all():
            all_used_terms.extend(feature.link_glossary(terms))
            feature.save()
        return list(set(all_used_terms))

    def save(self, *args, **kwargs):
        # Relink glossary terms.
        glossary_set = False
        try:
            glossary_terms = self._link_glossary()
            glossary_set = True
        except:
            pass
        self._convert_refs()
        self._derive_gis_data()
        self.title = self.get_title()
        super(Site, self).save(*args, **kwargs)

        if glossary_set:
            # Set the glossary terms used in this site report.
            self.glossary_terms.clear()
            self.glossary_terms.add(*glossary_terms)
Example #28
0
class Job(GeoQBase, Assignment):
    """
    Mid-level organizational object.
    """

    GRID_SERVICE_VALUES = ['usng', 'mgrs']
    GRID_SERVICE_CHOICES = [(choice, choice) for choice in GRID_SERVICE_VALUES]
    EDITORS = ['geoq','osm']
    EDITOR_CHOICES = [(choice, choice) for choice in EDITORS]

    analysts = models.ManyToManyField(User, blank=True, null=True, related_name="analysts")
    teams = models.ManyToManyField(Group, blank=True, null=True, related_name="teams")
    reviewers = models.ManyToManyField(User, blank=True, null=True, related_name="reviewers")
    progress = models.SmallIntegerField(max_length=2, blank=True, null=True)
    project = models.ForeignKey(Project, related_name="project")
    grid = models.CharField(max_length=5, choices=GRID_SERVICE_CHOICES, default=GRID_SERVICE_VALUES[0],
                            help_text='Select usng for Jobs inside the US, otherwise use mgrs')
    tags = models.CharField(max_length=50, blank=True, null=True, help_text='Useful tags to search social media with')
    editor = models.CharField(max_length=20, help_text='Editor to be used for creating features', choices=EDITOR_CHOICES, default=EDITOR_CHOICES[0])
    editable_layer = models.ForeignKey( 'maps.EditableMapLayer', blank=True, null=True)

    map = models.ForeignKey('maps.Map', blank=True, null=True)
    feature_types = models.ManyToManyField('maps.FeatureType', blank=True, null=True)
    required_courses = models.ManyToManyField(Training, blank=True, null=True, help_text="Courses that must be passed to open these cells")

    class Meta:
        permissions = (

        )
        ordering = ('-created_at',)

    def get_absolute_url(self):
        return reverse('job-detail', args=[self.id])

    def get_update_url(self):
        return reverse('job-update', args=[self.id])

    def aois_geometry(self):
        return self.aois.all().collect()

    def aois_envelope(self):
        """
        Returns the envelope of related AOIs geometry.
        """
        return getattr(self.aois.all().collect(), 'envelope', None)

    def aoi_count(self):
        return self.aois.count()

    @property
    def aoi_counts_html(self):
        count = defaultdict(int)
        for cell in AOI.objects.filter(job__id=self.id):
            count[cell.status] += 1

        return str(', '.join("%s: <b>%r</b>" % (key, val) for (key, val) in count.iteritems()))

    @property
    def user_count(self):
        return self.analysts.count()

    @property
    def base_layer(self):
        if self.map is not None and self.map.layers is not None:
            layers = sorted([l for l in self.map.layers if l.is_base_layer], key = lambda x: x.stack_order)
            if len(layers) > 0:
                layer = layers[0].layer
                return [layer.name, layer.url, layer.attribution]
            else:
                return []
        else:
            return []

    def features_table_html(self):
        counts = {}

        for feature_item in self.feature_set.all():
            status = str(feature_item.status)
            featuretype = str(feature_item.template.name)
            if not featuretype in counts:
                counts[featuretype] = {}
            if not status in counts[featuretype]:
                counts[featuretype][status] = 0
            counts[featuretype][status] += 1

        #TODO: Also return this as JSON
        if len(counts):
            output = "<table class='job_feature_list'>"

            header = "<th><i>Feature Counts</i></th>"
            for (featuretype, status_obj) in counts.iteritems():
                header = header + "<th><b>" + cgi.escape(featuretype) + "</b></th>"
            output += "<tr>" + header + "</tr>"

            for status in STATUS_VALUES_LIST:
                status = str(status)
                row = "<td><b>" + status + "</b></td>"
                for (featuretype, status_obj) in counts.iteritems():
                    if status in status_obj:
                        val = status_obj[status]
                    else:
                        val = 0
                    row += "<td>" + cgi.escape(str(val)) + "</td>"
                output += "<tr>" + row + "</tr>"
            output += "</table>"
        else:
            output = ""

        return output

    def complete(self):
        """
        Returns the completed AOIs.
        """
        return self.aois.filter(status='Completed')

    def in_work(self):
        """
        Returns the AOIs currently being worked on or in review
        """
        return self.aois.filter(Q(status='In work') | Q(status='Awaiting review') | Q(status='In review'))

    def in_work_count(self):
        return self.in_work().count()

    def complete_count(self):
        return self.complete().count()

    def complete_percent(self):
        if self.aois.count() > 0:
            return round(float(self.complete().count() * 100) / self.aois.count(), 2)
        return 0.0

    def total_count(self):
        return self.aois.count()

    def geoJSON(self, as_json=True):
        """
        Returns geoJSON of the feature.
        """

        geojson = SortedDict()
        geojson["type"] = "FeatureCollection"
        geojson["features"] = [json.loads(aoi.geoJSON()) for aoi in self.aois.all()]

        return clean_dumps(geojson) if as_json else geojson

    def features_geoJSON(self, as_json=True, using_style_template=True):

        geojson = SortedDict()
        geojson["type"] = "FeatureCollection"
        geojson["properties"] = dict(id=self.id)

        geojson["features"] = [n.geoJSON(as_json=False, using_style_template=using_style_template) for n in self.feature_set.all()]

        return clean_dumps(geojson, indent=2) if as_json else geojson

    def grid_geoJSON(self, as_json=True):
        """
        Return geoJSON of grid for export
        """

        geojson = SortedDict()
        geojson["type"] = "FeatureCollection"
        geojson["features"] = [json.loads(aoi.grid_geoJSON()) for aoi in self.aois.all()]

        return clean_dumps(geojson) if as_json else geojson

    def base_layer_object(self):
        """
        create base layer object that can override leaflet base OSM map
        """

        obj = {}
        if len(self.base_layer) > 0:
            obj["layers"] = [self.base_layer]

        return obj
Example #29
0
class Polygon(models.Model):
    """
    Polygon represent geo object,
    and could refer to collection of lower polygons
    """

    # Polygon as polygon
    polygon_id = models.CharField(max_length=50, primary_key=True)
    organizations = models.ManyToManyField(Organization, blank=True)
    shape = models.PolygonField(null=True, blank=True)
    centroid = models.PointField(null=True, blank=True)
    address = models.CharField(max_length=800, null=True, blank=True)
    layer = models.ForeignKey('self', blank=True, null=True)

    # Polygon as layer
    country = 0
    region = 1
    area = 2
    district = 3
    building = 4
    LEVEL = (
        (country, _("Root polygon")),
        (region, _("Regions of country")),
        (area, _("Subregions or big sities")),
        (district, _("Towns or districts of city")),
        (building, _("Houses"))
    )
    level = models.IntegerField(choices=LEVEL, default=building)
    is_verified = models.BooleanField(default=True)
    updated = models.DateTimeField(auto_now=True)

    # outdated fields
    is_default = models.BooleanField(default=False)
    zoom = models.IntegerField(blank=True, null=True)

    claims = models.IntegerField(default=0)

    objects = models.GeoManager()

    @property
    def total_claims(self):
        claims = 0
        if self.level == self.building:
            claims += sum([x.claims for x in self.organizations.all()])

        else:
            cached = cache.get('claims_for::%s' % self.polygon_id)
            if cached is not None:
                claims = cached
            else:
                childs = self.polygon_set.all()
                for child in childs:
                    claims += child.total_claims

                cache.set('claims_for::%s' % self.polygon_id, claims)

        return claims

    def color_spot(self, value, max_value):
        if max_value:
            percent = value * 100 / max_value
        else:
            percent = 0

        if percent <= 20:
            return 'green'
        elif percent <= 70:
            return 'yellow'
        else:
            return 'red'

    @property
    def get_color(self):
        cached = cache.get('color_for::%s' % self.polygon_id)
        if cached is not None:
            color = cached
        else:
            if self.layer:
                brothers = self.layer.polygon_set.all()
                max_claims_value = max([x.total_claims for x in brothers])
            else:
                max_claims_value = 0

            color = self.color_spot(
                self.total_claims, max_claims_value)\
                if self.total_claims else 'grey'

            cache.set('color_for::%s' % self.polygon_id, color)

        return color

    # def orgs_count(self):
    #     return self.organizations.all().count()

    def first_organization(self):
        orgs = self.organizations.all()
        if orgs:
            return orgs[0]
        else:
            return None

    def __str__(self):
        return 'Polygon ' + str(self.polygon_id)

    def polygon_to_json(self, shape=True):
        # reverse coordinates for manualy adding polgygons
        if shape and self.shape:
            geometry = json.loads(self.shape.json)
            [x.reverse() for x in geometry["coordinates"][0]]
        else:
            geometry = None

        centroid = list(self.centroid.coords)
        centroid.reverse()

        responce = {
            "type": "Feature",
            "properties": {
                "ID": self.polygon_id,
                "centroid": centroid,
                'address': self.address,
                'parent_id': self.layer.polygon_id if self.layer else None,
                'level': self.level,
                # "polygon_claims": self.claims
            },
            "geometry": geometry
        }

        if self.level == self.building:
            orgs = []
            polygon_claims = 0
            for org in self.organizations.all():
                org_claims = org.claims
                polygon_claims += org_claims
                orgs.append({'id': org.id,
                            'name': org.name,
                             'claims_count': org_claims,
                             # 'claim_types': org.claim_types()
                             'org_type_id': org.org_type.type_id
                             })

            responce["properties"]["organizations"] = orgs
            responce["properties"]["polygon_claims"] = polygon_claims

        else:
            responce["properties"]["polygon_claims"] = self.total_claims

        # print(responce)
        return responce
Example #30
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")