class SearchPoi(DefaultFields):
    displayed_name = models.CharField(max_length=1000)
    bodies = models.ManyToManyField(Body, blank=True)
    osm_id = models.BigIntegerField(null=True, blank=True)
    osm_amenity = models.CharField(null=True, max_length=1000)
    geometry = GeometryField(null=True)
    exclude_from_search = models.BooleanField(default=False)
Esempio n. 2
0
class Data(models.Model):
    geom = GeometryField(default=None)
    sidewalks = models.TextField(default=None)
    correct = models.BooleanField(default=True)
    date = models.DateTimeField()
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    intersection = models.ForeignKey(Intersection, on_delete=models.CASCADE)
class SearchPoi(DefaultFields):
    displayed_name = models.CharField(max_length=1000)
    body = models.ForeignKey(Body,
                             blank=True,
                             null=True,
                             on_delete=models.CASCADE)
    osm_id = models.BigIntegerField(null=True, blank=True)
    osm_amenity = models.CharField(null=True, max_length=1000)
    geometry = GeometryField(null=True)
    exclude_from_search = models.BooleanField(default=False)
class Location(DefaultFields):
    description = models.TextField(null=True, blank=True)
    # Unique field to avoid duplicating addresses through location extraction
    # The 767 is a limitation of InnoDB for indexed columns
    # See https://stackoverflow.com/q/1827063/3549270
    search_str = models.CharField(max_length=767,
                                  null=True,
                                  blank=True,
                                  unique=True)

    street_address = models.CharField(max_length=512, null=True, blank=True)
    postal_code = models.CharField(max_length=512, null=True, blank=True)
    locality = models.CharField(max_length=512, null=True, blank=True)
    room = models.CharField(max_length=512, null=True, blank=True)

    is_official = models.BooleanField()
    osm_id = models.BigIntegerField(null=True, blank=True)
    geometry = GeometryField(default=None)

    def __str__(self):
        return self.description or _("Unknown")

    def short(self) -> str:
        """Tries to return a short description of the adress, with a fallback to the long one"""
        if self.street_address and self.room:
            return "{}, {}".format(self.street_address, self.room)
        else:
            return self.description

    def for_maps(self) -> str:
        """Tries to build a good search string for google maps / open street map"""
        if self.street_address:
            if self.postal_code and self.locality:
                return "{}, {} {}".format(self.street_address,
                                          self.postal_code, self.locality)
            else:
                return self.street_address
        else:
            return self.description

    # noinspection PyUnresolvedReferences
    def coordinates(self) -> Optional[Dict[str, Any]]:
        if self.geometry and self.geometry["type"] == "Point":
            return {
                "lat": self.geometry["coordinates"][1],
                "lon": self.geometry["coordinates"][0],
            }
        else:
            return None
class Location(DefaultFields):
    description = models.TextField(null=True, blank=True)

    street_address = models.CharField(max_length=512, null=True, blank=True)
    postal_code = models.CharField(max_length=512, null=True, blank=True)
    locality = models.CharField(max_length=512, null=True, blank=True)
    room = models.CharField(max_length=512, null=True, blank=True)

    is_official = models.BooleanField()
    osm_id = models.BigIntegerField(null=True, blank=True)
    geometry = GeometryField(default=None)

    def __str__(self):
        return self.description or _("Unknown")

    def short(self) -> str:
        """ Tries to return a short description of the adress, with a fallback to the long one """
        if self.street_address and self.room:
            return "{}, {}".format(self.street_address, self.room)
        else:
            return self.description

    def for_maps(self) -> str:
        """ Tries to build a good search string for google maps / open street map"""
        if self.street_address:
            if self.postal_code and self.locality:
                return "{}, {} {}".format(self.street_address,
                                          self.postal_code, self.locality)
            else:
                return self.street_address
        else:
            return self.description

    # noinspection PyUnresolvedReferences
    def coordinates(self) -> Optional[Dict[str, Any]]:
        if self.geometry and self.geometry["type"] == "Point":
            return {
                "lat": self.geometry["coordinates"][1],
                "lon": self.geometry["coordinates"][0],
            }
        else:
            return None
Esempio n. 6
0
class Location(DefaultFields):
    name = models.CharField(max_length=200)
    short_name = models.CharField(max_length=50)
    description = models.TextField(null=True, blank=True)
    # Need to work around a cyclic import here
    bodies = models.ManyToManyField("mainapp.Body", blank=True)
    is_official = models.BooleanField()
    osm_id = models.BigIntegerField(null=True, blank=True)
    geometry = GeometryField(default=None)

    def __str__(self):
        return self.short_name

    def coordinates(self):
        if self.geometry and self.geometry['type'] == 'Point':
            return {
                "lat": self.geometry['coordinates'][1],
                "lon": self.geometry['coordinates'][0],
            }
        else:
            return None
