Esempio n. 1
0
class Person(AuditModel):
    person_guid = models.UUIDField(primary_key=True,
                                   default=uuid.uuid4,
                                   editable=False,
                                   verbose_name="Person UUID")
    first_name = models.CharField(
        max_length=100,
        db_comment=
        ('Legal first name of the well driller or well pump installer who has applied and/or is '
         'registered with the province.'))
    surname = models.CharField(
        max_length=100,
        db_comment=
        ('Legal last name of the well driller or well pump installer who has applied and/or is '
         'registered with the province.'))

    # As per D.A. - temporary fields to hold compliance-related details
    well_driller_orcs_no = models.CharField(
        max_length=25,
        blank=True,
        null=True,
        verbose_name='ORCS File # reference (in context of Well Driller).',
        db_comment=
        ('Well driller\'s unique filing number used in the BC government Operational Records '
         'Classification Systems (ORCS) filing system. Each person has an ORCS number when a file '
         'is started with their correspondence, usually with the application for being '
         'registered. E.g. 3800-25/PUMP DRI W. The standard format for this number is '
         '3800-25/DRI {first 4 characters of last name} {initial of first name}.'
         ))
    pump_installer_orcs_no = models.CharField(
        max_length=25,
        blank=True,
        null=True,
        verbose_name='ORCS File # reference (in context of Pump Installer).',
        db_comment=
        ('Well pump installer\'s unique filing number used in the BC government Operational '
         'Records Classification Systems (ORCS) filing system. Each person has an ORCS number '
         'when a file is started with their correspondence, usually with the application for '
         'being registered. Each person can have a unique ORCS number as a well pump installer '
         'and as a well driller. E.g. 3800-25/PUMP PRIC W. The standard format for this number '
         'is 3800-25/PUMP {first 4 characters of last name} {initial of first name}.'
         ))
    # contact information
    contact_tel = models.CharField(
        blank=True,
        null=True,
        max_length=15,
        verbose_name='Contact telephone number',
        db_comment=
        ('Land line area code and 7 digit phone number provided by the well driller or well pump '
         'installer where they can be contacted.'))
    contact_cell = models.CharField(
        blank=True,
        null=True,
        max_length=15,
        verbose_name='Contact cell number',
        db_comment=
        ('Cell phone area code and 7 digit number provided by the well driller or well pump '
         'installer where they can be contacted.'))
    contact_email = models.EmailField(
        blank=True,
        null=True,
        verbose_name='Email address',
        db_comment='Email address for the well driller or well pump installer.'
    )

    effective_date = models.DateTimeField(
        default=timezone.now,
        null=False,
        db_comment=
        'The date when the registries person record became available for use.')
    expiry_date = models.DateTimeField(
        default=timezone.make_aware(timezone.datetime.max,
                                    timezone.get_default_timezone()),
        null=False,
        db_comment=
        'The date and time after which the record is no longer valid and should not be used.'
    )

    history = GenericRelation(Version)

    class Meta:
        db_table = 'registries_person'
        ordering = ['first_name', 'surname']
        verbose_name_plural = 'People'

    db_table_comment = 'Placeholder table comment.'

    def __str__(self):
        return '%s %s' % (self.first_name, self.surname)

    @property
    def name(self):
        return '%s %s' % (self.first_name, self.surname)
Esempio n. 2
0
class RemoteCkanDataset(models.Model):
    class Meta(object):
        verbose_name = "Jeu de données moissonné"
        verbose_name_plural = "Jeux de données moissonnés"
        unique_together = ('remote_instance', 'dataset')

    remote_instance = models.ForeignKey(
        to='RemoteCkan',
        on_delete=models.CASCADE,
        to_field='id',
    )

    dataset = models.ForeignKey(
        to='Dataset',
        on_delete=models.CASCADE,
        to_field='id',
    )

    remote_dataset = models.UUIDField(
        verbose_name="Ckan UUID",
        editable=False,
        null=True,
        blank=True,
        unique=True,
    )

    remote_organisation = models.SlugField(
        verbose_name="Organisation distante",
        max_length=100,
        blank=True,
        null=True,
    )

    created_by = models.ForeignKey(
        User,
        related_name='creates_dataset_from_remote_ckan',
        verbose_name="Utilisateur",
        null=True,
        on_delete=models.SET_NULL,
    )

    created_on = models.DateTimeField(
        verbose_name="Créé le",
        auto_now_add=True,
    )

    updated_on = models.DateTimeField(
        verbose_name="Mis-à-jour le",
        auto_now_add=True,
    )

    def __str__(self):
        return '{0} - {1}'.format(self.remote_instance, self.dataset)

    @property
    def url(self):
        base_url = self.remote_instance.url
        if not base_url.endswith('/'):
            base_url += '/'
        return reduce(urljoin,
                      [base_url, 'dataset/',
                       str(self.remote_dataset)])
Esempio n. 3
0
class Order(models.Model):
    """
    processing_fee should default to the maximum of base fees in the order but can then be edited mannually
    """
    class OrderStatus(models.TextChoices):
        DRAFT = 'DRAFT', _('Draft')
        PENDING = 'PENDING', _('Pending')
        QUOTE_DONE = 'QUOTE_DONE', _('Quote done')
        READY = 'READY', _('Ready')
        IN_EXTRACT = 'IN_EXTRACT', _('In extract')
        PARTIALLY_DELIVERED = 'PARTIALLY_DELIVERED', _('Partially delivered')
        PROCESSED = 'PROCESSED', _('Processed')
        ARCHIVED = 'ARCHIVED', _('Archived')
        REJECTED = 'REJECTED', _('Rejected')

    title = models.CharField(
        _('title'),
        max_length=255,
        validators=[
            RegexValidator(
                regex=r'^[^<>%$"\(\)\n\r]*$',
                message=_('Title contains forbidden characters'),
            ),
        ])
    description = models.TextField(_('description'), blank=True)
    processing_fee = MoneyField(_('processing_fee'),
                                max_digits=14,
                                decimal_places=2,
                                default_currency='CHF',
                                blank=True,
                                null=True)
    total_without_vat = MoneyField(_('total_without_vat'),
                                   max_digits=14,
                                   decimal_places=2,
                                   default_currency='CHF',
                                   blank=True,
                                   null=True)
    part_vat = MoneyField(_('part_vat'),
                          max_digits=14,
                          decimal_places=2,
                          default_currency='CHF',
                          blank=True,
                          null=True)
    total_with_vat = MoneyField(_('total_with_vat'),
                                max_digits=14,
                                decimal_places=2,
                                default_currency='CHF',
                                blank=True,
                                null=True)
    geom = models.PolygonField(_('geom'), srid=settings.DEFAULT_SRID)
    client = models.ForeignKey(UserModel,
                               models.PROTECT,
                               verbose_name=_('client'),
                               blank=True)
    invoice_contact = models.ForeignKey(Contact,
                                        models.PROTECT,
                                        verbose_name=_('invoice_contact'),
                                        related_name='invoice_contact',
                                        blank=True,
                                        null=True)
    invoice_reference = models.CharField(_('invoice_reference'),
                                         max_length=255,
                                         blank=True)
    email_deliver = models.EmailField(_('email_deliver'),
                                      max_length=254,
                                      blank=True,
                                      null=True)
    order_type = models.ForeignKey(OrderType,
                                   models.PROTECT,
                                   verbose_name=_('order_type'))
    status = models.CharField(_('status'),
                              max_length=20,
                              choices=OrderStatus.choices,
                              default=OrderStatus.DRAFT)
    date_ordered = models.DateTimeField(_('date_ordered'),
                                        blank=True,
                                        null=True)
    date_downloaded = models.DateTimeField(_('date_downloaded'),
                                           blank=True,
                                           null=True)
    date_processed = models.DateTimeField(_('date_processed'),
                                          blank=True,
                                          null=True)
    extract_result = models.FileField(upload_to='extract',
                                      null=True,
                                      blank=True)
    download_guid = models.UUIDField(_('download_guid'), null=True, blank=True)

    class Meta:
        db_table = 'order'
        ordering = ['-date_ordered']
        verbose_name = _('order')

    def _reset_prices(self):
        self.processing_fee = None
        self.total_without_vat = None
        self.part_vat = None
        self.total_with_vat = None

    def set_price(self):
        """
        Sets price information if all items have prices
        """
        self._reset_prices()
        items = self.items.all()
        if items == []:
            return False
        self.total_without_vat = Money(0, 'CHF')
        self.processing_fee = Money(0, 'CHF')
        for item in items:
            if item.base_fee is None:
                self._reset_prices()
                return False
            if item.base_fee > self.processing_fee:
                self.processing_fee = item.base_fee
            self.total_without_vat += item.price
        self.total_without_vat += self.processing_fee
        self.part_vat = self.total_without_vat * settings.VAT
        self.total_with_vat = self.total_without_vat + self.part_vat
        return True

    def quote_done(self):
        """Admins confirmation they have given a manual price"""
        price_is_set = self.set_price()
        if price_is_set:
            self.status = self.OrderStatus.QUOTE_DONE
            self.save()
            send_geoshop_email(_('Geoshop - Quote has been done'),
                               recipient=self.email_deliver
                               or self.client.identity,
                               template_name='email_quote_done',
                               template_data={
                                   'order_id': self.id,
                                   'first_name':
                                   self.client.identity.first_name,
                                   'last_name': self.client.identity.last_name
                               })
        return price_is_set

    def _expand_product_groups(self):
        """
        When a product is a group of products, the group is deleted from cart and
        is replaced with one OrderItem for each product inside the group.
        """
        items = self.items.all()
        for item in items:
            # if product is a group (if product has children)
            if item.product.products.exists():
                for product in item.product.products.all():
                    # only pick products that intersect current order geom
                    if product.geom.intersects(self.geom):
                        new_item = OrderItem(order=self,
                                             product=product,
                                             data_format=item.data_format)
                        # If the data format for the group is not available for the item,
                        # pick the first possible
                        if item.data_format not in item.available_formats:
                            new_item.data_format = product.product_formats.all(
                            ).first().data_format
                        new_item.set_price()
                        new_item.save()
                item.delete()

    def confirm(self):
        """Customer's confirmations he wants to proceed with the order"""
        self._expand_product_groups()
        items = self.items.all()
        has_all_prices_calculated = True
        for item in items:
            if item.price_status == OrderItem.PricingStatus.PENDING:
                item.ask_price()
                has_all_prices_calculated = has_all_prices_calculated and False
            else:
                item.status = OrderItem.OrderItemStatus.IN_EXTRACT
        if has_all_prices_calculated:
            self.date_ordered = timezone.now()
            self.download_guid = uuid.uuid4()
            self.status = Order.OrderStatus.READY
        else:
            self.status = Order.OrderStatus.PENDING

    def next_status_on_extract_input(self):
        """Controls status when Extract uploads a file or cancel an order item"""
        previous_accepted_status = [
            Order.OrderStatus.READY, Order.OrderStatus.IN_EXTRACT,
            Order.OrderStatus.PARTIALLY_DELIVERED
        ]
        if self.status not in previous_accepted_status:
            raise Exception("Order has an inappropriate status after input")
        items_statuses = set(self.items.all().values_list('status', flat=True))

        if OrderItem.OrderItemStatus.IN_EXTRACT in items_statuses:
            if OrderItem.OrderItemStatus.PROCESSED in items_statuses:
                self.status = Order.OrderStatus.PARTIALLY_DELIVERED
            else:
                self.status = Order.OrderStatus.READY
        else:
            if OrderItem.OrderItemStatus.PROCESSED in items_statuses:
                self.status = Order.OrderStatus.PROCESSED
                self.date_processed = timezone.now()
                send_geoshop_email(_('Geoshop - Download ready'),
                                   recipient=self.email_deliver
                                   or self.client.identity,
                                   template_name='email_download_ready',
                                   template_data={
                                       'order_id':
                                       self.id,
                                       'download_guid':
                                       self.download_guid,
                                       'front_url':
                                       '{}://{}{}'.format(
                                           settings.FRONT_PROTOCOL,
                                           settings.FRONT_URL,
                                           settings.FRONT_HREF),
                                       'first_name':
                                       self.client.identity.first_name,
                                       'last_name':
                                       self.client.identity.last_name,
                                   })
            else:
                self.status = Order.OrderStatus.REJECTED
        return self.status

    @property
    def geom_srid(self):
        return self.geom.srid

    @property
    def geom_area(self):
        return self.geom.area

    def __str__(self):
        return '%s - %s' % (self.id, self.title)
Esempio n. 4
0
class Signal(CreatedUpdatedModel):
    SOURCE_DEFAULT_ANONYMOUS_USER = '******'

    # we need an unique id for external systems.
    # TODO SIG-563 rename `signal_id` to `signal_uuid` to be more specific.
    signal_id = models.UUIDField(default=uuid.uuid4, db_index=True)
    source = models.CharField(max_length=128, default=SOURCE_DEFAULT_ANONYMOUS_USER)

    text = models.CharField(max_length=3000)
    text_extra = models.CharField(max_length=10000, default='', blank=True)

    location = models.OneToOneField('signals.Location',
                                    related_name='signal',
                                    null=True,
                                    on_delete=models.SET_NULL)
    status = models.OneToOneField('signals.Status',
                                  related_name='signal',
                                  null=True,
                                  on_delete=models.SET_NULL)
    category_assignment = models.OneToOneField('signals.CategoryAssignment',
                                               related_name='signal',
                                               null=True,
                                               on_delete=models.SET_NULL)
    categories = models.ManyToManyField('signals.Category',
                                        through='signals.CategoryAssignment')
    reporter = models.OneToOneField('signals.Reporter',
                                    related_name='signal',
                                    null=True,
                                    on_delete=models.SET_NULL)
    priority = models.OneToOneField('signals.Priority',
                                    related_name='signal',
                                    null=True,
                                    on_delete=models.SET_NULL)

    # Date of the incident.
    incident_date_start = models.DateTimeField(null=False)
    incident_date_end = models.DateTimeField(null=True)

    # Date action is expected
    operational_date = models.DateTimeField(null=True)

    # Date we should have reported back to reporter.
    expire_date = models.DateTimeField(null=True)

    # file will be saved to MEDIA_ROOT/uploads/2015/01/30
    upload = ArrayField(models.FileField(upload_to='uploads/%Y/%m/%d/'), null=True)  # TODO: remove

    extra_properties = JSONField(null=True)

    # SIG-884
    parent = models.ForeignKey(to='self', related_name='children', null=True, blank=True,
                               on_delete=models.SET_NULL)

    objects = SignalQuerySet.as_manager()
    actions = SignalManager()

    @property
    def image(self):
        """ Field for backwards compatibility. The attachment table replaces the old 'image'
        property """
        attachment = self.attachments.filter(is_image=True).first()
        return attachment.file if attachment else ""

    @property
    def image_crop(self):
        attachment = self.attachments.filter(is_image=True).first()
        return attachment.image_crop if self.image else ''

    class Meta:
        permissions = (
            ('sia_read', 'Can read from SIA'),
            ('sia_write', 'Can write to SIA'),
            ('sia_split', 'Can split a signal into a parent with X children'),
            ('sia_signal_create_initial', 'Can create new signals'),
            ('sia_signal_create_note', 'Can create notes for signals'),
            ('sia_signal_change_status', 'Can change the status of a signal'),
            ('sia_signal_change_category', 'Can change the category of a signal'),
            ('sia_signal_export', 'Can export signals'),
            ('sia_signal_report', 'Can create reports for signals'),
        )
        ordering = ('created_at',)
        indexes = [
            models.Index(fields=['created_at']),
            models.Index(fields=['id', 'parent']),
        ]

    def __init__(self, *args, **kwargs):
        super(Signal, self).__init__(*args, **kwargs)
        if not self.signal_id:
            self.signal_id = uuid.uuid4()

    def __str__(self):
        """Identifying string.
        DO NOT expose sensitive stuff here.
        """
        state = ''
        buurt_code = ''

        if self.status:
            state = self.status.state
        if self.location:
            buurt_code = self.location.buurt_code

        return '{} - {} - {} - {}'.format(
            self.id,
            state,
            buurt_code,
            self.created_at
        )

    @property
    def sia_id(self):
        """SIA identifier used for external communication.

        :returns: str
        """
        return 'SIA-{id}'.format(id=self.id)

    def get_fqdn_image_crop_url(self):
        """Get FQDN image crop url.

        :returns: url (str) or None
        """
        if not self.image_crop:
            return None

        is_swift = isinstance(self.image_crop.storage, SwiftStorage)
        if is_swift:
            return self.image_crop.url  # Generated temp url from Swift Object Store.
        else:
            # Generating a fully qualified url ourself.
            current_site = Site.objects.get_current()
            is_local = 'localhost' in current_site.domain or settings.DEBUG
            fqdn_url = '{scheme}://{domain}{path}'.format(
                scheme='http' if is_local else 'https',
                domain=current_site.domain,
                path=self.image_crop.url)
            return fqdn_url

    def is_parent(self):
        # If we have children we are a parent
        return self.children.exists()

    def is_child(self):
        # If we have a parent we are a child
        return self.parent is not None

    @property
    def siblings(self):
        if self.is_child():
            # If we are a child return all siblings
            siblings_qs = self.parent.children.all()
            if self.pk:
                # Exclude myself if possible
                return siblings_qs.exclude(pk=self.pk)
            return siblings_qs

        # Return a non queryset
        return self.__class__.objects.none()

    def _validate(self):
        if self.is_parent() and self.is_child():
            # We cannot be a parent and a child at once
            raise ValidationError('Cannot be a parent and a child at the once')

        if self.parent and self.parent.is_child():
            # The parent of this Signal cannot be a child of another Signal
            raise ValidationError('A child of a child is not allowed')

        if (self.pk is None and self.is_child() and
                self.siblings.count() >= settings.SIGNAL_MAX_NUMBER_OF_CHILDREN):
            # we are a new child and our parent already has the max number of children
            raise ValidationError('Maximum number of children reached for the parent Signal')

        if self.children.exists() and self.status.state != workflow.GESPLITST:
            # If we have children our status can only be "gesplitst"
            raise ValidationError('The status of a parent Signal can only be "gesplitst"')

    def save(self, *args, **kwargs):
        self._validate()
        super(Signal, self).save(*args, **kwargs)
Esempio n. 5
0
class Track(TimeStampedModel):
    class Meta:
        abstract = True

    name = models.CharField(max_length=100)
    description = models.TextField(blank=True)
    image = ThumbnailerImageField(upload_to=get_image_path,
                                  blank=True,
                                  null=True)

    # Main activity of the track
    activity_type = models.ForeignKey(ActivityType,
                                      default=1,
                                      on_delete=models.SET_DEFAULT)

    # link to athlete
    athlete = models.ForeignKey("Athlete",
                                on_delete=models.CASCADE,
                                related_name="tracks")

    # elevation gain in m
    total_elevation_gain = models.FloatField("Total elevation gain in m",
                                             default=0)

    # elevation loss in m
    total_elevation_loss = models.FloatField("Total elevation loss in m",
                                             default=0)

    # route distance in m
    total_distance = models.FloatField("Total length of the track in m",
                                       default=0)

    # geographic information
    geom = models.LineStringField("line geometry", srid=3857)

    # Start and End-place
    start_place = models.ForeignKey(Place,
                                    null=True,
                                    related_name="starts_%(class)s",
                                    on_delete=models.SET_NULL)

    end_place = models.ForeignKey(Place,
                                  null=True,
                                  related_name="ends_%(class)s",
                                  on_delete=models.SET_NULL)

    # uuid field to generate unique file names
    uuid = models.UUIDField(default=uuid4, editable=False)

    # track data as a pandas DataFrame
    data = DataFrameField(null=True,
                          upload_to=athlete_data_directory_path,
                          unique_fields=["uuid"])

    def calculate_step_distances(self, min_distance: float, commit=True):
        """
        calculate distance between each row, removing steps where distance is too small.
        """
        data = self.data.copy()
        data["geom"], srid = self.geom, self.geom.srid

        data.drop(data[data.distance.diff() < min_distance].index,
                  inplace=True)
        data["step_distance"] = data.distance.diff()

        try:
            self.geom = LineString(data.geom.tolist(), srid=srid)
        except ValueError:
            message = "Cannot clean track data: invalid distance values."
            logger.error(message, exc_info=True)
            raise ValueError(message)
        data.drop(columns=["geom"], inplace=True)
        self.data = data.fillna(value=0)

        if commit:
            self.save(update_fields=["data", "geom"])

    def calculate_gradients(self, max_gradient: float, commit=True):
        """
        calculate gradients in percents based on altitude and distance
        while cleaning up bad values.
        """
        data = self.data.copy()
        data["geom"], srid = self.geom, self.geom.srid

        # calculate gradients
        data["gradient"] = data.altitude.diff() / data.distance.diff() * 100

        # find rows with offending gradients
        bad_rows = data[(data["gradient"] < -max_gradient) |
                        (data["gradient"] > max_gradient)]

        # drop bad rows and recalculate until all offending values have been removed
        while not bad_rows.empty:
            data.drop(bad_rows.index, inplace=True)
            data["gradient"] = data.altitude.diff() / data.distance.diff(
            ) * 100
            bad_rows = data[(data["gradient"] < -max_gradient) |
                            (data["gradient"] > max_gradient)]

        # save the values back to the track object
        try:
            self.geom = LineString(data.geom.tolist(), srid=srid)
        except ValueError:
            message = "Cannot clean track data: invalid altitude values."
            logger.error(message, exc_info=True)
            raise ValueError(message)
        data.drop(columns=["geom"], inplace=True)
        self.data = data.fillna(value=0)

        if commit:
            self.save(update_fields=["data", "geom"])

    def calculate_cumulative_elevation_differences(self, commit=True):
        """
        Calculates two columns from the altitude data:
        - cumulative_elevation_gain: cumulative sum of positive elevation data
        - cumulative_elevation_loss: cumulative sum of negative elevation data
        """

        # only consider entries where altitude difference is greater than 0
        self.data["cumulative_elevation_gain"] = self.data.altitude.diff()[
            self.data.altitude.diff() >= 0].cumsum()

        # only consider entries where altitude difference is less than 0
        self.data["cumulative_elevation_loss"] = self.data.altitude.diff()[
            self.data.altitude.diff() <= 0].cumsum()

        # Fill the NaNs with the last valid value of the series
        # then, replace the remaining NaN (at the beginning) with 0
        self.data[["cumulative_elevation_gain",
                   "cumulative_elevation_loss"]] = (self.data[[
                       "cumulative_elevation_gain", "cumulative_elevation_loss"
                   ]].fillna(method="ffill").fillna(value=0))

        if commit:
            self.save(update_fields=["data"])

    def add_distance_and_elevation_totals(self, commit=True):
        """
        add total distance and total elevation gain to every row
        """
        self.data["total_distance"] = self.total_distance
        self.data["total_elevation_gain"] = self.total_elevation_gain

        if commit:
            self.save(update_fields=["data"])

    def update_permanent_track_data(self,
                                    min_step_distance=1,
                                    max_gradient=100,
                                    commit=True,
                                    force=False):
        """
        make sure all unvarying data columns required for
        schedule calculation are available.

        :param min_step_distance: minimum distance in m to keep between each point
        :param max_gradient: maximum gradient to keep when cleaning rows
        :param commit: save the instance to the database after update
        :param force: recalculates columns even if they are already present

        :returns: None
        :raises ValueError: if the number of coords in the track geometry
        is not equal to the number of rows in data or if the cleaned data columns
        are left with only one row.
        """
        # flag if any of the data columns have been updated
        track_data_updated = False

        # make sure we have step distances
        if "step_distance" not in self.data.columns or force:
            track_data_updated = True
            self.calculate_step_distances(min_distance=min_step_distance,
                                          commit=False)

        # make sure we have step gradients
        if "gradient" not in self.data.columns or force:
            track_data_updated = True
            self.calculate_gradients(max_gradient=max_gradient, commit=False)

        # make sure we have cumulative elevation differences
        if (not all(column in self.data.columns for column in
                    ["cumulative_elevation_gain", "cumulative_elevation_loss"])
                or force):
            track_data_updated = True
            self.calculate_cumulative_elevation_differences(commit=False)

        # make sure we have distance and elevation totals
        if (not all(column in self.data.columns
                    for column in ["total_distance", "total_elevation_gain"])
                or force):
            track_data_updated = True
            self.add_distance_and_elevation_totals(commit=False)

        # commit changes to the database if any
        if track_data_updated and commit:
            self.save(update_fields=["data", "geom"])

    def update_track_details_from_data(self, commit=True):
        """
        set track details from the track data,
        usually replacing remote information received for the route
        """
        if not all(column in
                   ["cumulative_elevation_gain", "cumulative_elevation_loss"]
                   for column in self.data.columns):
            self.calculate_cumulative_elevation_differences(commit=False)

        # update total_distance, total_elevation_gain and total_elevation_loss from data
        self.total_distance = self.data.distance.max()
        self.total_elevation_loss = abs(
            self.data.cumulative_elevation_loss.min())
        self.total_elevation_gain = self.data.cumulative_elevation_gain.max()

        if commit:
            self.save(update_fields=[
                "total_distance",
                "total_elevation_loss",
                "total_elevation_gain",
            ])

    def get_prediction_model(self, user):
        """
        get the prediction model from the Model instance containing prediction values

        Use an instance of ActivityPerformance if it exists for the athlete and
        activity type. Fallback on ActivityType otherwise.
        """
        if user.is_authenticated:
            try:
                performance = user.athlete.performances
                performance = performance.filter(
                    activity_type=self.activity_type).get()
                return performance.get_prediction_model()
            except ActivityPerformance.DoesNotExist:
                pass

        # no ActivityPerformance for the user, fallback on ActivityType
        return self.activity_type.get_prediction_model()

    def calculate_projected_time_schedule(self,
                                          user,
                                          workout_type=None,
                                          gear=None):
        """
        Calculates route pace and route schedule based on the athlete's prediction model
        for the route's activity type.
        """
        # make sure we have all required data columns
        self.update_permanent_track_data()

        # add temporary columns useful to the schedule calculation
        data = self.data

        # add gear and workout type to every row
        data["gear"] = gear or "None"
        data["workout_type"] = workout_type or "None"

        # restore prediction model for athlete and activity_type
        prediction_model = self.get_prediction_model(user)

        # keep model pipelines and columns in local variable for readability
        pipeline = prediction_model.pipeline
        numerical_columns = prediction_model.numerical_columns
        categorical_columns = prediction_model.categorical_columns
        feature_columns = numerical_columns + categorical_columns

        # calculate pace and schedule columns for the route
        data["pace"] = pipeline.predict(data[feature_columns])
        data["schedule"] = (data.pace *
                            data.step_distance).cumsum().fillna(value=0)

        self.data = data

    def get_data(self, line_location, data_column):
        """
        interpolate the value of a given column in the DataFrame
        based on the line_location and the total_distance column.
        """

        # calculate the distance value to interpolate with
        # based on line location and the total length of the track.
        interp_x = line_location * self.total_distance

        # interpolate the value, see:
        # https://docs.scipy.org/doc/numpy/reference/generated/numpy.interp.html
        return interp(interp_x, self.data["distance"], self.data[data_column])

    def get_distance_data(self, line_location, data_column, absolute=False):
        """
        wrap around the get_data method
        to return a Distance object.
        """
        distance_data = self.get_data(line_location, data_column)

        # return distance object
        if distance_data is not None:
            return D(m=abs(distance_data)) if absolute else D(m=distance_data)

    def get_time_data(self, line_location, data_column):
        """
        wrap around the get_data method
        to return a timedelta object.
        """
        time_data = self.get_data(line_location, data_column)
        # return time object
        if time_data is not None:
            return timedelta(seconds=int(time_data))

    def get_start_altitude(self):
        start_altitude = self.get_distance_data(0, "altitude")
        return start_altitude

    def get_end_altitude(self):
        end_altitude = self.get_distance_data(1, "altitude")
        return end_altitude

    def get_total_distance(self):
        """
        returns track total_distance as a Distance object
        """
        return D(m=self.total_distance)

    def get_total_elevation_gain(self):
        """
        returns total altitude gain as a Distance object
        """
        return D(m=self.total_elevation_gain)

    def get_total_elevation_loss(self):
        """
        returns total altitude loss as a Distance object
        """
        return D(m=self.total_elevation_loss)

    def get_total_duration(self):
        """
        returns total duration as a timedelta object
        """
        return self.get_time_data(1, "schedule")

    def get_closest_places_along_line(self, line_location=0, max_distance=200):
        """
        retrieve Place objects with a given distance of a point on the line.
        """
        # create the point from location
        point = self.geom.interpolate_normalized(line_location)

        # get closest places to the point
        places = get_places_within(point, max_distance)

        return places

    def get_start_places(self, max_distance=200):
        """
        retrieve Place objects close to the start of the track.
        """
        return self.get_closest_places_along_line(line_location=0,
                                                  max_distance=max_distance)

    def get_end_places(self, max_distance=200):
        """
        retrieve Place objects close to the end of the track.
        """
        return self.get_closest_places_along_line(line_location=1,
                                                  max_distance=max_distance)