class Location(DefaultFields):
    short_description = models.TextField(null=True, blank=True)
    description = models.TextField(null=True, blank=True)
    # Need to work around a cyclic import here
    bodies = models.ManyToManyField("mainapp.Body", blank=True)
    is_official = models.BooleanField()
    osm_id = models.BigIntegerField(null=True, blank=True)
    geometry = GeometryField(default=None)
    streetAddress = models.CharField(max_length=512, null=True, blank=True)
    room = models.CharField(max_length=512, null=True, blank=True)
    postalCode = models.CharField(max_length=512, null=True, blank=True)
    locality = models.CharField(max_length=512, null=True, blank=True)

    def __str__(self):
        return self.short_description or self.description or _("Unknown")

    def coordinates(self):
        if self.geometry and self.geometry['type'] == 'Point':
            return {
                "lat": self.geometry['coordinates'][1],
                "lon": self.geometry['coordinates'][0],
            }
        else:
            return None
Esempio n. 8
0
class BaseComment(BaseModel):
    parent_field = None  # Required for factories and API
    parent_model = None  # Required for factories and API
    geojson = GeometryField(blank=True, null=True, verbose_name=_('location'))
    authorization_code = models.CharField(verbose_name=_('authorization code'),  max_length=32, blank=True)
    author_name = models.CharField(verbose_name=_('author name'), max_length=255, blank=True, null=True)
    plugin_identifier = models.CharField(verbose_name=_('plugin identifier'), blank=True, max_length=255)
    plugin_data = models.TextField(verbose_name=_('plugin data'), blank=True)
    label = models.ForeignKey("Label", verbose_name=_('label'), blank=True, null=True)
    language_code = models.CharField(verbose_name=_('language code'), blank=True, max_length=15)
    n_votes = models.IntegerField(
        verbose_name=_('vote count'),
        help_text=_('number of votes given to this comment'),
        default=0,
        editable=False
    )
    n_unregistered_votes = models.IntegerField(
        verbose_name=_('unregistered vote count'),
        help_text=_('number of unregistered votes'),
        default=0,
        editable=False
    )
    voters = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        verbose_name=_('voters'),
        help_text=_('users who voted for this comment'),
        related_name="voted_%(app_label)s_%(class)s",
        blank=True
    )

    class Meta:
        abstract = True

    @property
    def parent(self):
        """
        :rtype: Commentable|None
        """
        return getattr(self, self.parent_field, None)

    @property
    def parent_id(self):
        """
        :rtype: int|str|None
        """
        return getattr(self, "%s_id" % self.parent_field, None)

    def _detect_lang(self):
        try:
            candidates = detect_langs(self.content.lower())
            for candidate in candidates:
                if candidate.lang in [lang['code'] for lang in settings.PARLER_LANGUAGES[None]]:
                    if candidate.prob > settings.DETECT_LANGS_MIN_PROBA:
                        self.language_code = candidate.lang
                    break

        except LangDetectException:
            pass

    def save(self, *args, **kwargs):
        if not (self.plugin_data or self.content or self.label):
            raise ValueError("Comments must have either plugin data, textual content or label")
        if not self.author_name and self.created_by_id:
            self.author_name = (self.created_by.get_display_name() or None)
        if not self.language_code and self.content:
            self._detect_lang()
        return super(BaseComment, self).save(*args, **kwargs)

    def recache_n_votes(self):
        n_votes = self.voters.all().count() + self.n_unregistered_votes
        if n_votes != self.n_votes:
            self.n_votes = n_votes
            self.save(update_fields=("n_votes", "n_unregistered_votes"))

    def recache_parent_n_comments(self):
        if self.parent_id:  # pragma: no branch
            self.parent.recache_n_comments()

    def can_edit(self, request):
        """
        Whether the given request (HTTP or DRF) is allowed to edit this Comment.
        """
        is_authenticated = request.user.is_authenticated()
        if is_authenticated and self.created_by == request.user:
            # also make sure the hearing is still commentable
            try:
                self.parent.check_commenting(request)
            except ValidationError:
                return False
            return True
        return False