Esempio n. 6
0
class UUIDModel(models.Model):

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Esempio n. 7
0
def make_django_model(meta_model, divider):
    class Meta:
        verbose_name = _(meta_model.get_verbose_name())
        verbose_name_plural = _(meta_model.get_verbose_name_plural())
        ordering = ('-modification_date', '-creation_date')
        unique_together = tuple(
            meta_model.get_property(prop_name='m_unique_together',
                                    default_value=[]))

    modelisation_version = getattr(meta_model, 'version', None)

    if (modelisation_version is not None
            and modelisation_version not in HANDELED_MODELISATION_VERSIONS):
        raise ValueError('Unknown modelisation format')

    creation_level = get_minimum_level(
        meta_model=meta_model,
        prop_name='m_creation_minimum_level',
        default_value='authenticated',
    )

    retrieve_level = get_minimum_level(
        meta_model=meta_model,
        prop_name='m_retrieve_minimum_level',
        default_value='authenticated',
    )

    update_level = get_minimum_level(
        meta_model=meta_model,
        prop_name='m_update_minimum_level',
        default_value='authenticated',
    )

    delete_level = get_minimum_level(
        meta_model=meta_model,
        prop_name='m_delete_minimum_level',
        default_value='superuser',
    )

    attrs = {
        'Meta': Meta,
        '__module__': 'concrete_datastore.concrete.models',
        '__str__': make_unicode_method(meta_model),
        '__creation_minimum_level__': creation_level,
        '__retrieve_minimum_level__': retrieve_level,
        '__update_minimum_level__': update_level,
        '__delete_minimum_level__': delete_level,
    }

    is_default_public = meta_model.get_property(
        prop_name='m_is_default_public', default_value=False)
    if is_default_public is None or is_default_public not in (True, False):
        is_default_public = False
    attrs.update(get_common_fields(public_default_value=is_default_public))

    ancestors = (models.Model, )
    if meta_model.get_model_name() == 'User':
        ancestors = (
            AbstractUser,
            HasPermissionAbstractUser,
            ConfirmableUserAbstract,
        )
        attrs.update({
            'username':
            None,
            'objects':
            UserManager(),
            'admin':
            models.BooleanField(default=False),
            'password_modification_date':
            models.DateField(default=date.today),
            'subscription_notification_token':
            models.UUIDField(default=uuid.uuid4, editable=False),
            'login_counter':
            models.IntegerField(default=0),
            'external_auth':
            models.BooleanField(default=False),
            # True if user created by an external auth
            'USERNAME_FIELD':
            'email',
            'REQUIRED_FIELDS': [],
        })
    else:
        attrs.update(get_user_tracked_fields())

    for field_name, field in meta_model.get_fields():
        if field_name in attrs:
            raise ValueError(
                f'{field_name} is a protected field and cannot be overwritten')

        # obsoletes fields
        if meta_model.get_model_name() == 'User':
            obsoletes_level = ('guest', 'manager', 'blocked')
            if field_name in obsoletes_level:
                raise ValueError(
                    f'The fields {obsoletes_level} are no longer supported')

        args = field.f_args
        if field.f_type == 'FileField':
            args.update({
                'blank': True,
                'null': True,
                'validators': [validate_file]
            })
        elif field.f_type == 'JSONField':
            json_args = args
            json_args['blank'] = True
            json_args['encoder'] = None
            json_args['null'] = False
            json_args['default'] = dict
            attrs.update({field_name: JSONField(**json_args)})
            continue
        elif field.f_type in ('CharField', 'TextField'):
            args['null'] = False
            args.setdefault('blank', True)
            args.setdefault('default', "")
        elif field.f_type in ('IntegerField', 'BigIntegerField'):
            args['null'] = False
            args.setdefault('blank', True)
            args.setdefault('default', 0)
        elif field.f_type == 'DecimalField':
            args['null'] = False
            args.setdefault('decimal_places', 2)
            args.setdefault('max_digits', 20)
            args.setdefault('default', 0.00)
        elif field.f_type in ('ForeignKey', ):

            # Copy args to not alter the real field.f_args
            args = args.copy()
            # Force FK to null=True to avoid default value problems
            args.update({'null': True})
            # PROTECT foreign key deletion by default
            on_delete_rule = args.get('on_delete', 'PROTECT')
            if on_delete_rule not in ('CASCADE', 'SET_NULL', 'PROTECT'):
                raise ValueError(
                    f'On delete rule "{on_delete_rule}" is invalid. '
                    'It must be "CASCADE", "SET_NULL" or "PROTECT"')

            args.update({'on_delete': getattr(models, on_delete_rule)})

        elif field.f_type in ('GenericIPAddressField', ):
            #: If blank is True, null should be too
            #: https://docs.djangoproject.com/fr/3.1/ref/models/fields/#genericipaddressfield
            if args.get('blank', False) is True:
                args['null'] = True
            else:
                args.setdefault('blank', False)
                args['null'] = False

        elif field.f_type in ('DateTimeField', ):
            if args.get('null', False) is True:
                args['null'] = True
                args['blank'] = True
            else:
                args['default'] = timezone.now
                args['null'] = False
                args.setdefault('blank', False)
        elif field.f_type in ('DateField', ):
            if args.get('null', False) is True:
                args['null'] = True
                args['blank'] = True
            else:
                args['default'] = date.today
                args['null'] = False
                args.setdefault('blank', False)
        elif field.f_type == 'ManyToManyField':
            # Copy args to not alter the real field.f_args
            args = args.copy()
            args.pop('null', None)
        attrs.update({field_name: getattr(models, field.f_type)(**args)})
    if meta_model.get_model_name() != divider:
        if meta_model.get_model_name() == "User":
            attrs.update(get_divider_fields_manytomany(divider))
            attrs.update(get_divider_notification_fields(divider))
            attrs.update(
                {'email': models.EmailField(_('email address'), unique=True)})
        elif meta_model.get_model_name() in UNDIVIDED_MODEL:
            pass
        else:
            attrs.update(get_divider_fields_foreignkey(divider))

    return type(meta_model.get_model_name(), ancestors, attrs)