Esempio n. 9
0
class GeoDirectoryMixin(models.Model):
    """
    A model with a corresponding entry in Terralego.

    The entry will be updated at every save.
    You can pass `terralego_commit` at False to force not updating the entry.

    This model is designed to be one-way only. This means that it won't update from terralego automatically.
    If you update the entry from somewhere else, you will have to call `_update_from_terralego_entry` manually.
    """

    terralego_id = models.UUIDField(verbose_name=_('Terralego id'), editable=False, null=True)
    terralego_last_update = models.DateTimeField(_('Terralego last update'), editable=False, null=True)
    terralego_geometry = GeometryField(_('Terralego geometry field'), blank=True, null=True)
    terralego_tags = models.TextField(_('Terralego tags'), blank=True, null=True)  # JSON list of tags

    class Meta:
        abstract = True

    # Save/update handling

    def update_from_terralego_data(self, data):
        """
        Set self.geometry and self.tags with the values in data and cache it.

        :param data: the geojson representing the entry
        """
        self.terralego_id = data['id']
        self.terralego_last_update = timezone.now()
        self.terralego_geometry = data['geometry']
        self.terralego_tags = json.dumps(data['properties']['tags'])

    def _update_tags_with_model(self, tags):
        model_path = '{0}.{1}'.format(self._meta.app_label, self._meta.object_name)
        if tags is None:
            tags = []
        if model_path not in tags:
            # Add the model_path to the tags
            tags.insert(0, model_path)
        elif tags[0] != model_path:
            # The model_path is already there but not in first place
            tags.remove(model_path)
            tags.insert(0, model_path)
        return tags

    def update_from_terralego_entry(self):
        """
        Get the terralego entry related to self.terralego_id and update the instance tags and geometry.
        """
        if self.terralego_id is not None and conf.TERRALEGO.get('ENABLED', True):
            data = geodirectory.get_entry(self.terralego_id)
            self.update_from_terralego_data(data)

    def save_to_terralego(self):
        """
        Create or update the entry in terralego, adding the model_path to the tags if needed.
        """
        tags = self.terralego_tags and json.loads(self.terralego_tags) or None
        tags = self._update_tags_with_model(tags)
        self.terralego_tags = json.dumps(tags)  # Save tags in case of error before the update_from_terralego_data
        if self.terralego_id is None:
            data = geodirectory.create_entry(self.terralego_geometry, tags)
        else:
            data = geodirectory.update_entry(self.terralego_id, self.terralego_geometry, tags)
        self.update_from_terralego_data(data)

    def delete_from_terralego(self, set_id_null=True):
        """
        Delete the entry in terralego
        """
        geodirectory.delete_entry(self.terralego_id)
        if set_id_null:
            self.terralego_id = None
            self.save(terralego_commit=False)

    def delete(self, *args, **kwargs):
        if self.terralego_id:
            try:
                self.delete_from_terralego(set_id_null=False)
            except RequestException as e:
                logger.error('Error while deleting from terralego: {0}'.format(e))
        return super(GeoDirectoryMixin, self).delete(*args, **kwargs)

    def save(self, *args, **kwargs):
        terralego_commit = kwargs.pop('terralego_commit', True)
        if terralego_commit and self.terralego_geometry is not None and conf.TERRALEGO.get('ENABLED', True):
            try:
                self.save_to_terralego()
            except RequestException as e:
                logger.error('Error while saving to terralego: {0}'.format(e))
        return super(GeoDirectoryMixin, self).save(*args, **kwargs)

    # Geodirectory methods

    def closest(self, tags=None):
        """
        Get the closest entry of this entry.
        
        :param tags: Optional. A list of tags to filter the entries on which the request is made.
        :return: An instance of GeoDirectoryMixin if the entry is one, or a dict describing the entry.
        """
        try:
            entry = geodirectory.closest(self.terralego_id, tags)
        except RequestException as e:
            return logger.error('Error while getting closest: {0}'.format(e))
        return convert_geodirectory_entry_to_model_instance(entry)