Esempio n. 8
0
class GeoService(models.Model):
    service_type = 'generic'

    def save(self, *args, **kwargs):
        if self.service_type == GeoService.service_type:
            raise Exception('Base geo service model can\'t be saved')
        self.type = self.service_type
        self.extent = self.boundary.envelope if self.boundary else None
        self.boundary_area = self.boundary.area if self.boundary else None
        super(GeoService, self).save(*args, **kwargs)

    guid = models.UUIDField(_('service guid'),
                            default=uuid.uuid4,
                            editable=False)
    name = models.CharField(_('service name'),
                            unique=True,
                            max_length=100,
                            blank=False,
                            null=False)
    desc = models.TextField(_('description'), blank=True, null=True)
    type = models.CharField(_('service type'),
                            max_length=20,
                            editable=False,
                            null=False)
    epsg = models.IntegerField(_('EPSG Code'), null=True, blank=True)
    icon = models.ForeignKey(ServiceIcon,
                             models.SET_NULL,
                             blank=True,
                             null=True)
    # license
    license_name = models.CharField(_('license name'),
                                    max_length=256,
                                    blank=True,
                                    null=True)
    license_url = models.URLField(_('license url'),
                                  max_length=512,
                                  blank=True,
                                  null=True)
    copyright_text = models.CharField(_('copyright text'),
                                      max_length=2048,
                                      blank=True,
                                      null=True)
    copyright_url = models.URLField(_('copyright url'),
                                    max_length=512,
                                    blank=True,
                                    null=True)
    terms_of_use_url = models.URLField(_('terms of use url'),
                                       max_length=512,
                                       blank=True,
                                       null=True)
    # creation & update info
    submitter = models.ForeignKey(NextgisUser,
                                  on_delete=models.SET_NULL,
                                  to_field='nextgis_guid',
                                  null=True)
    created_at = models.DateTimeField(_('created at'), auto_now_add=True)
    updated_at = models.DateTimeField(_('updated at'), null=True, blank=True)
    # source info
    source = models.TextField(_('source'), blank=True, null=True)
    source_url = models.URLField(_('source url'),
                                 max_length=512,
                                 blank=True,
                                 null=True)
    # status
    last_status = models.ForeignKey('GeoServiceStatus',
                                    on_delete=models.SET_NULL,
                                    null=True,
                                    related_name='last_for',
                                    blank=True)
    # extent & boundary
    extent = models.PolygonField(srid=4326,
                                 spatial_index=True,
                                 null=True,
                                 blank=True)
    boundary = models.MultiPolygonField(srid=4326,
                                        spatial_index=True,
                                        null=True,
                                        blank=True)
    boundary_area = models.FloatField(null=True, blank=True)

    # TODO: tags

    def __str__(self):
        return self.name

    def get_typed_instance(self):
        if self.type == TmsService.service_type:
            return self.tmsservice
        if self.type == WmsService.service_type:
            return self.wmsservice
        if self.type == WfsService.service_type:
            return self.wfsservice
        if self.type == GeoJsonService.service_type:
            return self.geojsonservice
        return self