Esempio n. 10
0
class Hearing(StringIdBaseModel, TranslatableModel):
    open_at = models.DateTimeField(verbose_name=_('opening time'),
                                   default=timezone.now)
    close_at = models.DateTimeField(verbose_name=_('closing time'),
                                    default=timezone.now)
    force_closed = models.BooleanField(verbose_name=_('force hearing closed'),
                                       default=False)
    translations = TranslatedFields(
        title=models.CharField(verbose_name=_('title'), max_length=255),
        borough=models.CharField(verbose_name=_('borough'),
                                 blank=True,
                                 default='',
                                 max_length=200),
    )
    servicemap_url = models.CharField(verbose_name=_('service map URL'),
                                      default='',
                                      max_length=255,
                                      blank=True)
    geojson = GeometryField(blank=True, null=True, verbose_name=_('area'))
    organization = models.ForeignKey(Organization,
                                     verbose_name=_('organization'),
                                     related_name="hearings",
                                     blank=True,
                                     null=True)
    labels = models.ManyToManyField("Label",
                                    verbose_name=_('labels'),
                                    blank=True)
    followers = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        verbose_name=_('followers'),
        help_text=_('users who follow this hearing'),
        related_name='followed_hearings',
        blank=True,
        editable=False)
    slug = AutoSlugField(
        verbose_name=_('slug'),
        populate_from='title',
        editable=True,
        unique=True,
        blank=True,
        help_text=_(
            'You may leave this empty to automatically generate a slug'))
    n_comments = models.IntegerField(verbose_name=_('number of comments'),
                                     blank=True,
                                     default=0,
                                     editable=False)
    contact_persons = models.ManyToManyField(ContactPerson,
                                             verbose_name=_('contact persons'),
                                             related_name='hearings')

    objects = BaseModelManager.from_queryset(HearingQueryset)()
    original_manager = models.Manager()

    class Meta:
        verbose_name = _('hearing')
        verbose_name_plural = _('hearings')

    def __str__(self):
        return (self.title or self.id)

    @property
    def closed(self):
        return self.force_closed or not (self.open_at <= now() <=
                                         self.close_at)

    def check_commenting(self, request):
        if self.closed:
            raise ValidationError(
                _("%s is closed and does not allow comments anymore") % self,
                code="hearing_closed")

    def check_voting(self, request):
        if self.closed:
            raise ValidationError(
                _("%s is closed and does not allow voting anymore") % self,
                code="hearing_closed")

    @property
    def preview_code(self):
        if not self.pk:
            return None
        return get_hmac_b64_encoded(self.pk)

    @property
    def preview_url(self):
        if not (self.preview_code
                and hasattr(settings, 'DEMOCRACY_UI_BASE_URL')):
            return ''
        url = urljoin(settings.DEMOCRACY_UI_BASE_URL,
                      '/hearing/%s/?preview=%s' % (self.pk, self.preview_code))
        return format_html('<a href="%s">%s</a>' % (url, url))

    def save(self, *args, **kwargs):
        slug_field = self._meta.get_field('slug')

        # we need to manually use autoslug utils here with ModelManager, because automatic slug populating
        # uses our default manager, which can lead to a slug collision between this and a deleted hearing
        self.slug = generate_unique_slug(slug_field, self, self.slug,
                                         Hearing.original_manager)

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

    def recache_n_comments(self):
        new_n_comments = (self.sections.all().aggregate(
            Sum('n_comments')).get('n_comments__sum') or 0)
        if new_n_comments != self.n_comments:
            self.n_comments = new_n_comments
            self.save(update_fields=("n_comments", ))

    def get_main_section(self):
        try:
            return self.sections.get(type__identifier=InitialSectionType.MAIN)
        except ObjectDoesNotExist:
            return None

    def is_visible_for(self, user):
        if self.published and self.open_at < now():
            return True
        if not user.is_authenticated():
            return False
        if user.is_superuser:
            return True
        user_organization = user.get_default_organization()
        if not (user_organization and self.organization):
            return False
        return user_organization == self.organization
Esempio n. 11
0
class Intersection(models.Model):
    geom = GeometryField()
    rank = models.IntegerField(default=0)
Esempio n. 12
0
class Hearing(Commentable, StringIdBaseModel):
    open_at = models.DateTimeField(verbose_name=_('opening time'),
                                   default=timezone.now)
    close_at = models.DateTimeField(verbose_name=_('closing time'),
                                    default=timezone.now)
    force_closed = models.BooleanField(verbose_name=_('force hearing closed'),
                                       default=False)
    title = models.CharField(verbose_name=_('title'), max_length=255)
    abstract = models.TextField(verbose_name=_('abstract'),
                                blank=True,
                                default='')
    borough = models.CharField(verbose_name=_('borough'),
                               blank=True,
                               default='',
                               max_length=200)
    servicemap_url = models.CharField(verbose_name=_('service map URL'),
                                      default='',
                                      max_length=255,
                                      blank=True)
    geojson = GeometryField(blank=True, null=True, verbose_name=_('area'))

    labels = models.ManyToManyField("Label",
                                    verbose_name=_('labels'),
                                    blank=True)
    followers = models.ManyToManyField(
        settings.AUTH_USER_MODEL,
        verbose_name=_('followers'),
        help_text=_('users who follow this hearing'),
        related_name='followed_hearings',
        blank=True,
        editable=False)

    class Meta:
        verbose_name = _('hearing')
        verbose_name_plural = _('hearings')

    def __str__(self):
        return (self.title or self.id)

    @property
    def closed(self):
        return self.force_closed or not (self.open_at <= now() <=
                                         self.close_at)

    def check_commenting(self, request):
        if self.closed:
            raise ValidationError(
                _("%s is closed and does not allow comments anymore") % self,
                code="hearing_closed")
        super().check_commenting(request)

    @property
    def preview_code(self):
        if not self.pk:
            return None
        return get_hmac_b64_encoded(self.pk)

    @property
    def preview_url(self):
        if not (self.preview_code
                and hasattr(settings, 'DEMOCRACY_UI_BASE_URL')):
            return ''
        url = urljoin(settings.DEMOCRACY_UI_BASE_URL,
                      '/hearing/%s/?preview=%s' % (self.pk, self.preview_code))
        return format_html('<a href="%s">%s</a>' % (url, url))