Esempio n. 9
0
class NextgisUser(AbstractBaseUser, PermissionsMixin):
    """
    Полная копия стандартного User
    + дополнительные поля:
    nextgis_guid
    """
    username = models.CharField(
        _('username'),
        max_length=30,
        unique=True,
        help_text=
        _('Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.'
          ),
        validators=[
            validators.RegexValidator(
                r'^[\w.@+-]+$',
                _('Enter a valid username. This value may contain only '
                  'letters, numbers '
                  'and @/./+/-/_ characters.')),
        ],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_(
            'Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
    #  custom fields
    nextgis_guid = models.UUIDField(_('nextgis guid'),
                                    default=uuid.uuid4,
                                    editable=False,
                                    unique=True)
    locale = models.CharField(_('user locale'),
                              max_length=30,
                              null=True,
                              blank=False,
                              choices=SupportedLanguages.dict_text.items(),
                              default=SupportedLanguages.DEFAULT)
    email_confirmed = models.BooleanField(_('mail confirmed'), default=False)

    @property
    def nextgis_id(self):
        return self.id

    objects = NextgisUserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def get_full_name(self):
        """
        Returns the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        "Returns the short name for the user."
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email], **kwargs)
Esempio n. 10
0
class UserResponse(models.Model):
    uuid = models.UUIDField(db_index=True, default=uuid_lib.uuid4, editable=False)
    date_created = models.DateTimeField(default=timezone.now, blank=True)
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="response_question")
    option = models.ForeignKey(QuestionOption, on_delete=models.CASCADE, related_name="response_option")
    response_set = models.ForeignKey(UserResponseSet, on_delete=models.CASCADE, related_name="response_response_set")
Esempio n. 11
0
class UserResponseSet(models.Model):
    uuid = models.UUIDField(db_index=True, default=uuid_lib.uuid4, editable=False)
    date_created = models.DateTimeField(default=timezone.now, blank=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="response_set_user")
Esempio n. 12
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
Esempio n. 13
0
class Profile(BaseModel):
    MALE = 'm'
    FEMALE = 'f'
    OTHER = 'o'
    SEX_CHOICES = ((MALE, _('male')), (FEMALE, _('female')), (OTHER,
                                                              _('other')))
    RACING_CYCLE = 'racing_cycle'
    CITY_BIKE = 'city_bike'
    MOUNTAIN_BIKE = 'mountain_bike'
    E_BIKE = 'e_bike'
    CARGO_BIKE = 'cargo_bike'
    E_CARGO_BIKE = 'e_cargo_bike'
    CATEGORY_OF_BIKE_CHOICES = (
        (RACING_CYCLE, _('racing cycle')),
        (CITY_BIKE, _('city bike')),
        (MOUNTAIN_BIKE, _('mountain bike')),
        (E_BIKE, _('e-bike')),
        (CARGO_BIKE, _('cargo bike')),
        (E_CARGO_BIKE, _('e-cargo-bike')),
    )
    NEVER = 0
    ONCE_PER_MONTH = 1
    ONCE_PER_WEEK = 2
    ONCE_PER_DAY = 3
    USAGE_CHOICES = (
        (NEVER, _('never')),
        (ONCE_PER_DAY, _('once per day')),
        (ONCE_PER_WEEK, _('once per week')),
        (ONCE_PER_MONTH, _('once per month')),
    )
    age = models.PositiveSmallIntegerField(_('age'), blank=True, null=True)
    category_of_bike = models.CharField(
        _('category of bike'),
        blank=True,
        null=True,
        max_length=20,
        choices=CATEGORY_OF_BIKE_CHOICES,
    )
    has_trailer = models.BooleanField(_('has trailer'), blank=True, null=True)
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    postal_code = models.CharField(_('postal code'),
                                   blank=True,
                                   null=True,
                                   max_length=5)
    sex = models.CharField(_('sex'),
                           blank=True,
                           null=True,
                           max_length=1,
                           choices=SEX_CHOICES)
    speed = models.PositiveSmallIntegerField(_('speed'), blank=True, null=True)
    security = models.PositiveSmallIntegerField(_('security'),
                                                blank=True,
                                                null=True)
    usage = models.PositiveSmallIntegerField(_('usage'),
                                             blank=True,
                                             null=True,
                                             choices=USAGE_CHOICES)

    class Meta:
        verbose_name = _('profile')
        verbose_name_plural = _('profiles')
Esempio n. 14
0
class RegistriesApplication(AuditModel):
    """
    Application from a well driller or pump installer to be on the GWELLS Register.
    """
    application_guid = models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        verbose_name='Register Application UUID',
        db_comment='Unique identifier for the registries_application record.')
    registration = models.ForeignKey(
        Register,
        db_column='register_guid',
        on_delete=models.PROTECT,
        verbose_name='Person Reference',
        related_name='applications',
        db_comment='Unique identifier for the registries_register record.')
    subactivity = models.ForeignKey(SubactivityCode,
                                    db_column='registries_subactivity_code',
                                    on_delete=models.PROTECT,
                                    related_name="applications")
    file_no = models.CharField(
        max_length=25,
        blank=True,
        null=True,
        verbose_name='ORCS File # reference.',
        db_comment=
        ('Operational Records Classification Systems (ORCS) number. Information schedules used '
         'to classify, file, retrieve and dispose of operational records. This number is '
         'assigned on creation of a file.'))
    proof_of_age = models.ForeignKey(ProofOfAgeCode,
                                     db_column='registries_proof_of_age_code',
                                     on_delete=models.PROTECT,
                                     verbose_name="Proof of age.",
                                     null=False)
    registrar_notes = models.CharField(
        max_length=255,
        blank=True,
        null=True,
        verbose_name='Registrar notes, for internal use only.',
        db_comment=
        'Internal notes documenting communication between an applicant and the province.'
    )
    reason_denied = models.CharField(
        max_length=255,
        blank=True,
        null=True,
        verbose_name='Free form text explaining reason for denial.',
        db_comment=
        ('The reason the Comptroller did not approve an individuals application for well driller '
         'or well pump installer, for example not meeting the requirements of the application. '
         'A brief internal note.'))

    # TODO Support multiple certificates
    primary_certificate = models.ForeignKey(AccreditedCertificateCode,
                                            blank=True,
                                            null=True,
                                            db_column='acc_cert_guid',
                                            on_delete=models.PROTECT,
                                            verbose_name="Certificate")
    primary_certificate_no = models.CharField(
        max_length=50,
        db_comment=
        'Unique number assigned to the certificate by the certifying organization.'
    )

    @property
    def display_status(self):
        # When an application is removed, it's status remains "Active", and only the removal date is
        # populated. We spare the front-end from having to know about this, by generating a human
        # readable property on this level.
        status = None
        if self.removal_date:
            status = 'Removed'
        elif self.current_status:
            status = self.current_status.description
        return status

    # TODO Should probably force this to have a default value of Pending!
    # This field should really be called "Approval Outcome"
    current_status = models.ForeignKey(
        ApplicationStatusCode,
        blank=True,
        null=True,
        db_column='registries_application_status_code',
        on_delete=models.PROTECT,
        verbose_name="Application Status Code Reference")
    application_recieved_date = models.DateField(
        blank=True,
        null=True,
        db_comment=
        ('Date that the province received an application for registration of a well driller or '
         'well pump installer.'))
    application_outcome_date = models.DateField(
        blank=True,
        null=True,
        db_comment=
        ('Date that the comptroller decided if the application for registration of a well '
         'driller or well pump installer was approved or denied.'))
    application_outcome_notification_date = models.DateField(
        blank=True,
        null=True,
        db_comment=
        ('Date that the individual was notified of the outcome of their application for '
         'registration for well driller or well pump installer.'))
    # The "removal_date" refers to the date on which a classification is "removed" from the register.
    # Removing a classification may result in a person being removed from the public register as a whole,
    # only if there are no other Approved classification.
    removal_date = models.DateField(
        blank=True,
        null=True,
        db_comment=
        'Date that a registered individual was removed from the register.')
    removal_reason = models.ForeignKey(
        RegistriesRemovalReason,
        db_column='registries_removal_reason_code',
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        verbose_name='Removal Reason')

    history = GenericRelation(Version)

    class Meta:
        db_table = 'registries_application'
        verbose_name_plural = 'Applications'
        ordering = ['primary_certificate_no']

    db_table_comment = 'Placeholder table comment.'

    def __str__(self):
        return '%s : %s' % (self.registration, self.file_no)
Esempio n. 15
0
class TripTemplate(models.Model):
    """Trip and template objects.

    A template records a plan for an excursion. It may have many
    points of interest and routes associated with it, and other
    documents and file.

    A trip plans for and records an event. It may be cloned from a
    template, or created blank. A trip record will have a range of
    dates associated with it. GPX files can be uploaded to trip
    records, and any waypoints, tracks our rutes in that file injected
    into the datbase.

    The identifier should be a universal unique identifier

        import uuid
        uuid.uuid4().hex

    Should yield something like this 32 character string:

        70d0209ecd564104936dc2c09cfaeabe
    """

    TRIP_TYPE = (
        ('air', 'air'),
        ('boat', 'boat'),
        ('cycle', 'cycle'),
        ('road', 'road'),
        ('tramping', 'tramping'),
    )

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)

    trip_type = models.CharField(max_length=64,
                                 choices=TRIP_TYPE,
                                 default='tramping')

    name = models.CharField(max_length=255)
    subject = models.CharField(max_length=255, blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    location = models.CharField(max_length=255, blank=True, null=True)

    days_length = models.IntegerField(default=1)

    owner = models.CharField(max_length=255, blank=True, null=True)
    group = models.CharField(max_length=255, blank=True, null=True)

    class Meta:
        abstract = True

    def __unicode__(self):
        return self.name

    def __get_absolute_url__(self):
        return os.path.join(settings.BASE_URL, self.identifier())

    url = property(__get_absolute_url__)

    def waypoints(self):
        return Waypoint.objects.filter(trip=self)

    def directory(self):
        """Return a webnote.Directory object of the filespace."""

        filespace = self.filespace()
        if not os.path.isdir(filespace):
            self.make_filespace()
        directory = webnote.Directory(filespace)

        return directory

    def gpxfiles(self):
        """A list of parsed gpx file objects."""

        gpxfiles = []
        for gpxfile in self.directory().model['gpx']:
            filepath = os.path.join(self.filespace(), gpxfile)
            if os.path.isfile(filepath):
                f = open(filepath)
                gpxf = GPXFile(f, self)
                gpxfiles.append(gpxf.analyse())

        return gpxfiles

    def save(self, files=None, *args, **kwargs):
        """Save any uploaded file to filespace."""

        if not os.path.isdir(self.filespace()):
            self.make_filespace()

        if files:
            for item in files:

                f = files[item]
                filepath = os.path.join(self.filespace(), str(f))

                with open(filepath, 'wb+') as destination:
                    for chunk in f.chunks():
                        destination.write(chunk)

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

    def identifier(self):
        """The first eight characters of the uuid, chopped by hyphen."""

        uri_steps = str(self.id).split('-')
        return uri_steps[0]

    def filespace(self):
        """Return a string pathname to this object's filespace in static files.
        """

        dirname = self.name.replace(' ', '-').replace("'", "")
        #dirname = dirname + '_' + self.identifier()

        filepath = os.path.join(settings.STATICFILES_DIR,
                                settings.BASE_FILESPACE, dirname)

        return filepath

    def make_filespace(self):
        if not os.path.isdir(self.filespace()):
            os.mkdir(self.filespace())
            return True
        return False
Esempio n. 16
0
class Location(models.Model):
    TYPE_CHOICES = [
        ('line', _('line')),
        ('area', _('area')),
    ]
    SUBDIVISION_CHOICES = [
        ('north', _('north')),
        ('northeast', _('northeast')),
        ('east', _('east')),
        ('southeast', _('southeast')),
        ('south', _('south')),
        ('southwest', _('southwest')),
        ('west', _('west')),
        ('northwest', _('northwest')),
        ('center', _('center')),
    ]
    CHARACTER_CHOICES = [
        ('commercial', _("Commercial")),
        ('cbd', _("Central business district")),
        ('civic', _("Civic")),
        ('cultural', _("Cultural")),
        ('educational', _("Educational")),
        ('industrial', _("Industrial")),
        ('infrastructural', _("Infrastructural")),
        ('medical', _("Medical")),
        ('mixed', _("Mixed")),
        ('office', _("Office")),
        ('recreational', _("Recreational")),
        ('residential', _("Residential")),
        ('rural', _("Rural")),
        ('stadium', _("Stadium")),
    ]

    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    agency = models.ForeignKey(Agency, on_delete=models.CASCADE)
    country = models.ForeignKey(Country,
                                on_delete=models.CASCADE,
                                help_text=_("Country that the survey location "
                                            "is based within."))
    region = models.CharField(max_length=1000,
                              blank=True,
                              help_text=_("State, county, or municipal "
                                          "boundary of the location."))
    city = models.CharField(max_length=1000,
                            blank=True,
                            help_text=_("Name of the city that the survey "
                                        "location is based within. Leave "
                                        "blank if the survey location is not "
                                        "based within a city."))
    name_primary = models.CharField(max_length=1000,
                                    help_text=_("Official, specific name of "
                                                "the survey location."))
    name_secondary = models.CharField(max_length=1000,
                                      blank=True,
                                      help_text=_("Secondary or specifying "
                                                  "name of the survey "
                                                  "location. Leave blank if "
                                                  "no specification is "
                                                  "necessary."))
    subdivision = models.CharField(max_length=9,
                                   choices=SUBDIVISION_CHOICES,
                                   blank=True,
                                   help_text=_("Indication of whether the "
                                               "location is a subdivision of "
                                               "a single survey location."))
    character = models.CharField(max_length=15,
                                 blank=True,
                                 choices=CHARACTER_CHOICES,
                                 help_text=_("Primary character of the survey "
                                             "location's immediate "
                                             "surroundings."))
    geometry = models.GeometryField(help_text=_("Polygon or line that "
                                                "describes the geometry "
                                                "of the location. Polygons "
                                                "are intended for counts of "
                                                "people staying in an area "
                                                "while lines are intended for "
                                                "counts of people moving "
                                                "across a threshold."))
    geometry_type = models.CharField(max_length=4,
                                     choices=TYPE_CHOICES,
                                     blank=True,
                                     help_text=_("Indication of whether the "
                                                 "location is intended for "
                                                 "counts of people "
                                                 "moving (across a line), "
                                                 "or whether "
                                                 "it is intended for counts "
                                                 "of people "
                                                 "staying (within an area)."))

    is_active = models.BooleanField(default=True)

    def __str__(self):
        return self.name_primary
Esempio n. 17
0
class RecorridoProposed(models.Model):
    recorrido = models.ForeignKey('core.Recorrido')
    parent = models.UUIDField(default=uuid.uuid4)
    uuid = models.UUIDField(default=uuid.uuid4)
    nombre = models.CharField(max_length=100)
    linea = models.ForeignKey('core.Linea')
    ruta = models.LineStringField()
    sentido = models.CharField(max_length=100, blank=True, null=True)
    slug = models.SlugField(max_length=200, blank=True, null=True)
    inicio = models.CharField(max_length=100, blank=True, null=True)
    fin = models.CharField(max_length=100, blank=True, null=True)
    semirrapido = models.BooleanField(default=False)
    color_polilinea = models.CharField(max_length=20, blank=True, null=True)
    horarios = models.TextField(blank=True, null=True)
    pois = models.TextField(blank=True, null=True)
    descripcion = models.TextField(blank=True, null=True)
    current_status = models.CharField(max_length=1,
                                      choices=MODERATION_CHOICES,
                                      default='E')

    date_create = models.DateTimeField(auto_now_add=True)
    date_update = models.DateTimeField(auto_now=True)

    # Si tiene las paradas completas es porque tiene todas las paradas de
    # este recorrido en la tabla paradas+horarios (horarios puede ser null),
    # y se puede utilizar en la busqueda usando solo las paradas.
    paradas_completas = models.BooleanField(default=False)

    @property
    def ciudades(self):
        return Ciudad.objects.filter(lineas=self.linea)

    objects = models.GeoManager()

    def save(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(RecorridoProposed, self).save(*args, **kwargs)
        mod = self.get_moderacion_last()
        if mod is None or (user is not None and user != mod.created_by):
            self.logmoderacion_set.create(created_by=user)

    def get_current_status_display(self):
        status_list = self.logmoderacion_set.all().order_by('-date_create')
        if status_list:
            return status_list[0].get_newStatus_display()
        else:
            return None

    def log(self):
        return self.logmoderacion_set.all().order_by('-date_create')

    def get_moderacion_last(self):
        loglist = self.logmoderacion_set.all().order_by('-date_create')
        if loglist:
            return loglist[0]
        else:
            return None

    def get_moderacion_last_user(self):
        loglist = self.logmoderacion_set.filter(
            created_by__is_staff=False).order_by('-date_create')
        if loglist:
            return loglist[0].created_by
        else:
            return AnonymousUser()

    def get_pretty_user(self):
        user = self.get_moderacion_last_user()
        if user.is_anonymous():
            return "Usuario Anónimo"
        else:
            if user.first_name or user.last_name:
                return user.first_name + " " + user.last_name
            else:
                return user.username

    def get_fb_uid(self):
        last = self.get_moderacion_last_user()
        if last != AnonymousUser():
            return last.social_auth.get(provider='facebook').uid
        else:
            return None

    def __unicode__(self):
        return str(self.linea) + " - " + self.nombre

    def aprobar(self, user):
        r = self.recorrido
        if not r.uuid:
            # todavia no existe una version de este recorrido real, que estoy por retirar
            # antes de retirarlo creo su version correspondiente
            rp = RecorridoProposed(recorrido=r,
                                   nombre=r.nombre,
                                   linea=r.linea,
                                   ruta=r.ruta,
                                   sentido=r.sentido,
                                   slug=r.slug,
                                   inicio=r.inicio,
                                   fin=r.fin,
                                   semirrapido=r.semirrapido,
                                   color_polilinea=r.color_polilinea,
                                   pois=r.pois,
                                   descripcion=r.descripcion)
            rp.save(user=user)
            self.parent = rp.uuid
            self.save()

        r.recorrido = self.recorrido
        r.nombre = self.nombre
        r.linea = self.linea
        r.ruta = self.ruta
        r.sentido = self.sentido
        r.inicio = self.inicio
        r.fin = self.fin
        r.semirrapido = self.semirrapido
        r.color_polilinea = self.color_polilinea
        r.pois = self.pois
        r.descripcion = self.descripcion
        r.save()

        try:
            parent = RecorridoProposed.objects.get(uuid=self.parent)
            if parent:
                parent.logmoderacion_set.create(created_by=user, newStatus='R')
        except RecorridoProposed.DoesNotExist:
            pass
        for rp in RecorridoProposed.objects.filter(
                current_status='S',
                recorrido=r.recorrido).exclude(uuid=self.uuid):
            rp.logmoderacion_set.create(created_by=user, newStatus='R')
        self.logmoderacion_set.create(created_by=user, newStatus='S')

        #call_command('crear_thumbs', recorrido_id=self.recorrido.id)

        # Notificacion por facebook
        token = urllib2.urlopen(
            'https://graph.facebook.com/oauth/access_token?client_id=' +
            settings.FACEBOOK_APP_ID + '&client_secret=' +
            settings.FACEBOOK_API_SECRET +
            '&grant_type=client_credentials').read().split('access_token=')[1]
        user = self.get_moderacion_last_user()
        if not user.is_anonymous():
            fb = user.social_auth.filter(provider='facebook')
            if len(fb) != 0:
                from facebook import GraphAPI
                fb_uid = fb[0].uid
                graph = GraphAPI(token)
                graph.request(
                    "/" + fb_uid + "/notifications/",
                    post_args={
                        "template":
                        'Felicitaciones! Un moderador aceptó tu edición en cualbondi',
                        "href":
                        "https://cualbondi.com.ar/revision/" + str(self.id) +
                        "/"
                    })

    def get_absolute_url(self):
        url = reverse('revision_externa', kwargs={
            'id_revision': self.id,
        })
        print "URL: " + url
        return url

    class Meta:
        permissions = (("moderate_recorridos",
                        "Can moderate (accept/decline) recorridos"), )
Esempio n. 18
0
class Job(models.Model):
    """ 
    Database model for an 'Export'.
    Immutable, except in the case of HDX Export Regions.
    """
    id = models.AutoField(primary_key=True, editable=False)
    uid = models.UUIDField(unique=True,
                           default=uuid.uuid4,
                           editable=False,
                           db_index=True)
    user = models.ForeignKey(User, related_name='owner')
    name = models.CharField(max_length=100, db_index=True, blank=False)
    description = models.CharField(max_length=1000,
                                   db_index=True,
                                   default='',
                                   blank=True)
    event = models.CharField(max_length=100,
                             db_index=True,
                             default='',
                             blank=True)
    export_formats = ArrayField(models.CharField(max_length=10),
                                validators=[validate_export_formats],
                                blank=False)
    published = models.BooleanField(default=False, db_index=True)
    the_geom = models.GeometryField(verbose_name='Uploaded geometry',
                                    srid=4326,
                                    blank=False)
    simplified_geom = models.GeometryField(verbose_name='Simplified geometry',
                                           srid=4326,
                                           blank=True,
                                           null=True)
    objects = models.GeoManager()
    feature_selection = models.TextField(
        blank=False, validators=[validate_feature_selection])
    created_at = models.DateTimeField(default=timezone.now, editable=False)
    updated_at = models.DateTimeField(default=timezone.now, editable=False)
    mbtiles_maxzoom = models.IntegerField(null=True, blank=True)
    mbtiles_minzoom = models.IntegerField(null=True, blank=True)
    mbtiles_source = models.TextField(null=True, blank=True)

    # flags
    buffer_aoi = models.BooleanField(default=False)
    unlimited_extent = models.BooleanField(default=False)
    hidden = models.BooleanField(default=False)  # hidden from the list page
    expire_old_runs = models.BooleanField(default=True)
    pinned = models.BooleanField(default=False)
    unfiltered = models.BooleanField(default=False)

    class Meta:  # pragma: no cover
        managed = True
        db_table = 'jobs'

    @property
    def osma_link(self):
        bounds = self.the_geom.extent
        return "http://osm-analytics.org/#/show/bbox:{0},{1},{2},{3}/buildings/recency".format(
            *bounds)

    @property
    def area(self):
        return get_geodesic_area(self.the_geom)

    def save(self, *args, **kwargs):
        self.the_geom = force2d(self.the_geom)
        self.simplified_geom = simplify_geom(self.the_geom,
                                             force_buffer=self.buffer_aoi)
        super(Job, self).save(*args, **kwargs)

    def __str__(self):
        return str(self.uid)
Esempio n. 19
0
class DefaultDivider(models.Model):
    uid = models.UUIDField(primary_key=True, default=uuid.uuid4)
    name = models.CharField(max_length=250, default="Default Divider")
Esempio n. 20
0
class ResourceXResource(models.Model):
    resourcexid = models.UUIDField(primary_key=True, default=uuid.uuid1)  # This field type is a guess.
    resourceinstanceidfrom = models.ForeignKey(
        "ResourceInstance",
        db_column="resourceinstanceidfrom",
        blank=True,
        null=True,
        related_name="resxres_resource_instance_ids_from",
        on_delete=models.CASCADE,
        db_constraint=False,
    )
    resourceinstanceidto = models.ForeignKey(
        "ResourceInstance",
        db_column="resourceinstanceidto",
        blank=True,
        null=True,
        related_name="resxres_resource_instance_ids_to",
        on_delete=models.CASCADE,
        db_constraint=False,
    )
    notes = models.TextField(blank=True, null=True)
    relationshiptype = models.TextField(blank=True, null=True)
    inverserelationshiptype = models.TextField(blank=True, null=True)
    tileid = models.ForeignKey(
        "TileModel", db_column="tileid", blank=True, null=True, related_name="resxres_tile_id", on_delete=models.CASCADE,
    )
    nodeid = models.ForeignKey("Node", db_column="nodeid", blank=True, null=True, related_name="resxres_node_id", on_delete=models.CASCADE,)
    datestarted = models.DateField(blank=True, null=True)
    dateended = models.DateField(blank=True, null=True)
    created = models.DateTimeField()
    modified = models.DateTimeField()

    def delete(self, *args, **kwargs):
        from arches.app.search.search_engine_factory import SearchEngineInstance as se
        from arches.app.search.mappings import RESOURCE_RELATIONS_INDEX

        se.delete(index=RESOURCE_RELATIONS_INDEX, id=self.resourcexid)

        # update the resource-instance tile by removing any references to a deleted resource
        deletedResourceId = kwargs.pop("deletedResourceId", None)
        if deletedResourceId and self.tileid and self.nodeid:
            newTileData = []
            data = self.tileid.data[str(self.nodeid_id)]
            if type(data) != list:
                data = [data]
            for relatedresourceItem in data:
                if relatedresourceItem["resourceId"] != str(deletedResourceId):
                    newTileData.append(relatedresourceItem)
            self.tileid.data[str(self.nodeid_id)] = newTileData
            self.tileid.save()

        super(ResourceXResource, self).delete()

    def save(self):
        from arches.app.search.search_engine_factory import SearchEngineInstance as se
        from arches.app.search.mappings import RESOURCE_RELATIONS_INDEX

        if not self.created:
            self.created = datetime.datetime.now()
        self.modified = datetime.datetime.now()
        document = model_to_dict(self)
        se.index_data(index=RESOURCE_RELATIONS_INDEX, body=document, idfield="resourcexid")
        super(ResourceXResource, self).save()

    class Meta:
        managed = True
        db_table = "resource_x_resource"
Esempio n. 21
0
class EmailDevice(Device):
    """
    A :class:`~django_otp.models.Device` that delivers a token to the user's
    registered email address (``user.email``). This is intended for
    demonstration purposes; if you allow users to reset their passwords via
    email, then this provides no security benefits.

    .. attribute:: key

        *CharField*: A hex-encoded secret key of up to 40 bytes. (Default: 20
        random bytes)
    """

    uid = models.UUIDField(default=uuid.uuid4, primary_key=True)
    key = models.CharField(
        max_length=80,
        validators=[key_validator],
        default=default_key,
        help_text='A hex-encoded secret key of up to 20 bytes.',
    )
    email = models.CharField(
        max_length=250,
        default='',
        help_text='Email address to send verification code.',
    )
    modification_date = models.DateTimeField(auto_now=True)
    creation_date = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(
        'concrete.User',
        related_name="owned_%(class)ss",
        null=True,
        on_delete=models.PROTECT,
    )

    @property
    def id(self):
        return self.pk

    @property
    def bin_key(self):
        return unhexlify(self.key.encode())

    def generate_challenge(self):
        code_timeout = settings.TWO_FACTOR_CODE_TIMEOUT_SECONDS
        token = totp(self.bin_key, step=code_timeout)

        main_app = apps.get_app_config('concrete')
        body = settings.TWO_FACTOR_TOKEN_MSG.format(
            platform_name=settings.PLATFORM_NAME,
            confirm_code=token,
            min_validity=int(code_timeout / 60),
        )
        main_app.models['email'].objects.create(
            receiver=self.user,
            body=body,
            resource_status='to-send',
            subject='[Confirmation authentication {}]'.format(
                settings.PLATFORM_NAME),
            created_by=self.user,
        )
        logging.debug('Confirm Email has been sent to {}'.format(
            self.user.email))

        return token

    def verify_token(self, token):
        try:
            token = int(token)
        except Exception:
            verified = False
        else:
            verified = any(
                totp(
                    self.bin_key,
                    step=settings.TWO_FACTOR_CODE_TIMEOUT_SECONDS,
                    drift=drift,
                ) == token for drift in [0, -1])

        return verified

    @classmethod
    def from_persistent_id(cls, persistent_id):
        """
        Loads a device from its persistent id::

            device == Device.from_persistent_id(device.persistent_id)

        """
        device = None

        try:
            model_label, device_id = persistent_id.rsplit('/', 1)
            app_label, model_name = model_label.split('.')

            main_app = apps.get_app_config(app_label)

            device_cls = main_app.models[model_name]
            if issubclass(device_cls, Device):
                device = device_cls.objects.filter(pk=device_id).first()
        except (ValueError, LookupError):
            pass

        return device
Esempio n. 22
0
class RoadMarkingPlan(
    UpdatePlanLocationMixin, SourceControlModel, SoftDeleteModel, UserControlModel
):
    id = models.UUIDField(
        primary_key=True, unique=True, editable=False, default=uuid.uuid4
    )
    location = models.GeometryField(_("Location (2D)"), srid=settings.SRID)
    device_type = models.ForeignKey(
        TrafficControlDeviceType,
        verbose_name=_("Device Type"),
        on_delete=models.PROTECT,
        limit_choices_to=Q(
            Q(target_model=None) | Q(target_model=DeviceTypeTargetModel.ROAD_MARKING)
        ),
    )
    line_direction = EnumField(
        LineDirection,
        verbose_name=_("Line direction"),
        max_length=10,
        default=LineDirection.FORWARD,
        blank=True,
        null=True,
    )
    arrow_direction = EnumField(
        ArrowDirection,
        verbose_name=_("Arrow direction"),
        max_length=10,
        blank=True,
        null=True,
    )
    value = models.CharField(
        _("Road Marking value"), max_length=254, blank=True, null=True
    )
    size = models.CharField(_("Size"), max_length=254, blank=True, null=True)
    material = models.CharField(_("Material"), max_length=254, blank=True, null=True)
    color = EnumIntegerField(
        RoadMarkingColor,
        verbose_name=_("Color"),
        default=RoadMarkingColor.WHITE,
        blank=True,
        null=True,
    )
    decision_date = models.DateField(_("Decision date"))
    decision_id = models.CharField(
        _("Decision id"), max_length=254, blank=True, null=True
    )
    validity_period_start = models.DateField(
        _("Validity period start"), blank=True, null=True
    )
    validity_period_end = models.DateField(
        _("Validity period end"), blank=True, null=True
    )
    traffic_sign_plan = models.ForeignKey(
        TrafficSignPlan,
        verbose_name=_("Traffic Sign Plan"),
        on_delete=models.PROTECT,
        blank=True,
        null=True,
    )
    plan = models.ForeignKey(
        Plan,
        verbose_name=_("Plan"),
        on_delete=models.PROTECT,
        related_name="road_marking_plans",
        blank=True,
        null=True,
    )
    type_specifier = models.CharField(
        _("Type specifier"), max_length=254, blank=True, null=True
    )
    seasonal_validity_period_start = models.DateField(
        _("Seasonal validity period start"), blank=True, null=True
    )
    seasonal_validity_period_end = models.DateField(
        _("Seasonal validity period end"), blank=True, null=True
    )
    owner = models.ForeignKey(
        "traffic_control.Owner",
        verbose_name=_("Owner"),
        blank=False,
        null=False,
        on_delete=models.PROTECT,
    )
    symbol = models.CharField(_("Symbol"), max_length=254, blank=True, null=True)
    lifecycle = EnumIntegerField(
        Lifecycle, verbose_name=_("Lifecycle"), default=Lifecycle.ACTIVE
    )
    road_name = models.CharField(_("Road name"), max_length=254, blank=True, null=True)
    lane_number = EnumField(
        LaneNumber, verbose_name=_("Lane number"), default=LaneNumber.MAIN_1, blank=True
    )
    lane_type = EnumField(
        LaneType,
        verbose_name=_("Lane type"),
        default=LaneType.MAIN,
        blank=True,
    )
    location_specifier = EnumIntegerField(
        LocationSpecifier,
        verbose_name=_("Location specifier"),
        default=LocationSpecifier.RIGHT_SIDE_OF_LANE,
        blank=True,
        null=True,
    )
    length = models.IntegerField(_("Length"), blank=True, null=True)
    width = models.IntegerField(_("Width"), blank=True, null=True)
    is_raised = models.BooleanField(_("Is raised"), null=True)
    is_grinded = models.BooleanField(_("Is grinded"), null=True)
    additional_info = models.TextField(_("Additional info"), blank=True, null=True)
    amount = models.CharField(_("Amount"), max_length=254, blank=True, null=True)

    objects = SoftDeleteQuerySet.as_manager()

    class Meta:
        db_table = "road_marking_plan"
        verbose_name = _("Road Marking Plan")
        verbose_name_plural = _("Road Marking Plans")
        unique_together = ["source_name", "source_id"]

    def __str__(self):
        return f"{self.id} {self.device_type} {self.value}"

    def save(self, *args, **kwargs):
        if not self.device_type.validate_relation(DeviceTypeTargetModel.ROAD_MARKING):
            raise ValidationError(
                f'Device type "{self.device_type}" is not allowed for road markings'
            )

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

        if (
            self.device_type.type == TrafficControlDeviceTypeType.TRANSVERSE
            and self.road_name == ""
        ):
            raise ValidationError(
                f'Road name is required for "{TrafficControlDeviceTypeType.TRANSVERSE.value}" road marking'
            )
Esempio n. 23
0
class Violation(models.Model, BaseModel, SourcesMixin, VersionsMixin,
                GetComplexFieldNameMixin):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)

    published = models.BooleanField(default=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Dates and status
        self.startdate = ComplexFieldContainer(self, ViolationStartDate)
        self.first_allegation = ComplexFieldContainer(
            self, ViolationFirstAllegation)
        self.enddate = ComplexFieldContainer(self, ViolationEndDate)
        self.last_update = ComplexFieldContainer(self, ViolationLastUpdate)
        self.status = ComplexFieldContainer(self, ViolationStatus)

        # Location fields
        self.locationdescription = ComplexFieldContainer(
            self, ViolationLocationDescription)
        self.adminlevel1 = ComplexFieldContainer(self, ViolationAdminLevel1)
        self.adminlevel2 = ComplexFieldContainer(self, ViolationAdminLevel2)
        self.osmname = ComplexFieldContainer(self, ViolationOSMName)
        self.osmid = ComplexFieldContainer(self, ViolationOSMId)
        self.division_id = ComplexFieldContainer(self, ViolationDivisionId)
        self.location = ComplexFieldContainer(self, ViolationLocation)
        self.location_name = ComplexFieldContainer(self, ViolationLocationName)
        self.location_id = ComplexFieldContainer(self, ViolationLocationId)

        # Descriptions and other attributes
        self.description = ComplexFieldContainer(self, ViolationDescription)
        self.perpetrator = ComplexFieldListContainer(self,
                                                     ViolationPerpetrator)
        self.perpetratororganization = ComplexFieldListContainer(
            self, ViolationPerpetratorOrganization)
        self.perpetratorclassification = ComplexFieldListContainer(
            self, ViolationPerpetratorClassification)
        self.types = ComplexFieldListContainer(self, ViolationType)

        self.complex_fields = [
            self.startdate, self.first_allegation, self.enddate,
            self.last_update, self.status, self.locationdescription,
            self.adminlevel1, self.adminlevel2, self.location,
            self.description, self.division_id
        ]

        self.complex_lists = [
            self.perpetrator, self.perpetratororganization,
            self.perpetratorclassification, self.types
        ]

        self.required_fields = [self.description, self.startdate, self.enddate]

    def get_value(self):
        return self.description.get_value()

    @property
    def related_entities(self):
        """
        Return a list of dicts with metadata for all of the entities linked to
        this Violation.

        Metadata dicts must have the following keys:
            - name
            - entity_type
            - url (a link to edit the entity)
        """
        related_entities = []

        # Second-highest administrative level for the location of the violation.
        if self.adminlevel1.get_value():
            location = self.adminlevel1.get_value().value
            related_entities.append({
                'name':
                location.name,
                'entity_type':
                _('AdminLevel1'),
                'url':
                reverse('edit-violation', args=[self.uuid])
            })

        # Highest administrative level for the location of the violation.
        if self.adminlevel2.get_value():
            location = self.adminlevel2.get_value().value
            related_entities.append({
                'name':
                location.name,
                'entity_type':
                _('AdminLevel2'),
                'url':
                reverse('edit-violation', args=[self.uuid])
            })

        # The location of the violation.
        if self.location.get_value():
            location = self.location.get_value().value
            related_entities.append({
                'name':
                location.name,
                'entity_type':
                _('Location'),
                'url':
                reverse('edit-violation', args=[self.uuid])
            })

        # The perpetrators of the violation (personnel).
        perpetrators = self.perpetrator.get_list()
        if perpetrators:
            perpetrators = [
                perp.get_value() for perp in perpetrators if perp.get_value()
            ]
            for perpetrator in perpetrators:
                person = perpetrator.value
                related_entities.append({
                    'name':
                    person.name.get_value().value,
                    'entity_type':
                    _('Perpetrator'),
                    'url':
                    reverse('edit-violation', args=[self.uuid])
                })

        # The perpetrators of the violation (organizations).
        perpetratororganizations = self.perpetratororganization.get_list()
        if perpetratororganizations:
            perpetrators = [
                perp.get_value() for perp in perpetratororganizations
                if perp.get_value()
            ]
            for perpetrator in perpetrators:
                org = perpetrator.value
                related_entities.append({
                    'name':
                    org.name.get_value().value,
                    'entity_type':
                    _('PerpetratorOrganization'),
                    'url':
                    reverse('edit-violation', args=[self.uuid])
                })

        return related_entities
Esempio n. 24
0
class Country(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    name = models.CharField(unique=True, max_length=100)

    class Meta:
        db_table = 'countries'
Esempio n. 25
0
class TileModel(models.Model):  #Tile
    """
    the data JSONField has this schema:

    values are dictionaries with n number of keys that represent nodeid's and values the value of that node instance

    .. code-block:: python

        {
            nodeid: node value,
            nodeid: node value,
            ...
        }

        {
            "20000000-0000-0000-0000-000000000002": "John",
            "20000000-0000-0000-0000-000000000003": "Smith",
            "20000000-0000-0000-0000-000000000004": "Primary"
        }

    the provisionaledits JSONField has this schema:

    values are dictionaries with n number of keys that represent nodeid's and values the value of that node instance

    .. code-block:: python

        {
            userid: {
                value: node value,
                status: "review", "approved", or "rejected"
                action: "create", "update", or "delete"
                reviewer: reviewer's user id,
                timestamp: time of last provisional change,
                reviewtimestamp: time of review
                }
            ...
        }

        {
            1: {
                "value": {
                        "20000000-0000-0000-0000-000000000002": "Jack",
                        "20000000-0000-0000-0000-000000000003": "Smith",
                        "20000000-0000-0000-0000-000000000004": "Primary"
                      },
                "status": "rejected",
                "action": "update",
                "reviewer": 8,
                "timestamp": "20180101T1500",
                "reviewtimestamp": "20180102T0800",
                },
            15: {
                "value": {
                        "20000000-0000-0000-0000-000000000002": "John",
                        "20000000-0000-0000-0000-000000000003": "Smith",
                        "20000000-0000-0000-0000-000000000004": "Secondary"
                      },
                "status": "review",
                "action": "update",
        }

    """

    tileid = models.UUIDField(
        primary_key=True, default=uuid.uuid1)  # This field type is a guess.
    resourceinstance = models.ForeignKey(ResourceInstance,
                                         db_column='resourceinstanceid')
    parenttile = models.ForeignKey('self',
                                   db_column='parenttileid',
                                   blank=True,
                                   null=True)
    data = JSONField(blank=True, null=True,
                     db_column='tiledata')  # This field type is a guess.
    nodegroup = models.ForeignKey(NodeGroup, db_column='nodegroupid')
    sortorder = models.IntegerField(blank=True, null=True, default=0)
    provisionaledits = JSONField(
        blank=True, null=True,
        db_column='provisionaledits')  # This field type is a guess.

    class Meta:
        managed = True
        db_table = 'tiles'

    def save(self, *args, **kwargs):
        if (self.sortorder is None
                or (self.provisionaledits is not None and self.data == {})):
            sortorder_max = TileModel.objects.filter(
                nodegroup_id=self.nodegroup_id,
                resourceinstance_id=self.resourceinstance_id).aggregate(
                    Max('sortorder'))['sortorder__max']
            self.sortorder = sortorder_max + 1 if sortorder_max is not None else 0
        super(TileModel, self).save(*args,
                                    **kwargs)  # Call the "real" save() method.
Esempio n. 26
0
class Sequence(models.Model):
    unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
    user = models.ForeignKey(UserModel, on_delete=models.CASCADE)
    camera_make = models.CharField(max_length=50, default='')
    captured_at = models.DateTimeField(null=True)
    created_at = models.DateTimeField(null=True)
    seq_key = models.CharField(max_length=100)
    pano = models.BooleanField(default=False)
    user_key = models.CharField(max_length=100)
    username = models.CharField(max_length=100)
    geometry_coordinates = models.LineStringField(null=True)
    geometry_coordinates_ary = ArrayField(ArrayField(models.FloatField(default=1)), null=True)
    coordinates_cas = ArrayField(models.FloatField(default=0), null=True)
    coordinates_ele = ArrayField(models.FloatField(default=0), null=True)
    coordinates_image = ArrayField(models.CharField(default='', max_length=100), null=True)
    is_uploaded = models.BooleanField(default=False)
    is_privated = models.BooleanField(default=False)
    updated_at = models.DateTimeField(default=datetime.now, blank=True)

    name = models.CharField(max_length=100, default='')
    description = models.TextField(default='')
    transport_type = models.ForeignKey(TransType, on_delete=models.CASCADE, null=True)
    tag = models.ManyToManyField(Tag)

    image_count = models.IntegerField(default=0)

    is_mapillary = models.BooleanField(default=True)
    is_published = models.BooleanField(default=True)

    def getImageCount(self):
        if not self.coordinates_image is None:
            return len(self.coordinates_image)
        else:
            return 0

    def getTransportType(self):
        captureType = []
        for t in self.type:
            cType = TransType.objects.get(pk=t)
            if cType:
                captureType.append(cType.name)

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

    def getTagStr(self):
        tags = []
        if self.tag.all().count() == 0:
            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.all().count() == 0:
            return ''
        for tag in self.tag.all():
            if tag and tag.is_actived:
                tags.append(tag.name)
        return tags

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

    def getFirstImageKey(self):
        if len(self.coordinates_image) > 0:
            return self.coordinates_image[0]
        else:
            return ''

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

    def getDistance(self):
        all_distance = 0
        if (len(self.geometry_coordinates_ary) > 0):
            first_point = self.geometry_coordinates_ary[0]
            for i in range(len(self.geometry_coordinates_ary) - 1):
                if i == 0:
                    continue
                second_point = self.geometry_coordinates_ary[i]
                d = distance(first_point, second_point)
                first_point = second_point
                all_distance += d
            all_distance = "%.3f" % all_distance
        return all_distance

    def getCoverImage(self):
        image_keys = self.coordinates_image
        if len(image_keys) > 0:
            return image_keys[0]
        else:
            return None

    def getFirstPointLat(self):
        lat = self.geometry_coordinates_ary[0][1]
        return lat

    def getFirstPointLng(self):
        lng = self.geometry_coordinates_ary[0][0]
        return lng
Esempio n. 27
0
class Organisation(models.Model):
    class Meta(object):
        verbose_name = "Organisation"
        verbose_name_plural = "Organisations"
        ordering = ('slug', )

    legal_name = models.CharField(
        verbose_name="Dénomination sociale",
        max_length=100,
        unique=True,
        db_index=True,
    )

    organisation_type = models.ForeignKey(
        to='OrganisationType',
        verbose_name="Type d'organisation",
        blank=True,
        null=True,
        on_delete=models.SET_NULL,
    )

    jurisdiction = models.ForeignKey(
        to='Jurisdiction',
        verbose_name="Territoire de compétence",
        blank=True,
        null=True,
    )

    slug = models.SlugField(
        verbose_name="Slug",
        max_length=100,
        unique=True,
        db_index=True,
    )

    ckan_id = models.UUIDField(
        verbose_name="Ckan UUID",
        default=uuid.uuid4,
        editable=False,
    )

    website = models.URLField(
        verbose_name="Site internet",
        blank=True,
        null=True,
    )

    email = models.EmailField(
        verbose_name="Adresse e-mail",
        blank=True,
        null=True,
    )

    description = models.TextField(
        verbose_name='Description',
        blank=True,
        null=True,
    )

    logo = models.ImageField(
        verbose_name="Logo",
        blank=True,
        null=True,
        upload_to='logos/',
    )

    address = models.TextField(
        verbose_name="Adresse",
        blank=True,
        null=True,
    )

    postcode = models.CharField(
        verbose_name="Code postal",
        max_length=100,
        blank=True,
        null=True,
    )

    city = models.CharField(
        verbose_name="Ville",
        max_length=100,
        blank=True,
        null=True,
    )

    phone = models.CharField(
        verbose_name="Téléphone",
        max_length=10,
        blank=True,
        null=True,
    )

    license = models.ForeignKey(
        to='License',
        verbose_name="Licence",
        on_delete=models.CASCADE,
        blank=True,
        null=True,
    )

    is_active = models.BooleanField(
        verbose_name="Organisation active",
        default=False,
    )

    is_crige_partner = models.BooleanField(
        verbose_name="Organisation partenaire du CRIGE",
        default=False,
    )

    geonet_id = models.TextField(
        verbose_name="UUID de la métadonnées",
        unique=True,
        db_index=True,
        blank=True,
        null=True,
    )

    def __str__(self):
        return self.legal_name

    @property
    def logo_url(self):
        try:
            return urljoin(settings.DOMAIN_NAME, self.logo.url)
        except (ValueError, Exception):
            return None

    @property
    def full_address(self):
        return "{} - {} {}".format(self.address, self.postcode, self.city)

    @property
    def ows_url(self):
        if MRAHandler.is_workspace_exists(self.slug):
            return OWS_URL_PATTERN.format(organisation=self.slug)

    @property
    def ows_settings(self):
        if MRAHandler.is_workspace_exists(self.slug):
            return MRAHandler.get_ows_settings('ows', self.slug)

    @property
    def api_location(self):
        kwargs = {'organisation_name': self.slug}
        return reverse('api:organisation_show', kwargs=kwargs)

    @property
    def members(self):
        Dataset = apps.get_model(app_label='idgo_admin', model_name='Dataset')
        Profile = apps.get_model(app_label='idgo_admin', model_name='Profile')
        LiaisonsContributeurs = apps.get_model(
            app_label='idgo_admin', model_name='LiaisonsContributeurs')
        LiaisonsReferents = apps.get_model(app_label='idgo_admin',
                                           model_name='LiaisonsReferents')

        filter = reduce(ior, [
            Q(organisation=self.pk),
            reduce(iand, [
                Q(liaisonscontributeurs__organisation=self.pk),
                Q(liaisonscontributeurs__validated_on__isnull=False)
            ]),
            reduce(iand, [
                Q(liaisonsreferents__organisation=self.pk),
                Q(liaisonsreferents__validated_on__isnull=False),
            ])
        ])

        profiles = Profile.objects.filter(filter).distinct().order_by(
            'user__username')

        data = [{
            'username':
            member.user.username,
            'full_name':
            member.user.get_full_name(),
            'is_member':
            Profile.objects.filter(organisation=self.pk,
                                   id=member.id).exists(),
            'is_contributor':
            LiaisonsContributeurs.objects.filter(
                profile=member,
                organisation__id=self.pk,
                validated_on__isnull=False).exists(),
            'is_referent':
            LiaisonsReferents.objects.filter(
                profile=member,
                organisation__id=self.pk,
                validated_on__isnull=False).exists(),
            'crige_membership':
            member.crige_membership,
            'datasets_count':
            len(
                Dataset.objects.filter(organisation=self.pk,
                                       editor=member.user)),
            'profile_id':
            member.id
        } for member in profiles]

        return data

    def get_datasets(self, **kwargs):
        Dataset = apps.get_model(app_label='idgo_admin', model_name='Dataset')
        return Dataset.objects.filter(organisation=self, **kwargs)

    def get_crige_membership(self):
        Profile = apps.get_model(app_label='idgo_admin', model_name='Profile')
        qs = Profile.objects.filter(organisation=self, crige_membership=True)
        return [profile.user for profile in qs]

    def get_members(self):
        """Retourner la liste des utilisateurs membres de l'organisation."""
        Profile = apps.get_model(app_label='idgo_admin', model_name='Profile')
        profiles = Profile.objects.filter(organisation=self,
                                          membership=True,
                                          is_active=True)
        return [e.user for e in profiles]

    def get_contributors(self):
        """Retourner la liste des utilisateurs contributeurs de l'organisation."""
        Nexus = apps.get_model(app_label='idgo_admin',
                               model_name='LiaisonsContributeurs')
        entries = Nexus.objects.filter(organisation=self,
                                       validated_on__isnull=False)
        return [e.profile.user for e in entries if e.profile.is_active]

    def get_referents(self):
        """Retourner la liste des utilisateurs référents de l'organisation."""
        Nexus = apps.get_model(app_label='idgo_admin',
                               model_name='LiaisonsReferents')
        entries = Nexus.objects.filter(organisation=self,
                                       validated_on__isnull=False)
        return [e.profile.user for e in entries if e.profile.is_active]
Esempio n. 28
0
class User(AbstractUser, BaseModel):
    """
    User model.
    """
    class Sex:
        male = 'M'
        female = 'F'
        none = 'N'

        choices = (
            (male, _('Male')),
            (female, _('Female')),
            (none, _('None')),
        )

    username_validator = InternationNubmerValidator()
    username = models.CharField(
        _('username'),
        max_length=15,
        unique=True,
        help_text=_('Required. 15 characters or fewer. '
                    'Validated by ITU-T recommendation (E.164).'),
        validators=[username_validator],
        error_messages={
            'unique': _('A user with that username already exists.'),
        },
    )

    first_name = None
    last_name = None
    created_at = property(lambda x: x.date_joined)
    date_joined = models.DateTimeField(_('date joined'),
                                       default=timezone.now,
                                       db_column='created_at')
    display_name = models.CharField(max_length=32,
                                    blank=True,
                                    help_text=_('Display name.'))
    avatar_uuid = models.UUIDField(blank=True, null=True)
    device_id = models.CharField(max_length=180, blank=True, null=True)
    sex = models.CharField(
        max_length=1,
        choices=Sex.choices,  # default=Sex.none,
        help_text='Valid Values: M or F')
    location = models.PointField(blank=True,
                                 null=True,
                                 help_text='Last user location.')
    birth_date = models.DateField(null=True,
                                  default=None,
                                  help_text=_('Birtht date.'))
    sms_code = models.OneToOneField(SMSCode,
                                    null=True,
                                    on_delete=models.CASCADE,
                                    related_name='sms_code')
    confirm_tos = models.BooleanField(default=False,
                                      help_text='User confirm the offer.')
    last_activity = models.DateTimeField(blank=True,
                                         null=True,
                                         help_text='Last user activity')
    show_activity = models.BooleanField(
        default=True, help_text='Show user ativity to others')
    is_restricted = models.BooleanField(default=False,
                                        help_text='User is blocked.')
    is_online = models.BooleanField(default=False, help_text='Is user online')

    black_list = models.ManyToManyField('self', blank=True, symmetrical=False)

    interests = TaggableManager(verbose_name='interests')

    class Meta:
        db_table = 'users'

    @cached_property
    def owner(self):
        return self

    @property
    def phone(self):
        return str(self.username)

    @property
    def is_new_user(self):
        """ Новый требующий дополнительные шаги для заполнения профиля. """
        return not self.confirm_tos

    def delete(self, *args, **kwargs):
        """ Не удаляем пользователей. """
        self.deleted_at = timezone.now()
        self.save()

    def black_listed(self, user):
        """ Пользователь входит в черный список. """
        return self.black_list.filter(pk=user.pk).exists()

    def get_avatar_url(self):
        if self.avatar_uuid:
            return get_public_url(self.avatar_uuid, prefix='av')
Esempio n. 29
0
class Node(models.Model):
    """
    Name is unique across all resources because it ties a node to values within tiles. Recommend prepending resource class to node name.

    """

    nodeid = models.UUIDField(primary_key=True, default=uuid.uuid1)
    name = models.TextField()
    description = models.TextField(blank=True, null=True)
    istopnode = models.BooleanField()
    ontologyclass = models.TextField(blank=True, null=True)
    datatype = models.TextField()
    nodegroup = models.ForeignKey(NodeGroup,
                                  db_column='nodegroupid',
                                  blank=True,
                                  null=True)
    graph = models.ForeignKey(GraphModel,
                              db_column='graphid',
                              blank=True,
                              null=True)
    config = JSONField(blank=True, null=True, db_column='config')
    issearchable = models.BooleanField(default=True)
    isrequired = models.BooleanField(default=False)
    sortorder = models.IntegerField(blank=True, null=True, default=0)

    def get_child_nodes_and_edges(self):
        """
        gather up the child nodes and edges of this node

        returns a tuple of nodes and edges

        """
        nodes = []
        edges = []
        for edge in Edge.objects.filter(domainnode=self):
            nodes.append(edge.rangenode)
            edges.append(edge)

            child_nodes, child_edges = edge.rangenode.get_child_nodes_and_edges(
            )
            nodes.extend(child_nodes)
            edges.extend(child_edges)
        return (nodes, edges)

    @property
    def is_collector(self):
        return str(self.nodeid) == str(
            self.nodegroup_id) and self.nodegroup is not None

    def get_relatable_resources(self):
        relatable_resource_ids = [
            r2r.resourceclassfrom
            for r2r in Resource2ResourceConstraint.objects.filter(
                resourceclassto_id=self.nodeid)
        ]
        relatable_resource_ids = relatable_resource_ids + [
            r2r.resourceclassto
            for r2r in Resource2ResourceConstraint.objects.filter(
                resourceclassfrom_id=self.nodeid)
        ]
        return relatable_resource_ids

    def set_relatable_resources(self, new_ids):
        old_ids = [res.nodeid for res in self.get_relatable_resources()]
        for old_id in old_ids:
            if old_id not in new_ids:
                Resource2ResourceConstraint.objects.filter(
                    Q(resourceclassto_id=self.nodeid)
                    | Q(resourceclassfrom_id=self.nodeid),
                    Q(resourceclassto_id=old_id)
                    | Q(resourceclassfrom_id=old_id)).delete()
        for new_id in new_ids:
            if new_id not in old_ids:
                new_r2r = Resource2ResourceConstraint.objects.create(
                    resourceclassfrom_id=self.nodeid,
                    resourceclassto_id=new_id)
                new_r2r.save()

    class Meta:
        managed = True
        db_table = 'nodes'
Esempio n. 30
0
class Organization(AuditModel):
    org_guid = models.UUIDField(primary_key=True,
                                default=uuid.uuid4,
                                editable=False,
                                verbose_name="Organization UUID")
    name = models.CharField(max_length=200,
                            db_comment='Company\'s Doing Business As name.')
    street_address = models.CharField(
        max_length=100,
        null=True,
        blank=True,
        verbose_name='Street Address',
        db_comment='Street address used for mailing address for the company.')
    city = models.CharField(
        max_length=50,
        null=True,
        blank=True,
        verbose_name='Town/City',
        db_comment='City used for mailing address for the company.')
    province_state = models.ForeignKey(
        ProvinceStateCode,
        db_column='province_state_code',
        on_delete=models.PROTECT,
        verbose_name='Province/State',
        related_name='companies',
        db_comment=
        'Province or state used for the mailing address for the company')
    postal_code = models.CharField(
        max_length=10,
        null=True,
        blank=True,
        verbose_name='Postal Code',
        db_comment='Postal code used for mailing address for the company')
    main_tel = models.CharField(
        null=True,
        blank=True,
        max_length=15,
        verbose_name='Telephone number',
        db_comment='Telephone number used to contact the company')
    fax_tel = models.CharField(
        null=True,
        blank=True,
        max_length=15,
        verbose_name='Fax number',
        db_comment='Fax number used to contact the company')
    website_url = models.URLField(
        null=True,
        blank=True,
        verbose_name='Website',
        db_comment='The web address associated with the company')
    effective_date = models.DateTimeField(
        default=timezone.now,
        null=False,
        db_comment=
        'The date the the organization record became available for use.')
    expiry_date = models.DateTimeField(
        default=timezone.make_aware(timezone.datetime.max,
                                    timezone.get_default_timezone()),
        null=False,
        db_comment=
        ('The date that the organization record was soft deleted (expired) by the staff.'
         ' Common reasons for deleting the record would be to remove duplicates, and'
         ' erroneous entries there by making these organizations unavailable for use.'
         ))
    email = models.EmailField(
        blank=True,
        null=True,
        verbose_name="Email adddress",
        db_comment=
        ('The email address for a company, this is different from the email for the individual '
         'who is a registered driller or pump installer.'))

    history = GenericRelation(Version)

    class Meta:
        db_table = 'registries_organization'
        ordering = ['name']
        verbose_name_plural = 'Organizations'

    db_table_comment = 'Placeholder table comment.'

    def __str__(self):
        return self.name

    @property
    def org_verbose_name(self):
        prov = self.province_state.province_state_code

        # display either "City, Province" or just "Province"
        location = '{}, {}'.format(self.city, prov) if (
            self.city is not None and len(self.city) is not 0) else prov

        return '{} ({})'.format(self.name, location)

    @property
    def mailing_address(self):
        address = [
            self.street_address,
            self.city,
            self.province_state_id,
            self.postal_code,
        ]
        return ", ".join([part for part in address if part])