Esempio n. 1
0
class Plot(models.Model, ManagementMixin, PendingMixin):
    present = models.BooleanField(default=True)
    width = models.FloatField(
        null=True,
        blank=True,
        error_messages={'invalid': "Error: This value must be a number."})
    length = models.FloatField(
        null=True,
        blank=True,
        error_messages={'invalid': "Error: This value must be a number."})

    type = models.CharField(max_length=256,
                            null=True,
                            blank=True,
                            choices=settings.CHOICES["plot_types"])
    powerline_conflict_potential = models.CharField(
        max_length=256,
        choices=settings.CHOICES["powerlines"],
        help_text="Are there overhead powerlines present?",
        null=True,
        blank=True)
    sidewalk_damage = models.CharField(max_length=256,
                                       null=True,
                                       blank=True,
                                       choices=settings.CHOICES["sidewalks"])

    address_street = models.CharField(max_length=256, blank=True, null=True)
    address_city = models.CharField(max_length=256, blank=True, null=True)
    address_zip = models.CharField(max_length=30, blank=True, null=True)
    neighborhood = models.ManyToManyField(Neighborhood, null=True)
    neighborhoods = models.CharField(
        max_length=150, null=True,
        blank=True)  # Really this should be 'blank=True' and null=False
    zipcode = models.ForeignKey(
        ZipCode, null=True,
        blank=True)  # Because it is calculated in the save method

    geocoded_accuracy = models.IntegerField(null=True, blank=True)
    geocoded_address = models.CharField(max_length=256, null=True, blank=True)
    geocoded_lat = models.FloatField(null=True, blank=True)
    geocoded_lon = models.FloatField(null=True, blank=True)

    geometry = models.PointField(srid=4326)

    #geocoded_geometry = models.PointField(null=True, srid=4326)
    #owner_geometry = models.PointField(null=True, srid=4326) #should we keep this?

    last_updated = models.DateTimeField(auto_now=True)
    last_updated_by = models.ForeignKey(
        User, related_name='plot_updated_by')  # TODO set to current user

    history = audit.AuditTrail()
    import_event = models.ForeignKey(ImportEvent)
    objects = models.GeoManager()
    # The locate Manager encapsulates plot search functionality
    locate = PlotLocateManager()

    #original data to help owners associate back to their own db
    data_owner = models.ForeignKey(User,
                                   related_name="owner",
                                   null=True,
                                   blank=True)
    owner_orig_id = models.CharField(max_length=256, null=True, blank=True)
    owner_additional_id = models.CharField(max_length=255,
                                           null=True,
                                           blank=True)
    owner_additional_properties = models.TextField(
        null=True,
        blank=True,
        help_text="Additional Properties (not searchable)")

    readonly = models.BooleanField(default=False)

    def itree_region(self):
        zone = ClimateZone.objects.filter(geometry__contains=self.geometry)

        if len(zone) == 0:
            return None
        else:
            return zone[0].itree_region

    def validate(self):
        self.full_clean()
        em = ExclusionMask.objects.filter(geometry__contains=self.geometry)
        if em.count() > 0:
            raise ValidationError(
                "Geometry may not be within an exclusion zone.")

    def get_plot_type_display(self):
        for key, value in settings.CHOICES["plot_types"]:
            if key == self.type:
                return value
        return None

    def get_plot_size(self):
        length = self.length
        width = self.width
        if length == None: length = 'Missing'
        elif length == 99: length = '15+ ft'
        else: length = '%.2f ft' % length
        if width == None: width = 'Missing'
        elif width == 99: width = '15+ ft'
        else: width = '%.2f ft' % width
        #print length, width
        return '%s x %s' % (length, width)

    def get_sidewalk_damage_display(self):
        for key, value in settings.CHOICES["sidewalks"]:
            if key == self.sidewalk_damage:
                return value
        return None

    def get_powerline_conflict_display(self):
        for key, value in settings.CHOICES["powerlines"]:
            if key == self.powerline_conflict_potential:
                return value
        return None

    def get_stewardship_count(self):
        return len(self.plotstewardship_set.all())

    def current_tree(self):
        trees = self.tree_set.filter(present=True)
        if trees.count() > 0:
            return trees[0]
        else:
            return None

    def get_active_pends(self):
        pends = self.plotpending_set.filter(status='pending')
        return pends

    def get_active_geopends(self):
        pends = self.plotpending_set.filter(status='pending').exclude(
            geometry=None)
        return pends

    def get_active_pends_with_tree_pends(self):
        plot_pends = self.plotpending_set.filter(status='pending')
        if self.current_tree():
            tree_pends = self.current_tree().get_active_pends()
        else:
            tree_pends = []
        pends = list(chain(plot_pends, tree_pends))
        return pends

    def get_plot_size(self):
        length = self.length
        width = self.width
        if length == None: length = 'Missing'
        elif length == 99: length = '15+ ft'
        else: length = '%.2f ft' % length
        if width == None: width = 'Missing'
        elif width == 99: width = '15+ ft'
        else: width = '%.2f ft' % width
        return '%s x %s' % (length, width)

    def quick_save(self, *args, **kwargs):
        super(Plot, self).save(*args, **kwargs)

    def save(self, *args, **kwargs):
        self.validate()

        pnt = self.geometry

        n = Neighborhood.objects.filter(geometry__contains=pnt)
        z = ZipCode.objects.filter(geometry__contains=pnt)

        if n:
            oldns = self.neighborhoods
            new_nhoods = []
            for nhood in n:
                if nhood:
                    new_nhoods.append(nhood.id.__str__())
            self.neighborhoods = " ".join(new_nhoods)
        else:
            self.neighborhoods = ""
            oldns = None

        if self.id:
            oldn = self.neighborhood.all()
            oldz = self.zipcode
        else:
            oldn = []
            oldz = None

        super(Plot, self).save(*args, **kwargs)
        if n:
            self.neighborhood.clear()
            for nhood in n:
                if nhood:
                    self.neighborhood.add(nhood)
        else:
            self.neighborhood.clear()
        if z: self.zipcode = z[0]
        else: self.zipcode = None

        if self.current_tree():
            set_environmental_summaries(self.current_tree())

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

        if self.neighborhoods != oldns:
            done = []
            if n:
                for nhood in n:
                    if nhood.id in done: continue
                    if self.current_tree():
                        self.current_tree().update_aggregate(
                            AggregateNeighborhood, nhood)
                    else:
                        self.update_aggregate(AggregateNeighborhood, nhood)
                    done.append(nhood.id)
            if oldn:
                for nhood in oldn:
                    if nhood.id in done: continue
                    if self.current_tree():
                        self.current_tree().update_aggregate(
                            AggregateNeighborhood, nhood)
                    else:
                        self.update_aggregate(AggregateNeighborhood, nhood)
                    done.append(nhood.id)

        if self.current_tree() and z and z[0] != oldz:
            if z: self.current_tree().update_aggregate(AggregateZipCode, z[0])
            if oldz:
                self.current_tree().update_aggregate(AggregateZipCode, oldz)

    def update_aggregate(self, ag_model, location):
        agg = ag_model.objects.filter(location=location)
        if agg:
            agg = agg[0]
        else:
            agg = ag_model(location=location)
        #print agg.__dict__
        #summaries = []
        trees = Tree.objects.filter(plot__geometry__within=location.geometry)
        plots = Plot.objects.filter(geometry__within=location.geometry)
        #print trees
        agg.total_trees = trees.count()
        agg.total_plots = plots.count()

        agg.save()

    def validate_proximity(self, return_trees=False, max_count=1):
        if not self.geometry:
            return None
        nearby = Plot.objects.filter(present=True,
                                     geometry__distance_lte=(self.geometry,
                                                             D(ft=10.0)))
        if nearby.count() > max_count:
            if return_trees:
                return nearby
            return (nearby.count() -
                    max_count).__str__()  #number greater than max_count allows
        return None

    def remove(self):
        """
        Mark the plot and its associated objects as not present.
        """
        if self.current_tree():
            tree = self.current_tree()
            tree.remove()

        self.present = False
        self.save()

        for audit_trail_record in self.history.all():
            audit_trail_record.present = False
            audit_trail_record.save()
Esempio n. 2
0
class Biology(Occurrence):
    infraspecificepithet = models.CharField(null=True,
                                            blank=True,
                                            max_length=50)
    infraspecificrank = models.CharField(null=True, blank=True, max_length=50)
    authoryearofscientificname = models.CharField(null=True,
                                                  blank=True,
                                                  max_length=50)
    nomenclaturalcode = models.CharField(null=True, blank=True, max_length=50)
    identificationqualifier = models.CharField(null=True,
                                               blank=True,
                                               max_length=50)
    identifiedby = models.CharField(null=True, blank=True, max_length=100)
    dateidentified = models.DateTimeField(null=True, blank=True)
    typestatus = models.CharField(null=True, blank=True, max_length=50)
    sex = models.CharField(null=True, blank=True, max_length=50)
    lifestage = models.CharField(null=True, blank=True, max_length=50)
    preparations = models.CharField(null=True, blank=True, max_length=50)
    morphobanknum = models.IntegerField(null=True, blank=True)
    side = models.CharField(null=True, blank=True, max_length=50)
    attributes = models.CharField(null=True, blank=True, max_length=50)
    faunanotes = models.TextField(null=True, blank=True, max_length=64000)
    toothupperorlower = models.CharField(null=True, blank=True, max_length=50)
    toothnumber = models.CharField(null=True, blank=True, max_length=50)
    toothtype = models.CharField(null=True, blank=True, max_length=50)
    umtoothrowlengthmm = models.FloatField(null=True, blank=True)
    um1lengthmm = models.FloatField(null=True, blank=True)
    um1widthmm = models.FloatField(null=True, blank=True)
    um2lengthmm = models.FloatField(null=True, blank=True)
    um2widthmm = models.FloatField(null=True, blank=True)
    um3lengthmm = models.FloatField(null=True, blank=True)
    um3widthmm = models.FloatField(null=True, blank=True)
    lmtoothrowlengthmm = models.FloatField(null=True, blank=True)
    lm1length = models.FloatField(null=True, blank=True)
    lm1width = models.FloatField(null=True, blank=True)
    lm2length = models.FloatField(null=True, blank=True)
    lm2width = models.FloatField(null=True, blank=True)
    lm3length = models.FloatField(null=True, blank=True)
    lm3width = models.FloatField(null=True, blank=True)
    element = models.CharField(null=True, blank=True, max_length=50)
    elementmodifier = models.CharField(null=True, blank=True, max_length=50)
    # TODO convert this field to boolean
    uli1 = models.IntegerField(null=True, blank=True)
    uli2 = models.IntegerField(null=True, blank=True)
    uli3 = models.IntegerField(null=True, blank=True)
    uli4 = models.IntegerField(null=True, blank=True)
    uli5 = models.IntegerField(null=True, blank=True)
    uri1 = models.IntegerField(null=True, blank=True)
    uri2 = models.IntegerField(null=True, blank=True)
    uri3 = models.IntegerField(null=True, blank=True)
    uri4 = models.IntegerField(null=True, blank=True)
    uri5 = models.IntegerField(null=True, blank=True)
    ulc = models.IntegerField(null=True, blank=True)
    urc = models.IntegerField(null=True, blank=True)
    ulp1 = models.IntegerField(null=True, blank=True)
    ulp2 = models.IntegerField(null=True, blank=True)
    ulp3 = models.IntegerField(null=True, blank=True)
    ulp4 = models.IntegerField(null=True, blank=True)
    urp1 = models.IntegerField(null=True, blank=True)
    urp2 = models.IntegerField(null=True, blank=True)
    urp3 = models.IntegerField(null=True, blank=True)
    urp4 = models.IntegerField(null=True, blank=True)
    ulm1 = models.IntegerField(null=True, blank=True)
    ulm2 = models.IntegerField(null=True, blank=True)
    ulm3 = models.IntegerField(null=True, blank=True)
    urm1 = models.IntegerField(null=True, blank=True)
    urm2 = models.IntegerField(null=True, blank=True)
    urm3 = models.IntegerField(null=True, blank=True)
    lli1 = models.IntegerField(null=True, blank=True)
    lli2 = models.IntegerField(null=True, blank=True)
    lli3 = models.IntegerField(null=True, blank=True)
    lli4 = models.IntegerField(null=True, blank=True)
    lli5 = models.IntegerField(null=True, blank=True)
    lri1 = models.IntegerField(null=True, blank=True)
    lri2 = models.IntegerField(null=True, blank=True)
    lri3 = models.IntegerField(null=True, blank=True)
    lri4 = models.IntegerField(null=True, blank=True)
    lri5 = models.IntegerField(null=True, blank=True)
    llc = models.IntegerField(null=True, blank=True)
    lrc = models.IntegerField(null=True, blank=True)
    llp1 = models.IntegerField(null=True, blank=True)
    llp2 = models.IntegerField(null=True, blank=True)
    llp3 = models.IntegerField(null=True, blank=True)
    llp4 = models.IntegerField(null=True, blank=True)
    lrp1 = models.IntegerField(null=True, blank=True)
    lrp2 = models.IntegerField(null=True, blank=True)
    lrp3 = models.IntegerField(null=True, blank=True)
    lrp4 = models.IntegerField(null=True, blank=True)
    llm1 = models.IntegerField(null=True, blank=True)
    llm2 = models.IntegerField(null=True, blank=True)
    llm3 = models.IntegerField(null=True, blank=True)
    lrm1 = models.IntegerField(null=True, blank=True)
    lrm2 = models.IntegerField(null=True, blank=True)
    lrm3 = models.IntegerField(null=True, blank=True)
    taxon = models.ForeignKey(Taxon, related_name='drp_biology_occurrences')
    identification_qualifier = models.ForeignKey(
        IdentificationQualifier, related_name='drp_biology_occurrences')

    class Meta:
        verbose_name = "DRP Biology"
        verbose_name_plural = "DRP Biology"
        #db_table='drp_biology'

    def __unicode__(self):
        return str(self.taxon.__unicode__())
Esempio n. 3
0
class Record(GroutModel):
    """
    An entity in the database. An entry of a given RecordType, following a
    schema defined by a certain RecordSchema.
    """
    schema = models.ForeignKey('RecordSchema', on_delete=models.CASCADE)
    data = JSONField(blank=True)  # `blank` lets us store empty dicts ({}).
    archived = models.BooleanField(default=False)
    occurred_from = models.DateTimeField(null=True, blank=True)
    occurred_to = models.DateTimeField(null=True, blank=True)
    geom = models.GeometryField(srid=settings.GROUT['SRID'],
                                null=True,
                                blank=True)
    location_text = models.CharField(max_length=200, null=True, blank=True)

    class Meta(object):
        ordering = ('-created', )

    def clean_geom(self):
        """
        Validate that the geometry of a new Record matches the geometry_type
        of its associated RecordType.

        :return: None if schema validates; otherwise, returns an error dict in the
                 format {'geom': '<error message>'}
        """
        expected_geotype = self.schema.record_type.get_geometry_type_display()

        if self.geom:
            incoming_geotype = self.geom.geom_type
        else:
            incoming_geotype = 'None'

        if incoming_geotype != expected_geotype:
            return {
                'geom':
                GEOMETRY_TYPE_ERROR.format(incoming=incoming_geotype,
                                           expected=expected_geotype,
                                           uuid=self.schema.record_type.uuid)
            }
        else:
            return None

    def clean_datetime(self):
        """
        Validate that the values for `occurred_from` and `occurred_to` match
        the `temporal` setting of the parent Record type and are syntactically
        valid.

        :return: None if the fields validate; otherwise, returns an error dict in the
                 format {'occurred_from': '<error_message>', 'occurred_to': '<error_message>'}
        """
        errors = {}
        datetime_required = self.schema.record_type.temporal

        if datetime_required:
            if self.occurred_from is None or self.occurred_to is None:
                # `occurred_from` and `occurred_to` must be present on a temporal
                # Record.
                if self.occurred_from is None:
                    errors['occurred_from'] = DATETIME_REQUIRED.format(
                        uuid=self.schema.record_type.uuid)
                if self.occurred_to is None:
                    errors['occurred_to'] = DATETIME_REQUIRED.format(
                        uuid=self.schema.record_type.uuid)
            else:
                # `occurred_from` cannot be a later date than `occurred_to`.
                # Since by the time that this method is called the values are
                # already attributes on the model, and since we've already
                # confirmed that neither value is null, we can assume for the
                # purposes of comparison that the values are datetime objects.
                if self.occurred_from > self.occurred_to:
                    errors['occurred_from'] = MIN_DATE_RANGE_ERROR
                    errors['occurred_to'] = MAX_DATE_RANGE_ERROR
        else:
            if self.occurred_from is not None:
                errors['occurred_from'] = DATETIME_NOT_PERMITTED.format(
                    uuid=self.schema.record_type.uuid)
            if self.occurred_to is not None:
                errors['occurred_to'] = DATETIME_NOT_PERMITTED.format(
                    uuid=self.schema.record_type.uuid)

        if errors.keys():
            return errors
        else:
            return None

    def clean_data(self):
        """
        Validate that the JSON represented by the `data` field matches the
        schema for this Record.

        :return: None if the schema validates; otherwise, returns an error dict
                 in the format {'data': '<error_message'}
        """
        try:
            return self.schema.validate_json(self.data)
        except jsonschema.exceptions.ValidationError as e:
            return {
                'data':
                SCHEMA_MISMATCH_ERROR.format(uuid=self.schema.uuid,
                                             message=e.message)
            }

    def clean(self):
        """
        Provide custom field validation for this model.
        """
        errors = {}

        # Make sure that incoming geometry matches the geometry_type of the
        # RecordType for this Record.
        geom_error = self.clean_geom()
        if geom_error:
            errors.update(geom_error)

        # Make sure that incoming datetime information matches the `temporal`
        # flag on the RecordType for this Record.
        datetime_error = self.clean_datetime()
        if datetime_error:
            errors.update(datetime_error)

        # Make sure that the incoming JSON data matches the RecordSchema
        # for this Record.
        schema_error = self.clean_data()
        if schema_error:
            errors.update(schema_error)

        if errors.keys():
            # Raise a DRF ValidationError instead of a Django Core validation
            # error, since this exception needs to get handled by the serializer.
            raise serializers.ValidationError(errors)

    def save(self, *args, **kwargs):
        """
        Extend the model's save method to run custom field validators.
        """
        self.clean()
        return super(Record, self).save(*args, **kwargs)
Esempio n. 4
0
class Factory(SoftDeleteMixin):
    """Factories that are potential to be illegal."""

    # List of fact_type & status
    factory_type_list = [
        ("2-1","沖床、銑床、車床、鏜孔"),
        ("2-2", "焊接、鑄造、熱處理"),
        ("2-3", "金屬表面處理、噴漆"),
        ("3", "塑膠加工、射出"),
        ("4", "橡膠加工"),
        ("5", "非金屬礦物(石材)"),
        ("6", "食品"),
        ("7", "皮革"),
        ("8", "紡織"),
        ("9", "其他"),
    ]
    cet_review_status_list = [
        ("A", "尚未審查"),
        ("O", "已審查-不檢舉"),
        ("P", "已審查-需補件"),
        ("Q", "已審查-待檢舉"),
        ("X", "已審查-已生成公文"),
    ]
    cet_report_status_list = [
        ("A", "未舉報"),
        ("O", "第一次發文待回覆"),
        ("P", "第一次發文已播電話追蹤"),
        ("Q", "第一次回文"),
        ("X", "第二次發文待回覆"),
        ("Y", "第二次發文已播電話追蹤"),
        ("Z", "第二次回文"),
        ("B", "已結案"),
    ]
    source_list = [
        ("G", "政府"),
        ("U", "使用者"),
    ]

    # All  Features
    id =  models.UUIDField(
        primary_key=True,
        default=uuid.uuid4,
        editable=False,
        verbose_name="ID",
    )

    lat = models.FloatField()
    lng = models.FloatField()
    point = models.PointField(srid=settings.POSTGIS_SRID)
    landcode = models.CharField(max_length=50, blank=True, null=True)
    towncode = models.CharField(max_length=50, blank=True, null=True)
    townname = models.CharField(max_length=50, blank=True, null=True)
    sectcode = models.CharField(max_length=50, blank=True, null=True)
    sectname = models.CharField(max_length=50, blank=True, null=True)

    name = models.CharField(max_length=50, blank=True, null=True)
    factory_type = models.CharField(
        max_length=3,
        choices=factory_type_list,
        blank=True,
        null=True,
    )
    before_release = models.BooleanField(default=False)  # 從 full-info.csv 匯入的那些都是 True ,使用者新增的通通是 False
    source = models.CharField(
        max_length=1,
        choices=source_list,
        default="U",
    )
    cet_review_status = models.CharField(
        max_length=1,
        choices=cet_review_status_list,
        default="A",
    )  # 地球公民基金會的審閱狀態(舉報前)
    cet_report_status = models.CharField(
        max_length=1,
        choices=cet_report_status_list,
        default="A",
    )  # 地球公民基金會的舉報狀態
    status_time = models.DateTimeField(auto_now_add=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def save(self, *args, **kwargs):
        self.point = Point(self.lng, self.lat, srid=4326)
        self.point.transform(settings.POSTGIS_SRID)
        super(Factory, self).save(*args, **kwargs)
Esempio n. 5
0
class PositionReport(models.Model):
    timestamp_received = models.DateTimeField(null=True, blank=True)
    latitude = models.DecimalField(max_digits=11,
                                   decimal_places=6,
                                   null=True,
                                   blank=True)
    longitude = models.DecimalField(max_digits=11,
                                    decimal_places=6,
                                    null=True,
                                    blank=True)
    heading = models.IntegerField(null=True, blank=True)
    altitude = models.IntegerField(null=True, blank=True)
    speed = models.IntegerField(null=True, blank=True)
    source = models.ForeignKey(PositionReportSource)
    quality = models.IntegerField(
        'The receiver-generated "quality" of this particular report.',
        null=True,
        blank=True)
    satellites_visible = models.IntegerField(
        'The number of satellites visible to the receiver',
        null=True,
        blank=True)
    hdop = models.CharField(max_length=10, null=True, blank=True)
    geoid_height_above_wgs84_meters = models.DecimalField(max_digits=10,
                                                          decimal_places=3,
                                                          null=True,
                                                          blank=True)
    geoidal_seperation_meters = models.IntegerField(null=True, blank=True)
    time_since_dgps_update = models.CharField(max_length=10,
                                              null=True,
                                              blank=True)
    dgps_reference_station_id = models.CharField(max_length=10,
                                                 null=True,
                                                 blank=True)
    receiver_warning = models.NullBooleanField(null=True, blank=True)
    magnetic_variation = models.DecimalField(max_digits=10,
                                             decimal_places=3,
                                             null=True,
                                             blank=True)
    magnetic_variation_direction = models.CharField(max_length=1,
                                                    null=True,
                                                    blank=True)
    checksum_gpgga = models.CharField('$GPGGA Checksum',
                                      max_length=10,
                                      null=True,
                                      blank=True)
    checksum_gprmc = models.CharField('$GPRMC Checksum',
                                      max_length=10,
                                      null=True,
                                      blank=True)
    duration_seconds = models.IntegerField('Duration (seconds)',
                                           null=True,
                                           blank=True)
    user = models.ForeignKey(User)
    timestamp = models.DateTimeField(auto_now_add=True)
    active = models.BooleanField(default=False)
    public = models.BooleanField(default=True)
    should_tumble = models.BooleanField(default=True)
    point = models.PointField(srid=4326, null=True, blank=True)

    objects = models.GeoManager()

    def get_bubble_text(self):
        bubble_text = 'Source: %s<br/>Received %s (Central)' % (
            self.source.name, self.timestamp_received)
        if self.speed != None and self.heading != None:
            bubble_text = '%s<br/>Compass Direction: %s degrees' % (
                bubble_text, self.heading)
        elif self.heading != None:
            bubble_text = '%s<br/>Compass Direction: %s degrees' % (
                bubble_text, self.speed, self.heading)
        if self.source.name == 'InstaMapper iPhone':
            bubble_text = '%s<br/>InstaMapper Available (Free) @ <a href=\'http://www.instamapper.com\'>instamapper.com</a>' % bubble_text
        return bubble_text

    def __unicode__(self):
        return u'Received %s, Lat/%s Lon/%s' % (self.timestamp_received,
                                                self.latitude, self.longitude)
Esempio n. 6
0
class Marvin(models.Model):
    objects = MarvinManager()

    name = models.CharField(_('name'), max_length=100, unique=True)
    hostname = models.CharField(
        _('hostname'),
        max_length=127,
        validators=[
            RegexValidator(URLValidator.host_re,
                           message=_("Please provide a valid host name"))
        ])
    type = models.CharField(_('type'), max_length=50)
    version = ArrayField(models.PositiveSmallIntegerField(),
                         verbose_name=_('version'))

    browser_name = models.CharField(_('browser name'), max_length=150)
    browser_version = ArrayField(models.PositiveSmallIntegerField(),
                                 verbose_name=_('browser version'))

    instance_type = models.CharField(_('instance type'),
                                     max_length=10,
                                     choices=[
                                         ('dual-stack', _('Dual-stack')),
                                         ('v4only', _('IPv4-only')),
                                         ('v6only', _('IPv6-only')),
                                         ('nat64', _('IPv6 with NAT64')),
                                     ])
    addresses = ArrayField(models.GenericIPAddressField(),
                           verbose_name=_('addresses'),
                           default=list)

    first_seen = models.DateTimeField(_('first seen'), auto_now_add=True)
    last_seen = models.DateTimeField(_('last seen'))

    is_alive = models.BooleanField(_('is alive'), default=True)
    parallel_tasks_limit = models.PositiveIntegerField(
        _('parallel tasks limit'))

    class Meta:
        ordering = ('-is_alive', 'instance_type', '-last_seen')

    def __str__(self):
        return _('{name} ({type}: {is_alive})').format(
            name=self.name,
            type=self.instance_type,
            is_alive=self.is_alive and _('alive') or _('dead'))

    def natural_key(self):
        return self.name

    @property
    def cache_key(self):
        return 'marvin_{}'.format(self.name)

    @property
    def tasks(self):
        return cache.get(self.cache_key, 0)

    @tasks.setter
    def tasks(self, value):
        cache.set(self.cache_key, value)

    def __enter__(self):
        key = self.cache_key
        cache.add(key, 0)
        cache.incr(key)

    def __exit__(self, exc_type, exc_val, exc_tb):
        key = self.cache_key
        cache.add(key, 1)
        cache.decr(key)

    def display_version(self):
        return '.'.join(map(str, self.version))

    display_version.short_description = _('version')
    display_version.admin_order_field = 'version'

    def display_browser_version(self):
        return '.'.join(map(str, self.browser_version))

    display_browser_version.short_description = _('browser version')
    display_browser_version.admin_order_field = 'browser_version'

    def last_seen_display(self):
        return naturaltime(self.last_seen)

    last_seen_display.short_description = _('last seen')
    last_seen_display.admin_order_field = 'last_seen'
Esempio n. 7
0
class Tour(models.Model):
    unique_id = models.UUIDField(default=uuid.uuid4,
                                 editable=False,
                                 unique=True)
    user = models.ForeignKey(UserModel, on_delete=models.CASCADE)
    tour_tag = models.ManyToManyField(TourTag)
    name = models.CharField(max_length=100, default='')
    username = models.CharField(max_length=100, null=True)
    description = models.TextField(default='')
    created_at = models.DateTimeField(default=datetime.now, blank=True)
    updated_at = models.DateTimeField(default=datetime.now, blank=True)
    is_published = models.BooleanField(default=True)

    def get_absolute_url(self):
        from django.urls import reverse
        return reverse('tour.tour_detail',
                       kwargs={'unique_id': str(self.unique_id)})

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

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

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

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

    def geometry_sequence(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        geometry = []

        if tour_sequences.count() > 0:
            for t_s in tour_sequences:
                sequence = t_s.sequence
                first_image = sequence.geometry_coordinates_ary[0]
                geometry.append(first_image)

        return geometry

    def get_first_sequence_captured(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        if tour_sequences.count() > 0:
            return tour_sequences[0].sequence.captured_at
        else:
            return ''

    def get_first_sequence_created(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        if tour_sequences.count() > 0:
            return tour_sequences[0].sequence.created_at
        else:
            return ''

    def get_image_count(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        image_count = 0
        if tour_sequences.count() > 0:
            for tour_sequence in tour_sequences:
                sequence = tour_sequence.sequence
                image_count += sequence.get_image_count()

        return image_count

    def get_like_count(self):
        liked_tour = TourLike.objects.filter(tour=self)
        if not liked_tour:
            return 0
        else:
            return liked_tour.count()

    def getSequenceCount(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        return tour_sequences.count()

    def get_distance(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        distance = 0
        if tour_sequences.count() > 0:
            for t_s in tour_sequences:
                distance += float(t_s.sequence.get_distance())

        return "%.3f" % distance

    def get_cover_image(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        if tour_sequences.count() > 0:
            sequence = tour_sequences[0].sequence
            return sequence.get_cover_image()
        else:
            return None

    def get_cover_imageUserOnMapillary(self):
        tour_sequences = TourSequence.objects.filter(tour=self)
        if tour_sequences.count() > 0:
            sequence = tour_sequences[0].sequence
            return sequence.username
        else:
            return ''
Esempio n. 8
0
class Person(models.Model):
    gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
    #  Jards Macalé is an amazing brazilian musician! =]
    enjoy_jards_macale = models.BooleanField(default=True)
    like_metal_music = models.BooleanField(default=False)
    name = models.CharField(max_length=30)
    nickname = models.SlugField(max_length=36)
    age = models.IntegerField()
    bio = models.TextField()
    birthday = models.DateField()
    birth_time = models.TimeField()
    appointment = models.DateTimeField()
    blog = models.URLField()
    occupation = models.CharField(max_length=10, choices=OCCUPATION_CHOICES)
    uuid = models.UUIDField(primary_key=False)
    name_hash = models.BinaryField(max_length=16)
    days_since_last_login = models.BigIntegerField()
    duration_of_sleep = models.DurationField()
    email = models.EmailField()
    id_document = models.CharField(unique=True, max_length=10)

    try:
        from django.db.models import JSONField

        data = JSONField()
    except ImportError:
        # Skip JSONField-related fields
        pass

    try:
        from django.contrib.postgres.fields import ArrayField, HStoreField
        from django.contrib.postgres.fields import JSONField as PostgresJSONField
        from django.contrib.postgres.fields.citext import (
            CICharField,
            CIEmailField,
            CITextField,
        )
        from django.contrib.postgres.fields.ranges import (
            BigIntegerRangeField,
            DateRangeField,
            DateTimeRangeField,
            IntegerRangeField,
        )

        if settings.USING_POSTGRES:
            acquaintances = ArrayField(models.IntegerField())
            postgres_data = PostgresJSONField()
            hstore_data = HStoreField()
            ci_char = CICharField(max_length=30)
            ci_email = CIEmailField()
            ci_text = CITextField()
            int_range = IntegerRangeField()
            bigint_range = BigIntegerRangeField()
            date_range = DateRangeField()
            datetime_range = DateTimeRangeField()
    except ImportError:
        # Skip PostgreSQL-related fields
        pass

    try:
        from django.contrib.postgres.fields.ranges import FloatRangeField

        if settings.USING_POSTGRES:
            float_range = FloatRangeField()
    except ImportError:
        # Django version greater or equal than 3.1
        pass

    try:
        from django.contrib.postgres.fields.ranges import DecimalRangeField

        if settings.USING_POSTGRES:
            decimal_range = DecimalRangeField()
    except ImportError:
        # Django version lower than 2.2
        pass

    if BAKER_GIS:
        geom = models.GeometryField()
        point = models.PointField()
        line_string = models.LineStringField()
        polygon = models.PolygonField()
        multi_point = models.MultiPointField()
        multi_line_string = models.MultiLineStringField()
        multi_polygon = models.MultiPolygonField()
        geom_collection = models.GeometryCollectionField()
Esempio n. 9
0
class OTP(models.Model):
    to = models.CharField(max_length=35, default='')
    otp = models.CharField(max_length=8, default='')
    status = models.CharField(max_length=20, default='')
    time = models.DateTimeField(default='')
Esempio n. 10
0
class Experiment(models.Model):
    description = models.TextField()
    name = models.TextField()
    run_time = models.DateTimeField()
Esempio n. 11
0
class Walk(models.Model):
    """
    A Walk contains the route of the walk and all location data about the start location
    """

    name = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255)
    description = models.TextField()
    start = models.PointField(blank=True, null=True)
    route = models.LineStringField()
    submitter = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))
    route_length = models.FloatField(
        help_text="The length of the walk in miles", default=0
    )
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    # Reverse Geocoding from OpenCage
    reverse_geocode_cache_time = models.DateTimeField(blank=True, null=True)
    # annotations
    what3words = models.CharField(max_length=100, blank=True, null=True)
    geohash = models.CharField(max_length=50, blank=True, null=True)
    # components
    continent = models.CharField(max_length=100, blank=True, null=True)
    country = models.CharField(max_length=100, blank=True, null=True)
    state = models.CharField(max_length=100, blank=True, null=True)
    county = models.CharField(max_length=100, blank=True, null=True)
    city = models.CharField(max_length=100, blank=True, null=True)
    suburb = models.CharField(max_length=100, blank=True, null=True)
    road = models.CharField(max_length=100, blank=True, null=True)
    postcode = models.CharField(max_length=100, blank=True, null=True)
    formatted = models.CharField(max_length=500, blank=True, null=True)

    attributes = models.ManyToManyField(Attribute, blank=True)

    def get_absolute_url(self):
        return reverse(
            "walk-detail",
            kwargs={"username": self.submitter.username, "slug": self.slug},
        )

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        initial_start = self.start
        self.slug = slugify(self.name)
        super().save(*args, **kwargs)

        # Check if the start location has changed
        start_location_changed = False

        if not self.start:
            self.start = self.calculate_walk_start()
            start_location_changed = True

        if self.start != initial_start:
            start_location_changed = True

        # check if the cache_has expired
        if self.reverse_geocode_cache_time is None:
            cache_expired = True
        else:
            cache_expired = self.reverse_geocode_cache_time < (
                now() - timedelta(days=180)
            )

        # update the location details if we need to
        if start_location_changed or cache_expired:
            if settings.OPENCAGE_API_KEY:
                geocoder = OpenCageGeocode(settings.OPENCAGE_API_KEY)

                try:
                    results = geocoder.reverse_geocode(
                        round(self.start[1], 6),
                        round(self.start[0], 6),
                        language="en",
                        limit=1,
                    )

                    if results and len(results):
                        self.what3words = (
                            results[0].get("annotations").get("what3words").get("words")
                        )
                        self.geohash = results[0].get("annotations").get("geohash")
                        self.continent = results[0].get("components").get("continent")
                        self.country = results[0].get("components").get("country")
                        self.state = results[0].get("components").get("state")
                        self.county = results[0].get("components").get("county")
                        self.city = results[0].get("components").get("city")
                        self.suburb = results[0].get("components").get("suburb")
                        self.road = results[0].get("components").get("road")
                        self.postcode = results[0].get("components").get("postcode")
                        self.formatted = results[0].get("formatted")

                        self.reverse_geocode_cache_time = now()
                        super().save(*args, **kwargs)

                except RateLimitExceededError as e:
                    print(e)

        self.route.transform(3857)
        self.route_length = D(m=self.route.length).mi
        super().save(*args, **kwargs)

    def calculate_walk_start(self):
        return Point(srid=self.route.srid, x=self.route[0][0], y=self.route[0][1],)
Esempio n. 12
0
class Audit(models.Model):
    model = models.CharField(max_length=255, null=True, db_index=True)
    model_id = models.IntegerField(null=True, db_index=True)
    instance = models.ForeignKey('Instance',
                                 null=True,
                                 blank=True,
                                 db_index=True)

    field = models.CharField(max_length=255, null=True)
    previous_value = models.TextField(null=True)
    current_value = models.TextField(null=True, db_index=True)

    user = models.ForeignKey('treemap.User')
    action = models.IntegerField()
    """
    These two fields are part of the pending edit system

    If requires_auth is True then this audit record represents
    a change that was *requested* but not applied.

    When an authorized user approves a pending edit
    it creates an audit record on this model with an action
    type of either "PendingApprove" or "PendingReject"

    ref can be set on *any* audit to note that it has been looked
    at and approved or rejected. If this is the case, the ref
    audit will be of type "ReviewApproved" or "ReviewRejected"

    An audit that is "PendingApproved/Rejected" cannot be be
    "ReviewApproved/Rejected"
    """
    requires_auth = models.BooleanField(default=False)
    ref = models.ForeignKey('Audit', null=True)

    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True, db_index=True)

    class Type:
        Insert = 1
        Delete = 2
        Update = 3
        PendingApprove = 4
        PendingReject = 5
        ReviewApprove = 6
        ReviewReject = 7

    TYPES = {
        Type.Insert: trans('Create'),
        Type.Delete: trans('Delete'),
        Type.Update: trans('Update'),
        Type.PendingApprove: trans('Approved Pending Edit'),
        Type.PendingReject: trans('Reject Pending Edit'),
        Type.ReviewReject: trans('Rejected Edit'),
        Type.ReviewApprove: trans('Approved Edit')
    }

    def _deserialize_value(self, value):
        """
        A helper method to transform deserialized audit strings

        When an audit record is written to the audit table, the
        value is stored as a string. When deserializing these values
        for presentation purposes or constructing objects, they
        need to be converted to their correct python value.

        Where possible, django model field classes are used to
        convert the value.
        """

        # some django fields can't handle .to_python(None), but
        # for insert audits (None -> <value>) this method will
        # still be called.
        if value is None:
            return None

        # get the model/field class for each audit record and convert
        # the value to a python object
        cls = get_auditable_class(self.model)
        field_query = cls._meta.get_field_by_name(self.field)
        field_cls, fk_model_cls, is_local, m2m = field_query
        field_modified_value = field_cls.to_python(value)

        # handle edge cases
        if isinstance(field_cls, models.GeometryField):
            field_modified_value = GEOSGeometry(field_modified_value)
        elif isinstance(field_cls, models.ForeignKey):
            field_modified_value = field_cls.rel.to.objects.get(
                pk=field_modified_value)

        return field_modified_value

    def _unit_format(self, value):
        model_name = self.model.lower()

        if isinstance(value, GEOSGeometry):
            if value.geom_type == 'Point':
                return '%d,%d' % (value.x, value.y)
            if value.geom_type in {'MultiPolygon', 'Polygon'}:
                value = value.area

        if is_convertible_or_formattable(model_name, self.field):
            _, value = get_display_value(self.instance, model_name, self.field,
                                         value)
            if value and is_convertible(model_name, self.field):
                units = get_unit_name(
                    get_units(self.instance, model_name, self.field))
                value += (' %s' % units)

        return value

    @property
    def clean_current_value(self):
        if self.field and self.field.startswith('udf:'):
            return self.current_value
        else:
            return self._deserialize_value(self.current_value)

    @property
    def clean_previous_value(self):
        if self.field.startswith('udf:'):
            return self.previous_value
        else:
            return self._deserialize_value(self.previous_value)

    @property
    def current_display_value(self):
        return self._unit_format(self.clean_current_value)

    @property
    def previous_display_value(self):
        return self._unit_format(self.clean_previous_value)

    @property
    def field_display_name(self):
        if not self.field:
            return ''
        name = self.field
        if name.startswith('udf:'):
            return name[4:]
        else:
            return name.replace('_', ' ')

    @property
    def display_action(self):
        return Audit.TYPES[self.action]

    @classmethod
    def audits_for_model(clz, model_name, inst, pk):
        return Audit.objects.filter(model=model_name,
                                    model_id=pk,
                                    instance=inst).order_by('created')

    @classmethod
    def pending_audits(clz):
        return Audit.objects.filter(requires_auth=True)\
                            .filter(ref__isnull=True)\
                            .order_by('created')

    @classmethod
    def audits_for_object(clz, obj):
        return clz.audits_for_model(obj._model_name, obj.instance, obj.pk)

    def short_descr(self):
        cls = get_auditable_class(self.model)
        # If a model has a defined short_descr method, use that
        if hasattr(cls, 'short_descr'):
            return cls.short_descr(self)

        format_string = cls.action_format_string_for_audit(self)

        if hasattr(cls, 'display_name'):
            model_display_name = cls.display_name
        else:
            model_display_name = trans(self.model)

        return format_string % {
            'field': self.field_display_name,
            'model': model_display_name.lower(),
            'value': self.current_display_value
        }

    def dict(self):
        return {
            'model': self.model,
            'model_id': self.model_id,
            'instance_id': self.instance.pk,
            'field': self.field,
            'previous_value': self.previous_value,
            'current_value': self.current_value,
            'user_id': self.user.pk,
            'action': self.action,
            'requires_auth': self.requires_auth,
            'ref': self.ref.pk if self.ref else None,
            'created': str(self.created)
        }

    def __unicode__(self):
        return u"pk=%s - action=%s - %s.%s:(%s) - %s => %s" % \
            (self.pk, self.TYPES[self.action], self.model,
             self.field, self.model_id,
             self.previous_value, self.current_value)

    def is_pending(self):
        return self.requires_auth and not self.ref
Esempio n. 13
0
class CommentFlag(models.Model):
    flagged = models.BooleanField(default=False)
    flagged_date = models.DateTimeField(auto_now=True)

    comment = models.ForeignKey(ThreadedComment, related_name="comment_flags")
    user = models.ForeignKey(User)
Esempio n. 14
0
class Tree(models.Model, ManagementMixin, PendingMixin):
    def __init__(self, *args, **kwargs):
        super(Tree,
              self).__init__(*args,
                             **kwargs)  #save, in order to get ID for the tree

    #owner properties based on wiki/DatabaseQuestions
    plot = models.ForeignKey(Plot)
    tree_owner = models.CharField(max_length=256, null=True, blank=True)
    steward_name = models.CharField(max_length=256, null=True,
                                    blank=True)  #only modifyable by admin
    steward_user = models.ForeignKey(
        User, null=True, blank=True,
        related_name="steward")  #only modifyable by admin
    sponsor = models.CharField(max_length=256, null=True,
                               blank=True)  #only modifyable by us

    species = models.ForeignKey(Species,
                                verbose_name="Scientific name",
                                null=True,
                                blank=True)
    species_other1 = models.CharField(max_length=255, null=True, blank=True)
    species_other2 = models.CharField(max_length=255, null=True, blank=True)
    orig_species = models.CharField(max_length=256, null=True, blank=True)
    dbh = models.FloatField(null=True, blank=True)  #gets auto-set on save
    height = models.FloatField(
        null=True,
        blank=True,
        error_messages={'invalid': "Error: This value must be a number."})
    canopy_height = models.FloatField(
        null=True,
        blank=True,
        error_messages={'invalid': "Error: This value must be a number."})
    date_planted = models.DateField(null=True, blank=True)
    date_removed = models.DateField(null=True, blank=True)
    present = models.BooleanField(default=True)

    last_updated = models.DateTimeField(auto_now=True)
    last_updated_by = models.ForeignKey(
        User, related_name='updated_by')  # TODO set to current user

    s_order = models.IntegerField(null=True, blank=True)
    photo_count = models.IntegerField(null=True, blank=True)

    objects = models.GeoManager()
    history = audit.AuditTrail()
    projects = models.CharField(max_length=20, null=True, blank=True)

    import_event = models.ForeignKey(ImportEvent)

    condition = models.CharField(max_length=256,
                                 null=True,
                                 blank=True,
                                 choices=settings.CHOICES["conditions"])
    canopy_condition = models.CharField(
        max_length=256,
        null=True,
        blank=True,
        choices=settings.CHOICES["canopy_conditions"])

    readonly = models.BooleanField(default=False)
    url = models.URLField(max_length=255, null=True, blank=True)
    pests = models.CharField(max_length=256,
                             null=True,
                             blank=True,
                             choices=settings.CHOICES["pests"])

    def has_common_attributes(self):
        if self.get_flag_count > 0:
            return True
        if self.species:
            spp = self.species
            if spp.flower_conspicuous or spp.fall_conspicuous or spp.palatable_human or spp.native_status:
                return True
        return False

    def get_absolute_url(self):
        return "/trees/%i/" % self.id

    def get_display(self, choices, val):
        for key, value in settings.CHOICES[choices]:
            if key == val:
                return value
        return None

    def get_condition_display(self):
        return self.get_display("conditions", self.condition)

    def get_canopy_condition_display(self):
        return self.get_display("canopy_condition", self.canopy_condition)

    def get_pests_display(self):
        return self.get_display("pests", self.pests)

    def get_scientific_name(self):
        if self.species:
            sn = self.species.scientific_name
            if not sn:
                sn = self.species.genus
            if self.species.cultivar_name:
                sn += " '%s'" % self.species.cultivar_name
            return sn
        else:
            return 'unavailable'

    def get_common_name(self):
        if self.species:
            return self.species.common_name
        return 'unavailable'

    def get_eco_impact(self):
        tr = TreeResource.objects.filter(tree=self)
        if tr:
            return "%0.2f" % tr[0].total_benefit()

    def get_action_count(self):
        return len(self.treeaction_set.all())

    def get_alert_count(self):
        return len(self.treealert_set.all())

    def get_flag_count(self):
        return len(self.treeflags_set.all())

    def get_stewardship_count(self):
        return len(self.treestewardship_set.all())

    def get_active_pends(self):
        pends = self.treepending_set.filter(status='pending')
        return pends

    def is_complete(self):
        if self.species >= 0 and self.dbh:
            return True
        else:
            return False

    def set_species(self, species_id, commit=True):
        """
        sets the species, and updates the species tree count
        """
        self.old_species = self.species
        new_species = Species.objects.get(id=species_id)
        self.species = new_species
        if commit:
            self.save()

    def save(self, *args, **kwargs):
        #save new neighborhood/zip connections if needed
        self.photo_count = self.treephoto_set.count()

        self.projects = ""
        for fl in self.treeflags_set.all():
            self.projects = self.projects + " " + fl.key

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

        self.quick_save(*args, **kwargs)

    def quick_save(self, *args, **kwargs):
        super(Tree, self).save(*args, **kwargs)
        set_environmental_summaries(self)
        #set new species counts
        if hasattr(self, 'old_species') and self.old_species:
            self.old_species.save()
        if hasattr(self, 'species') and self.species:
            self.species.save()

        self.plot.last_updated = self.last_updated
        self.plot.save()

    def update_aggregate(self, ag_model, location):
        agg = ag_model.objects.filter(location=location)
        if agg:
            agg = agg[0]
        else:
            agg = ag_model(location=location)
        #print agg.__dict__
        #summaries = []
        trees = Tree.objects.filter(plot__geometry__within=location.geometry)
        plots = Plot.objects.filter(geometry__within=location.geometry)
        #print trees
        agg.total_trees = trees.count()
        agg.total_plots = plots.count()

        trees = trees.exclude(Q(dbh=None) | Q(dbh=0.0)).exclude(species=None)
        #print agg.total_trees
        #TODO figure out how to summarize diff stratum stuff
        field_names = [
            x.name for x in ResourceSummaryModel._meta.fields
            if not x.name == 'id'
        ]

        if agg.total_trees == 0:
            for f in field_names:
                setattr(agg, f, 0.0)
        else:
            #TODO speed this up
            for f in field_names:
                fn = 'treeresource__' + f
                s = trees.aggregate(Sum(fn))[fn + '__sum'] or 0.0
                setattr(agg, f, s)
        agg.save()

    def percent_complete(self):
        has = 0
        attr = settings.COMPLETE_ARRAY
        for item in attr:
            if hasattr(self, item):
                if getattr(self, item):
                    has += 1
            elif hasattr(self.plot, item):
                if getattr(self.plot, item):
                    has += 1
        return has / float(len(attr)) * 100

    def validate_all(self):
        #print watch_tests
        for test, method in watch_tests.iteritems():
            #print test
            result = getattr(self, method)()
            #print result

            # check for results and save - passed tests return None
            if not result:
                TreeWatch.objects.filter(tree=self,
                                         key=watch_choices[test]).delete()
                continue
            # if identical watch already exists skip it
            if TreeWatch.objects.filter(tree=self,
                                        key=watch_choices[test],
                                        value=result):
                continue

            TreeWatch.objects.filter(tree=self,
                                     key=watch_choices[test]).delete()
            self.treewatch_set.create(
                key=watch_choices[test],
                value=result,
            )

    def validate_proximity(self, return_trees=False, max_count=1):
        if not self.plot.geometry:
            return None
        return self.plot.validate_proximity()

    # Disallowed combinations:
    #   Dead + 0% loss, Dead + 25% loss, Dead + 50% loss, Dead + 75% loss
    #   Excellent + 100% loss, Excellent + 75% loss
    def validate_canopy_condition(self):
        if not self.canopy_condition or not self.condition:
            return None

        cond = self.condition
        c_cond = self.canopy_condition
        if cond == 'Dead':
            if not c_cond == 'Little or None (up to 100% missing)' and not c_cond == 'None':
                return cond + ", " + c_cond

        elif cond == 'Excellent':
            if c_cond == 'Little or None (up to 100% missing)' or c_cond == 'Large Gaps (up to 75% missing)':
                return cond + ", " + c_cond

        return None

    # discussions: http://www.nativetreesociety.org/measure/tdi/diameter_height_ratio.htm
    def validate_height_dbh(self):
        if not self.height or not self.dbh:
            return None
        getcontext().prec = 3
        cbh = self.dbh * math.pi
        cbh_feet = cbh * .75 / 9
        float_ratio = self.height / cbh_feet
        hd_ratio = Decimal(float_ratio.__str__())
        #print hd_ratio
        if hd_ratio < 100:
            return None
        return round(hd_ratio, 2).__str__()

    def validate_max_dbh(self):
        if not self.dbh or not self.species or not self.species.v_max_dbh:
            return None
        if self.dbh > self.species.v_max_dbh:
            return "%s (species max: %s )" % (str(
                self.dbh), str(self.species.v_max_dbh))
        return None

    def validate_max_height(self):
        if not self.height or not self.species or not self.species.v_max_height:
            return None
        if self.height > self.species.v_max_height:
            return "%s (species max: %s)" % (str(
                self.height), str(self.species.v_max_height))
        return None

    def remove(self):
        """
        Mark the tree and its associated objects as not present.
        """
        self.present = False
        self.save()
        for audit_trail_record in self.history.all():
            audit_trail_record.present = False
            audit_trail_record.save()

    def __unicode__(self):
        if self.species:
            return u'%s, %s, %s' % (self.species.common_name
                                    or '', self.species.scientific_name,
                                    self.plot.geocoded_address)
        else:
            return self.plot.geocoded_address
Esempio n. 15
0
class Event(models.Model):
    """
    Meeting or hearing associated with a proposal.
    """
    title = models.CharField(max_length=256, db_index=True)
    created = models.DateTimeField(auto_now_add=True)
    date = models.DateTimeField(db_index=True)
    duration = models.DurationField(null=True)
    location = models.CharField(
        max_length=256, default="Somerville City Hall, 93 Highland Ave")
    region_name = models.CharField(max_length=128, default="Somerville, MA")
    description = models.TextField()
    proposals = models.ManyToManyField(Proposal,
                                       related_name="events",
                                       related_query_name="event")
    minutes = models.URLField(blank=True)

    objects = EventManager()

    class Meta:
        unique_together = (("date", "title", "region_name"))

    def to_json_dict(self):
        d = model_to_dict(self, exclude=["created", "proposals"])
        return d

    @classmethod
    def make_event(cls, event_json):
        """
        event_json should have the following fields:
        - title (str) - Name of the event
        - description (str)
        - date (datetime with local timezone) - When will the event occur?
        - cases - A list of case numbers
        - region_name
        - duration (timedelta, optional) - how long will the event last?
        - agenda_url (string, optional)
        """
        keys = ("title", "description", "date", "region_name", "duration")
        try:
            event = cls.objects.get(title=event_json["title"],
                                    date=event_json["date"],
                                    region_name=event_json["region_name"])
            for k in keys:
                setattr(event, k, event_json.get(k))
            event.minutes = event_json.get("agenda_url")
        except cls.DoesNotExist:
            kwargs = {k: event_json.get(k) for k in keys}
            kwargs["minutes"] = event_json.get("agenda_url", "")
            event = cls(**kwargs)

        event.save()

        for case_number in event_json["cases"]:
            try:
                proposal = Proposal.objects.get(case_number=case_number)
                event.proposals.add(proposal)
            except Proposal.DoesNotExist:
                continue

        return event
Esempio n. 16
0
class Map(NamedModel):
    """
    A single thematical map.
    """
    ANONYMOUS = 1
    EDITORS = 2
    OWNER = 3
    PUBLIC = 1
    OPEN = 2
    PRIVATE = 3
    EDIT_STATUS = (
        (ANONYMOUS, _('Everyone can edit')),
        (EDITORS, _('Only editors can edit')),
        (OWNER, _('Only owner can edit')),
    )
    SHARE_STATUS = (
        (PUBLIC, _('everyone (public)')),
        (OPEN, _('anyone with link')),
        (PRIVATE, _('editors only')),
    )
    slug = models.SlugField(db_index=True)
    description = models.TextField(blank=True,
                                   null=True,
                                   verbose_name=_("description"))
    center = models.PointField(geography=True, verbose_name=_("center"))
    zoom = models.IntegerField(default=7, verbose_name=_("zoom"))
    locate = models.BooleanField(default=False,
                                 verbose_name=_("locate"),
                                 help_text=_("Locate user on load?"))
    licence = models.ForeignKey(Licence,
                                help_text=_("Choose the map licence."),
                                verbose_name=_('licence'),
                                on_delete=models.SET_DEFAULT,
                                default=get_default_licence)
    modified_at = models.DateTimeField(auto_now=True)
    tilelayer = models.ForeignKey(TileLayer,
                                  blank=True,
                                  null=True,
                                  related_name="maps",
                                  verbose_name=_("background"))
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              blank=True,
                              null=True,
                              related_name="owned_maps",
                              verbose_name=_("owner"))
    editors = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                     blank=True,
                                     verbose_name=_("editors"))
    edit_status = models.SmallIntegerField(choices=EDIT_STATUS,
                                           default=OWNER,
                                           verbose_name=_("edit status"))
    share_status = models.SmallIntegerField(choices=SHARE_STATUS,
                                            default=PUBLIC,
                                            verbose_name=_("share status"))
    settings = DictField(blank=True, null=True, verbose_name=_("settings"))

    objects = models.GeoManager()
    public = PublicManager()

    def get_absolute_url(self):
        return reverse("map",
                       kwargs={
                           'slug': self.slug or "map",
                           'pk': self.pk
                       })

    def get_anonymous_edit_url(self):
        signer = Signer()
        signature = signer.sign(self.pk)
        return reverse('map_anonymous_edit_url',
                       kwargs={'signature': signature})

    def is_anonymous_owner(self, request):
        if self.owner:
            # edit cookies are only valid while map hasn't owner
            return False
        key, value = self.signed_cookie_elements
        try:
            has_anonymous_cookie = int(request.get_signed_cookie(
                key, False)) == value
        except ValueError:
            has_anonymous_cookie = False
        return has_anonymous_cookie

    def can_edit(self, user=None, request=None):
        """
        Define if a user can edit or not the instance, according to his account
        or the request.
        """
        can = False
        if request and not self.owner:
            if (getattr(settings, "LEAFLET_STORAGE_ALLOW_ANONYMOUS", False)
                    and self.is_anonymous_owner(request)):
                can = True
                if user and user.is_authenticated():
                    # if user is authenticated, attach as owner
                    self.owner = user
                    self.save()
                    msg = _(
                        "Your anonymous map has been attached to your account %s"
                        % user)
                    messages.info(request, msg)
        if self.edit_status == self.ANONYMOUS:
            can = True
        elif not user.is_authenticated():
            pass
        elif user == self.owner:
            can = True
        elif self.edit_status == self.EDITORS and user in self.editors.all():
            can = True
        return can

    def can_view(self, request):
        if self.owner is None:
            can = True
        elif self.share_status in [self.PUBLIC, self.OPEN]:
            can = True
        elif request.user == self.owner:
            can = True
        else:
            can = not (self.share_status == self.PRIVATE
                       and request.user not in self.editors.all())
        return can

    @property
    def signed_cookie_elements(self):
        return ('anonymous_owner|%s' % self.pk, self.pk)

    def get_tilelayer(self):
        return self.tilelayer or TileLayer.get_default()

    def clone(self, **kwargs):
        new = self.__class__.objects.get(pk=self.pk)
        new.pk = None
        new.name = u"%s %s" % (_("Clone of"), self.name)
        if "owner" in kwargs:
            # can be None in case of anonymous cloning
            new.owner = kwargs["owner"]
        new.save()
        for editor in self.editors.all():
            new.editors.add(editor)
        for datalayer in self.datalayer_set.all():
            datalayer.clone(map_inst=new)
        return new
Esempio n. 17
0
class Proposal(models.Model):
    case_number = models.CharField(max_length=64,
                                   unique=True,
                                   help_text=("The unique case number "
                                              "assigned by the city"))
    address = models.CharField(max_length=128, help_text="Street address")
    other_addresses = models.CharField(
        max_length=250,
        blank=True,
        help_text="Other addresses covered by this proposal")
    location = models.PointField(help_text="The latitude and longitude")
    region_name = models.CharField(max_length=128,
                                   default="Somerville, MA",
                                   null=True,
                                   help_text="")
    # The time when the proposal was last saved:
    modified = models.DateTimeField(auto_now=True)
    # The last time that the source was changed:
    updated = models.DateTimeField()
    created = models.DateTimeField(auto_now_add=True)
    summary = models.CharField(max_length=1024, default="")
    description = models.TextField(default="")
    source = models.URLField(null=True,
                             help_text="The data source for the proposal.")
    status = models.CharField(max_length=64)

    # A proposal can be associated with a Project:
    project = models.ForeignKey("project.Project", blank=True, null=True)
    # A misnomer; if True, indicates that the planning board has issued a
    # ruling (approval or disapproval):
    complete = models.BooleanField(default=False)

    parcel = models.ForeignKey("parcel.Parcel",
                               related_name="proposals",
                               null=True,
                               on_delete=models.SET_NULL)

    objects = ProposalManager()

    def get_absolute_url(self):
        return reverse("view-proposal", kwargs={"pk": self.pk})

    def document_for_field(self, field):
        return self.document_set.filter(field=field)

    @classmethod
    def create_or_update_proposal_from_dict(kls, p_dict):
        """
        Constructs a Proposal from a dictionary.  If an existing proposal has a
        matching case number, update it from p_dict."""
        try:
            proposal = kls.objects.get(case_number=p_dict["case_number"])
            created = False
        except kls.DoesNotExist:
            proposal = kls(case_number=p_dict["case_number"])
            created = True

        changed = not created
        if changed:
            prop_changes = []

        for p, fn in property_map:
            old_val = changed and getattr(proposal, p)
            try:
                val = fn(p_dict)
                if changed and val != old_val:
                    prop_changes.append({
                        "name": p,
                        "new": val,
                        "old": old_val
                    })
                setattr(proposal, p, val)
            except Exception as exc:
                if old_val:
                    continue
                raise Exception("Missing required property: %s\n Reason: %s" %
                                (p, exc))

        proposal.save()

        proposal.create_events(p_dict.get("events"))

        # Create associated documents:
        proposal.create_documents(
            (field, val["links"]) for field, val in p_dict.items()
            if isinstance(val, dict) and val.get("links"))

        if changed:
            attr_changes = []
        for attr_name, attr_val in p_dict.get("attributes", []):
            try:
                handle = utils.normalize(attr_name)
                attr = proposal.attributes.get(handle=handle)
                old_val = attr.text_value
            except Attribute.DoesNotExist:
                proposal.attributes.create(name=attr_name,
                                           handle=handle,
                                           text_value=attr_val,
                                           published=p_dict["updated_date"])
                old_val = None
            if changed:
                attr_changes.append({
                    "name": attr_name,
                    "old": old_val,
                    "new": attr_val
                })

        if changed:
            changeset = Changeset.from_changes(proposal, {
                "properties": prop_changes,
                "attributes": attr_changes
            })
            changeset.save()

        return (created, proposal)

    def create_documents(self, docs):
        for field, links in docs:
            for link in links:
                try:
                    doc = proposal.document_set.get(url=link["url"])
                except Document.DoesNotExist:
                    doc = Document(proposal=proposal)

                    doc.url = link["url"]
                    doc.title = link["title"]
                    doc.field = field
                    doc.published = p_dict["updated_date"]

                    doc.save()

    def create_events(self, events_json):
        if events_json:
            for event_json in events_json:
                event_json["cases"] = [self.case_number]
                Event.make_event(event_json)
Esempio n. 18
0
class Comment(models.Model):
    created_at = models.DateTimeField(verbose_name=_('Time of creation'),
                                      auto_now_add=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                   verbose_name=_('Created by'),
                                   null=True,
                                   blank=True,
                                   related_name='%(class)s_created',
                                   on_delete=models.PROTECT)
    text = models.TextField(verbose_name=_('Text'))
    content_type = models.ForeignKey(ContentType,
                                     on_delete=models.CASCADE,
                                     limit_choices_to=get_content_type_choices)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    objects = CommentQuerySet.as_manager()

    class Meta:
        ordering = ('id', )
        verbose_name = _('Comment')
        verbose_name_plural = _('Comments')
        index_together = (('content_type', 'object_id'), )

    def __str__(self):
        author = self.created_by.get_display_name(
        ) if self.created_by else 'Unknown author'
        text = self.text if len(self.text) < 40 else self.text[:37] + '...'
        return '%s %s %s: %s' % (self.content_type.model, self.object_id,
                                 author, text)

    @staticmethod
    def can_user_comment_object(user, target_object):
        if not (user and user.is_authenticated):
            return False

        target_model = target_object.__class__
        if target_model not in COMMENTABLE_MODELS.values():
            return False

        if target_model == Reservation:
            if user == target_object.user:
                return True
            if target_object.resource.can_access_reservation_comments(user):
                return True
        elif target_model == CateringOrder:
            if user == target_object.reservation.user:
                return True
            if target_object.reservation.resource.can_view_reservation_catering_orders(
                    user):
                return True
        return False

    def get_notification_context(self, language_code):
        target_object = self.content_object
        target_model = target_object.__class__
        assert target_model in COMMENTABLE_MODELS.values()

        target_type = next(api_name
                           for api_name, model in COMMENTABLE_MODELS.items()
                           if model == target_model)
        context = dict(text=self.text,
                       target_type=target_type,
                       created_at=self.created_at)
        if self.created_by:
            context['created_by'] = dict(
                display_name=self.created_by.get_display_name())
        else:
            context['created_by'] = None

        if target_model == Reservation:
            context['reservation'] = target_object.get_notification_context(
                language_code)
            tz = target_object.resource.unit.get_tz()
        elif target_model == CateringOrder:
            context['catering_order'] = target_object.get_notification_context(
                language_code)
            tz = target_object.reservation.resource.unit.get_tz()

        # Use local timezones by default
        context['created_at'] = context['created_at'].astimezone(tz)

        return context

    def _send_notification(self, request=None):
        target_object = self.content_object
        target_model = target_object.__class__
        assert target_model in COMMENTABLE_MODELS.values()

        if target_model == CateringOrder:
            catering_provider = target_object.get_provider()
            email = catering_provider.notification_email if catering_provider else None
            reserver = target_object.reservation.user
            notification_type = NotificationType.CATERING_ORDER_COMMENT_CREATED
        elif target_model == Reservation:
            unit = target_object.resource.unit
            email = unit.manager_email if unit.manager_email else None
            reserver = target_object.user
            notification_type = NotificationType.RESERVATION_COMMENT_CREATED

        context = self.get_notification_context(DEFAULT_LANG)
        try:
            rendered_notification = render_notification_template(
                notification_type, context, DEFAULT_LANG)
        except NotificationTemplateException as e:
            logger.error(e, exc_info=True, extra={'request': request})
            return

        if email:
            send_respa_mail(email, rendered_notification['subject'],
                            rendered_notification['body'],
                            rendered_notification['html_body'])
        if self.created_by != reserver and reserver.email:
            send_respa_mail(reserver.email, rendered_notification['subject'],
                            rendered_notification['body'],
                            rendered_notification['html_body'])

    def send_created_notification(self, request=None):
        self._send_notification(request)
class DatedModel(models.Model):
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True
Esempio n. 20
0
class Lot(models.Model):
    # spatial fields
    coord = models.PointField(srid=4326, blank=True)
    bounds = models.MultiPolygonField(srid=4326, blank=True)
    #area = models.FloatField(db_index=True, blank=True)

    address = models.CharField(db_index=True, max_length=255, blank=True)
    city = models.CharField(max_length=255)
    state = models.CharField(max_length=2)
    country = models.CharField(max_length=255)
    code = models.CharField(max_length=10, blank=True)

    # meta fields
    is_visible = models.BooleanField(db_index=True, default=True)
    is_vacant = models.BooleanField(db_index=True, default=False)
    is_public = models.BooleanField(db_index=True, default=False)

    #auto-generated fields
    slug = models.SlugField(max_length=255, editable=False)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    comments = generic.GenericRelation(Comment)

    objects = PassThroughGeoManager.for_queryset_class(LotQuerySet)()

    class Meta:
        pass

    @property
    def activity_count(self):
        activity = self.comments.count() + self.ideas.count()
        return activity

    @property
    def has_project(self):
        if self.ideas:
            return True
        else:
            return False

    @property
    def has_comment(self):
        if self.comments:
            return True
        else:
            return False

    def get_sqft(self):
        """ 
        Returns the area in sq ft. 
        """
        # Convert our geographic polygons (in WGS84)
        # into a local projection for New York (here EPSG:32118)
        try:
            return self.bounds.transform(102729, clone=True).area
        except Exception:
            return None

    def get_acres(self):
        """ 
        Returns the area in sq ft. 
        """
        # Convert our geographic polygons (in WGS84)
        # into a local projection for New York (here EPSG:32118)
        try:
            return self.bounds.transform(102729,
                                         clone=True).area * 0.00002295684
        except Exception:
            return None

    def __unicode__(self):
        if self.address:
            return u'%s' % (self.address)
        elif self.coord:
            return u'(%s,%s)' % (self.coord.x, self.coord.y)
        else:
            return u'No Address %s' % (self.pk)

    def get_absolute_url(self):
        return reverse('lotxlot_lot_detail', args=[str(self.id)])

    def save(self, *args, **kwargs):
        if not self.pk:
            if self.address:
                slug = '%s %s, %s' % (self.address, self.city, self.state)
            elif self.coord:
                slug = '(%s,%s) %s, %s' % (self.coord.x, self.coord.y,
                                           self.city, self.state)
            else:
                slug = 'No Address %s %s, %s' % (self.pk, self.city,
                                                 self.state)
            self.slug = slugify(slug)
        super(Lot, self).save(*args, **kwargs)
Esempio n. 21
0
class SiteVisit(AbstractAdditionalData):
    """Site visit model."""
    EMBEDDEDNESS_CHOICES = ((EMBEDDEDNESS_1, '0-25'), (EMBEDDEDNESS_2,
                                                       '26-50'),
                            (EMBEDDEDNESS_3, '51-75'), (EMBEDDEDNESS_4,
                                                        '76-100'))

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

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

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

    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        help_text='Creator/owner of this data from the web',
        null=True,
        blank=True,
        related_name='%(class)s_owner',
        on_delete=models.SET_NULL)

    collector = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        help_text='Actual capturer/collector of this data',
        null=True,
        blank=True,
        related_name='%(class)s_data_collector',
        on_delete=models.SET_NULL)

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

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

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

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

    embeddedness = models.CharField(max_length=50,
                                    choices=EMBEDDEDNESS_CHOICES,
                                    blank=True,
                                    default='')

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

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

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

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

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

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

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

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

    def __unicode__(self):
        return self.location_site.name
Esempio n. 22
0
class Unit(models.Model):
    id = models.IntegerField(primary_key=True)
    data_source_url = models.URLField(null=True)
    name = models.CharField(max_length=200, db_index=True)
    description = models.TextField(null=True)

    provider_type = models.IntegerField()

    location = models.PointField(null=True, srid=PROJECTION_SRID)
    geometry = models.GeometryField(srid=PROJECTION_SRID, null=True)
    department = models.ForeignKey(Department, null=True)
    organization = models.ForeignKey(Organization)

    street_address = models.CharField(max_length=100, null=True)
    address_zip = models.CharField(max_length=10, null=True)
    phone = models.CharField(max_length=50, null=True)
    email = models.EmailField(max_length=100, null=True)
    www_url = models.URLField(max_length=400, null=True)
    address_postal_full = models.CharField(max_length=100, null=True)
    municipality = models.ForeignKey(Municipality, null=True, db_index=True)

    data_source = models.CharField(max_length=20, null=True)
    extensions = HStoreField(null=True)

    picture_url = models.URLField(max_length=250, null=True)
    picture_caption = models.CharField(max_length=200, null=True)

    origin_last_modified_time = models.DateTimeField(
        db_index=True, help_text='Time of last modification')

    services = models.ManyToManyField(Service, related_name='units')
    divisions = models.ManyToManyField(AdministrativeDivision)
    keywords = models.ManyToManyField(Keyword)

    connection_hash = models.CharField(
        max_length=40,
        null=True,
        help_text='Automatically generated hash of connection info')
    accessibility_property_hash = models.CharField(
        max_length=40,
        null=True,
        help_text='Automatically generated hash of accessibility property info'
    )
    identifier_hash = models.CharField(
        max_length=40,
        null=True,
        help_text='Automatically generated hash of other identifiers')

    # Cached fields for better performance
    root_services = models.CommaSeparatedIntegerField(max_length=50, null=True)

    objects = models.GeoManager()
    search_objects = UnitSearchManager()

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

    def get_root_services(self):
        tree_ids = self.services.all().values_list('tree_id',
                                                   flat=True).distinct()
        qs = Service.objects.filter(level=0).filter(tree_id__in=list(tree_ids))
        srv_list = qs.values_list('id', flat=True).distinct()
        return sorted(srv_list)
Esempio n. 23
0
class Event(CommonAbstractModel):
    STATUS_CHOICES = (
        ('ACTIVE', _('Active')),
        ('INACTIVE', _('Inactive')),
    )

    disaster = models.ForeignKey(Disaster, verbose_name=verbose_disaster)
    point = models.PointField(
        null=True,
        blank=True,
        verbose_name=verbose_point
    )
    created = models.DateTimeField(
        default=timezone.now,
        verbose_name=verbose_created
    )
    updated = models.DateTimeField(auto_now=True, verbose_name=verbose_updated)
    closed = models.DateTimeField(
        null=True,
        blank=True,
        verbose_name=verbose_closed
    )
    status = models.CharField(
        max_length=50,
        choices=STATUS_CHOICES,
        default='ACTIVE',
        verbose_name=verbose_status
    )
    note = models.TextField(blank=True, verbose_name=verbose_note)
    height = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=verbose_height,
        help_text=_('Unit in cm')
    )
    height_min = models.PositiveIntegerField(
        null=True,
        blank=True,
        verbose_name=verbose_height_min,
        help_text=_('Unit in cm')
    )
    magnitude = models.DecimalField(
        null=True,
        blank=True,
        max_digits=4,
        decimal_places=2,
        validators=[MinValueValidator(Decimal('0.01'))],
        verbose_name=verbose_magnitude,
        help_text=_('Unit in Scala Richter')
    )
    depth = models.DecimalField(
        null=True,
        blank=True,
        max_digits=4,
        decimal_places=2,
        validators=[MinValueValidator(Decimal('0.01'))],
        verbose_name=verbose_depth,
        help_text=_('Unit in km')
    )
    province = models.ForeignKey(
        Province,
        null=True,
        blank=True,
        verbose_name=verbose_province
    )
    city = models.ForeignKey(
        City,
        null=True,
        blank=True,
        verbose_name=verbose_city
    )
    subdistrict = models.ForeignKey(
        Subdistrict,
        null=True,
        blank=True,
        verbose_name=verbose_subdistrict
    )
    village = models.ForeignKey(
        Village,
        null=True,
        blank=True,
        verbose_name=verbose_village
    )
    rw = models.ForeignKey(RW, null=True, blank=True, verbose_name=verbose_rw)
    rt = models.ForeignKey(RT, null=True, blank=True, verbose_name=verbose_rt)

    class Meta:
        ordering = ['-pk']
        get_latest_by = 'pk'
        verbose_name_plural = _('Events')

    def get_absolute_url(self):
        return reverse('reports:event_detail', args=[self.pk])

    def __unicode__(self):
        return '[%s] - %s' % (self.disaster, timezone.localtime(self.created).strftime('%Y-%m-%d %H:%M:%S %Z'))
class HowHearAboutUsItem(models.Model):
    '''
    METADATA
    '''
    class Meta:
        app_label = 'tenant_foundation'
        db_table = 'nwapp_how_did_you_hear_about_us_items'
        verbose_name = _('How Hear About Us Item')
        verbose_name_plural = _('How Hear About Us Items')
        default_permissions = ()
        permissions = ()

    '''
    MODEL FIELDS
    '''

    objects = HowHearAboutUsItemManager()
    """
    MODEL FUNCTIONS
    """

    text = models.CharField(_("Text"),
                            max_length=127,
                            help_text=_('The text content of this item.'),
                            db_index=True,
                            unique=True)
    sort_number = models.PositiveSmallIntegerField(
        _("Sort #"),
        help_text=_('The number this item will appear when sorted by number.'),
        blank=True,
        default=0,
        db_index=True,
    )
    is_for_associate = models.BooleanField(
        _("Is for associate"),
        help_text=_(
            'Indicates this option will be visible for the associate.'),
        default=True,
        blank=True)
    is_for_customer = models.BooleanField(
        _("Is for customer"),
        help_text=_('Indicates this option will be visible for the customer.'),
        default=True,
        blank=True)
    is_for_staff = models.BooleanField(
        _("Is for staff"),
        help_text=_('Indicates this option will be visible for the staff.'),
        default=True,
        blank=True)
    is_for_partner = models.BooleanField(
        _("Is for partner"),
        help_text=_('Indicates this option will be visible for the partner.'),
        default=True,
        blank=True)
    is_archived = models.BooleanField(
        _("Is Archived"),
        help_text=_('Indicates whether how hear item was archived.'),
        default=False,
        blank=True,
        db_index=True)

    # AUDITING

    uuid = models.CharField(
        _("UUID"),
        help_text=
        _('The unique identifier we want to release to the public to identify this unique record.'
          ),
        default=uuid.uuid4,
        editable=False,
        max_length=63,  # Do not change
        unique=True,
        db_index=True,
    )
    slug = models.SlugField(
        _("Slug"),
        help_text=_('The unique identifier used externally.'),
        max_length=255,
        null=False,
        unique=True,
        db_index=True,
    )
    created_at = models.DateTimeField(auto_now_add=True, db_index=True)
    created_by = models.ForeignKey(
        SharedUser,
        help_text=_('The user whom created this score point.'),
        related_name="created_how_did_you_hear_about_us_items",
        on_delete=models.SET_NULL,
        blank=True,
        null=True)
    created_from = models.GenericIPAddressField(
        _("Created from"),
        help_text=_('The IP address of the creator.'),
        blank=True,
        null=True)
    created_from_is_public = models.BooleanField(
        _("Is the IP "),
        help_text=_('Is creator a public IP and is routable.'),
        default=False,
        blank=True)
    created_from_position = models.PointField(
        _("Created from position"),
        help_text=_('The latitude and longitude coordinates for the creator.'),
        srid=4326,
        geography=True,
        null=True,
        blank=True,
    )
    last_modified_at = models.DateTimeField(auto_now=True)
    last_modified_by = models.ForeignKey(
        SharedUser,
        help_text=_('The user whom last modified this private image upload.'),
        related_name="last_modified_how_did_you_hear_about_us_items",
        on_delete=models.SET_NULL,
        blank=True,
        null=True)
    last_modified_from = models.GenericIPAddressField(
        _("Last modified from"),
        help_text=_('The IP address of the modifier.'),
        blank=True,
        null=True)
    last_modified_from_is_public = models.BooleanField(
        _("Is the IP "),
        help_text=_('Is modifier a public IP and is routable.'),
        default=False,
        blank=True)
    last_modified_from_position = models.PointField(
        _("Last modified from position"),
        help_text=_(
            'The latitude and longitude coordinates for the last modified user.'
        ),
        srid=4326,
        geography=True,
        null=True,
        blank=True,
    )
    """
    MODEL FUNCTIONS
    """

    def __str__(self):
        return str(self.text)

    @transaction.atomic
    def save(self, *args, **kwargs):
        '''
        Override the `save` function to support extra functionality of our model.
        '''
        '''
        If we are creating a new row, then we will automatically increment the
        `id` field instead of relying on Postgres DB.
        '''
        if self.id == None:
            try:
                latest_obj = HowHearAboutUsItem.objects.latest('id')
                self.id = latest_obj.id + 1
            except HowHearAboutUsItem.DoesNotExist:
                self.id = 1
        '''
        If we are creating a new model, then we will automatically increment the `id`.
        '''
        # The following code will generate a unique slug and if the slug
        # is not unique in the database, then continue to try generating
        # a unique slug until it is found.
        if self.slug == "" or self.slug == None:
            slug = slugify(self.text)
            while HowHearAboutUsItem.objects.filter(slug=slug).exists():
                slug = slugify(self.text) + "-" + get_referral_code(16)
            self.slug = slug
        '''
        Finally call the parent function which handles saving so we can carry
        out the saving operation by Django in our ORM.
        '''
        super(HowHearAboutUsItem, self).save(*args, **kwargs)
Esempio n. 25
0
class Event(SimpleModel):
    name = models.CharField(max_length=100)
    when = models.DateTimeField()
Esempio n. 26
0
class Instance(models.Model, InstanceBaseClass):
    """
    Model representing a single submission to an XForm
    """

    json = JSONField(default=dict, null=False)
    xml = models.TextField()
    user = models.ForeignKey(
        User, related_name='instances', null=True, on_delete=models.SET_NULL)
    xform = models.ForeignKey(
        'logger.XForm', null=False, related_name='instances',
        on_delete=models.CASCADE)
    survey_type = models.ForeignKey(
        'logger.SurveyType', on_delete=models.PROTECT)

    # shows when we first received this instance
    date_created = models.DateTimeField(auto_now_add=True)

    # this will end up representing "date last parsed"
    date_modified = models.DateTimeField(auto_now=True)

    # this will end up representing "date instance was deleted"
    deleted_at = models.DateTimeField(null=True, default=None)
    deleted_by = models.ForeignKey(User, related_name='deleted_instances',
                                   null=True, on_delete=models.SET_NULL)

    # this will be edited when we need to create a new InstanceHistory object
    last_edited = models.DateTimeField(null=True, default=None)

    # ODK keeps track of three statuses for an instance:
    # incomplete, submitted, complete
    # we add a fourth status: submitted_via_web
    status = models.CharField(max_length=20,
                              default=u'submitted_via_web')
    uuid = models.CharField(max_length=249, default=u'', db_index=True)
    version = models.CharField(max_length=XFORM_TITLE_LENGTH, null=True)

    # store a geographic objects associated with this instance
    geom = models.GeometryCollectionField(null=True)

    # Keep track of whether all media attachments have been received
    media_all_received = models.NullBooleanField(
        _("Received All Media Attachemts"),
        null=True,
        default=True)
    total_media = models.PositiveIntegerField(_("Total Media Attachments"),
                                              null=True,
                                              default=0)
    media_count = models.PositiveIntegerField(_("Received Media Attachments"),
                                              null=True,
                                              default=0)
    checksum = models.CharField(max_length=64, null=True, blank=True,
                                db_index=True)
    # Keep track of submission reviews, only query reviews if true
    has_a_review = models.BooleanField(_("has_a_review"), default=False)

    tags = TaggableManager()

    class Meta:
        app_label = 'logger'
        unique_together = ('xform', 'uuid')

    @classmethod
    def set_deleted_at(cls, instance_id, deleted_at=timezone.now(), user=None):
        try:
            instance = cls.objects.get(id=instance_id)
        except cls.DoesNotExist:
            pass
        else:
            instance.set_deleted(deleted_at, user)

    def _check_active(self, force):
        """Check that form is active and raise exception if not.

        :param force: Ignore restrictions on saving.
        """
        # pylint: disable=no-member
        if not force and self.xform and not self.xform.downloadable:
            raise FormInactiveError()

    def _check_is_merged_dataset(self):
        """Check for merged datasets.

        Raises an exception to prevent datasubmissions
        """
        # pylint: disable=no-member
        if self.xform and self.xform.is_merged_dataset:
            raise FormIsMergedDatasetError()

    def get_expected_media(self):
        """
        Returns a list of expected media files from the submission data.
        """
        if not hasattr(self, '_expected_media'):
            # pylint: disable=no-member
            data = self.get_dict()
            media_list = []
            if 'encryptedXmlFile' in data and self.xform.encrypted:
                media_list.append(data['encryptedXmlFile'])
                if 'media' in data:
                    # pylint: disable=no-member
                    media_list.extend([i['media/file'] for i in data['media']])
            else:
                media_xpaths = (self.xform.get_media_survey_xpaths() +
                                self.xform.get_osm_survey_xpaths())
                for media_xpath in media_xpaths:
                    media_list.extend(
                        get_values_matching_key(data, media_xpath))
            # pylint: disable=attribute-defined-outside-init
            self._expected_media = list(set(media_list))

        return self._expected_media

    @property
    def num_of_media(self):
        """
        Returns number of media attachments expected in the submission.
        """
        if not hasattr(self, '_num_of_media'):
            # pylint: disable=attribute-defined-outside-init
            self._num_of_media = len(self.get_expected_media())

        return self._num_of_media

    @property
    def attachments_count(self):
        return self.attachments.filter(
            name__in=self.get_expected_media()
        ).distinct('name').order_by('name').count()

    def save(self, *args, **kwargs):
        force = kwargs.get('force')

        if force:
            del kwargs['force']

        self._check_is_merged_dataset()
        self._check_active(force)
        self._set_geom()
        self._set_json()
        self._set_survey_type()
        self._set_uuid()
        # pylint: disable=no-member
        self.version = self.json.get(VERSION, self.xform.version)

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

    # pylint: disable=no-member
    def set_deleted(self, deleted_at=timezone.now(), user=None):
        if user:
            self.deleted_by = user
        self.deleted_at = deleted_at
        self.save()
        # force submission count re-calculation
        self.xform.submission_count(force_update=True)
        self.parsed_instance.save()
Esempio n. 27
0
class Occurrence(models.Model):
    #id = models.AutoField("id",primary_key=True,db_column="id",null=False,blank=True)
    barcode = models.IntegerField("Barcode", null=True, blank=True)
    datelastmodified = models.DateTimeField("Date Last Modified",
                                            null=True,
                                            blank=True)
    basisofrecord = models.CharField("Basis of Record",
                                     null=True,
                                     blank=True,
                                     max_length=50,
                                     choices=basisCHOICES)
    itemtype = models.CharField("Item Type",
                                null=True,
                                blank=True,
                                max_length=255,
                                choices=itemtypeCHOICES)
    institutionalcode = models.CharField("Institution",
                                         null=True,
                                         blank=True,
                                         max_length=20)
    collectioncode = models.CharField("Coll Code",
                                      null=True,
                                      blank=True,
                                      max_length=20,
                                      choices=(("DIK", "DIK"), ("ASB", "ASB")))
    paleolocalitynumber = models.IntegerField("Locality #",
                                              null=True,
                                              blank=True)
    paleosublocality = models.CharField("Sublocality",
                                        null=True,
                                        blank=True,
                                        max_length=50)
    itemnumber = models.IntegerField("Item #",
                                     null=True,
                                     blank=True,
                                     max_length=50)
    itempart = models.CharField("Item Part",
                                null=True,
                                blank=True,
                                max_length=10)
    catalognumber = models.CharField("Catalog Number",
                                     null=True,
                                     blank=True,
                                     max_length=255)
    remarks = models.TextField("Remarks",
                               null=True,
                               blank=True,
                               max_length=2500)
    itemscientificname = models.CharField("Scientific Name",
                                          null=True,
                                          blank=True,
                                          max_length=255)
    itemdescription = models.CharField("Description",
                                       null=True,
                                       blank=True,
                                       max_length=255)
    continent = models.CharField(null=True, blank=True, max_length=50)
    country = models.CharField(null=True, blank=True, max_length=50)
    stateprovince = models.CharField(null=True, blank=True, max_length=50)
    locality_text = models.CharField(null=True,
                                     blank=True,
                                     max_length=255,
                                     db_column="locality")
    verbatimcoordinates = models.CharField(null=True,
                                           blank=True,
                                           max_length=50)
    verbatimcoordinatesystem = models.CharField(null=True,
                                                blank=True,
                                                max_length=50)
    georeferenceremarks = models.CharField(null=True,
                                           blank=True,
                                           max_length=50)
    utmzone = models.IntegerField(null=True, blank=True)
    utmeast = models.FloatField(null=True, blank=True)
    utmnorth = models.FloatField(null=True, blank=True)
    geodeticdatum = models.CharField(null=True, blank=True, max_length=20)
    collectingmethod = models.CharField("Collection Method",
                                        null=True,
                                        blank=True,
                                        max_length=50)
    relatedcatalogitems = models.CharField(null=True,
                                           blank=True,
                                           max_length=50)
    earliestdatecollected = models.DateTimeField(null=True, blank=True)
    dayofyear = models.IntegerField(null=True, blank=True)
    collector = models.CharField(null=True, blank=True, max_length=50)
    finder = models.CharField(null=True, blank=True, max_length=50)
    disposition = models.CharField(null=True, blank=True, max_length=255)
    collectionremarks = models.CharField("Remarks",
                                         null=True,
                                         blank=True,
                                         max_length=255)
    fieldnumber = models.DateTimeField("Field Number", null=True, blank=True)
    monthcollected = models.CharField(null=True, blank=True, max_length=20)
    yearcollected = models.IntegerField("Year", null=True, blank=True)
    individualcount = models.IntegerField(null=True, blank=True)
    preparationstatus = models.CharField(null=True, blank=True, max_length=50)
    strat_upper = models.CharField(null=True,
                                   blank=True,
                                   max_length=255,
                                   db_column="stratigraphicmarkerupper")
    distancefromupper = models.FloatField("Distance Upper",
                                          null=True,
                                          blank=True)
    strat_lower = models.CharField(null=True,
                                   blank=True,
                                   max_length=255,
                                   db_column="stratigraphicmarkerlower")
    distancefromlower = models.FloatField("Distance Lower",
                                          null=True,
                                          blank=True)
    strat_found = models.CharField(null=True,
                                   blank=True,
                                   max_length=255,
                                   db_column="stratigraphicmarkerfound")
    distancefromfound = models.FloatField("Distance Found",
                                          null=True,
                                          blank=True)
    strat_likely = models.CharField(null=True,
                                    blank=True,
                                    max_length=255,
                                    db_column="stratigraphicmarkerlikely")
    distancefromlikely = models.FloatField("Distance Likely",
                                           null=True,
                                           blank=True)
    stratigraphicmember = models.CharField("Member",
                                           null=True,
                                           blank=True,
                                           max_length=255)
    analyticalunit = models.CharField("Submember",
                                      null=True,
                                      blank=True,
                                      max_length=255)
    analyticalunit2 = models.CharField("Submember2",
                                       null=True,
                                       blank=True,
                                       max_length=255)
    analyticalunit3 = models.CharField("Submember3",
                                       null=True,
                                       blank=True,
                                       max_length=255)
    insitu = models.IntegerField("In Situ?", null=True, blank=True)
    ranked = models.IntegerField("Ranked?", null=True, blank=True)
    imageurl = models.CharField(null=True, blank=True, max_length=255)
    relatedinformation = models.CharField(null=True, blank=True, max_length=50)
    #localityid = models.IntegerField(null=True, blank=True)
    stratigraphicsection = models.CharField(null=True,
                                            blank=True,
                                            max_length=50)
    stratigraphicheightinmeters = models.FloatField(null=True, blank=True)
    weathering = models.IntegerField(null=True, blank=True)
    surfacemodification = models.CharField(null=True,
                                           blank=True,
                                           max_length=255)
    #point_x = models.FloatField(null=True, blank=True)
    #point_y = models.FloatField(null=True, blank=True)
    problem = models.IntegerField(null=True, blank=True, max_length=5)
    problemcomment = models.CharField(null=True, blank=True, max_length=255)
    dgupdate2013 = models.IntegerField(null=True, blank=True)
    dgupdatex = models.FloatField(null=True, blank=True)
    dgupdatey = models.FloatField(null=True, blank=True)
    geom = models.PointField(srid=4326)
    objects = models.GeoManager()
    locality = models.ForeignKey(Locality)

    @staticmethod
    def fields_to_display():
        fields = ("id", "barcode")
        return fields

    def point_X(self):
        return self.geom.x

    def point_Y(self):
        return self.geom.y

    def __unicode__(self):
        niceName = str(self.collectioncode) + "-" + str(
            self.paleolocalitynumber) + "-" + str(self.itemnumber) + str(
                self.itempart) + " [" + str(
                    self.itemscientificname) + " " + str(
                        self.itemdescription) + "]"
        return niceName.replace("None", "").replace("--", "")

    def save(self, *args, **kwargs):  #custom save method for occurrence
        theCatalogNumber = str(self.collectioncode) + "-" + str(
            self.paleolocalitynumber) + str(self.paleosublocality) + "-" + str(
                self.itemnumber) + str(self.itempart)
        self.catalognumber = theCatalogNumber.replace("None", "")
        self.datelastmodified = datetime.now(
        )  # TODO change datelastmodified autonow option to accomplish this

        #call the normal drp_occurrence save method using alternate database
        super(Occurrence, self).save(*args, **kwargs)

    class Meta:
        verbose_name = "DRP Occurrence"
        verbose_name_plural = "DRP Occurrences"
        # The DRP database is in the SDE standard in order to make it compatible with
        # ArcGIS 10.1. Django does not handle PostGIS DB schemas natively. This is a
        # work-around to point Django to the right location for the data tables.
        #db_table='drp_occurrence'
        ordering = [
            "collectioncode", "paleolocalitynumber", "itemnumber", "itempart"
        ]
Esempio n. 28
0
class InstanceHistory(models.Model, InstanceBaseClass):

    class Meta:
        app_label = 'logger'

    xform_instance = models.ForeignKey(
        Instance, related_name='submission_history', on_delete=models.CASCADE)
    user = models.ForeignKey(User, null=True, on_delete=models.CASCADE)

    xml = models.TextField()
    # old instance id
    uuid = models.CharField(max_length=249, default=u'')

    date_created = models.DateTimeField(auto_now_add=True)
    date_modified = models.DateTimeField(auto_now=True)
    submission_date = models.DateTimeField(null=True, default=None)
    geom = models.GeometryCollectionField(null=True)
    checksum = models.CharField(max_length=64, null=True, blank=True)

    @property
    def xform(self):
        return self.xform_instance.xform

    @property
    def attachments(self):
        return self.xform_instance.attachments.all()

    @property
    def json(self):
        return self.get_full_dict(load_existing=False)

    @property
    def status(self):
        return self.xform_instance.status

    @property
    def tags(self):
        return self.xform_instance.tags

    @property
    def notes(self):
        return self.xform_instance.notes.all()

    @property
    def reviews(self):
        return self.xform_instance.reviews.all()

    @property
    def version(self):
        return self.xform_instance.version

    @property
    def osm_data(self):
        return self.xform_instance.osm_data

    @property
    def deleted_at(self):
        return None

    @property
    def total_media(self):
        return self.xform_instance.total_media

    @property
    def has_a_review(self):
        return self.xform_instance.has_a_review

    @property
    def media_count(self):
        return self.xform_instance.media_count

    @property
    def media_all_received(self):
        return self.xform_instance.media_all_received

    def _set_parser(self):
        if not hasattr(self, "_parser"):
            self._parser = XFormInstanceParser(
                self.xml, self.xform_instance.xform
            )

    @classmethod
    def set_deleted_at(cls, instance_id, deleted_at=timezone.now()):
        return None
Esempio n. 29
0
class Reservation(ModifiableModel):
    CREATED = 'created'
    CANCELLED = 'cancelled'
    CONFIRMED = 'confirmed'
    DENIED = 'denied'
    REQUESTED = 'requested'
    WAITING_FOR_PAYMENT = 'waiting_for_payment'
    STATE_CHOICES = (
        (CREATED, _('created')),
        (CANCELLED, _('cancelled')),
        (CONFIRMED, _('confirmed')),
        (DENIED, _('denied')),
        (REQUESTED, _('requested')),
        (WAITING_FOR_PAYMENT, _('waiting for payment')),
    )

    TYPE_NORMAL = 'normal'
    TYPE_BLOCKED = 'blocked'
    TYPE_CHOICES = (
        (TYPE_NORMAL, _('Normal reservation')),
        (TYPE_BLOCKED, _('Resource blocked')),
    )

    resource = models.ForeignKey('Resource',
                                 verbose_name=_('Resource'),
                                 db_index=True,
                                 related_name='reservations',
                                 on_delete=models.PROTECT)
    begin = models.DateTimeField(verbose_name=_('Begin time'))
    end = models.DateTimeField(verbose_name=_('End time'))
    duration = pgfields.DateTimeRangeField(
        verbose_name=_('Length of reservation'),
        null=True,
        blank=True,
        db_index=True)
    comments = models.TextField(null=True,
                                blank=True,
                                verbose_name=_('Comments'))
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             verbose_name=_('User'),
                             null=True,
                             blank=True,
                             db_index=True,
                             on_delete=models.PROTECT)
    state = models.CharField(max_length=32,
                             choices=STATE_CHOICES,
                             verbose_name=_('State'),
                             default=CREATED)
    approver = models.ForeignKey(settings.AUTH_USER_MODEL,
                                 verbose_name=_('Approver'),
                                 related_name='approved_reservations',
                                 null=True,
                                 blank=True,
                                 on_delete=models.SET_NULL)
    staff_event = models.BooleanField(verbose_name=_('Is staff event'),
                                      default=False)
    type = models.CharField(blank=False,
                            verbose_name=_('Type'),
                            max_length=32,
                            choices=TYPE_CHOICES,
                            default=TYPE_NORMAL)

    # access-related fields
    access_code = models.CharField(verbose_name=_('Access code'),
                                   max_length=32,
                                   null=True,
                                   blank=True)

    # EXTRA FIELDS START HERE

    event_subject = models.CharField(max_length=200,
                                     verbose_name=_('Event subject'),
                                     blank=True)
    event_description = models.TextField(verbose_name=_('Event description'),
                                         blank=True)
    number_of_participants = models.PositiveSmallIntegerField(
        verbose_name=_('Number of participants'), blank=True, null=True)
    participants = models.TextField(verbose_name=_('Participants'), blank=True)
    host_name = models.CharField(verbose_name=_('Host name'),
                                 max_length=100,
                                 blank=True)
    reservation_extra_questions = models.TextField(
        verbose_name=_('Reservation extra questions'), blank=True)

    reserver_name = models.CharField(verbose_name=_('Reserver name'),
                                     max_length=100,
                                     blank=True)
    reserver_id = models.CharField(
        verbose_name=_('Reserver ID (business or person)'),
        max_length=30,
        blank=True)
    reserver_email_address = models.EmailField(
        verbose_name=_('Reserver email address'), blank=True)
    reserver_phone_number = models.CharField(
        verbose_name=_('Reserver phone number'), max_length=30, blank=True)
    reserver_address_street = models.CharField(
        verbose_name=_('Reserver address street'), max_length=100, blank=True)
    reserver_address_zip = models.CharField(
        verbose_name=_('Reserver address zip'), max_length=30, blank=True)
    reserver_address_city = models.CharField(
        verbose_name=_('Reserver address city'), max_length=100, blank=True)
    company = models.CharField(verbose_name=_('Company'),
                               max_length=100,
                               blank=True)
    billing_first_name = models.CharField(verbose_name=_('Billing first name'),
                                          max_length=100,
                                          blank=True)
    billing_last_name = models.CharField(verbose_name=_('Billing last name'),
                                         max_length=100,
                                         blank=True)
    billing_email_address = models.EmailField(
        verbose_name=_('Billing email address'), blank=True)
    billing_phone_number = models.CharField(
        verbose_name=_('Billing phone number'), max_length=30, blank=True)
    billing_address_street = models.CharField(
        verbose_name=_('Billing address street'), max_length=100, blank=True)
    billing_address_zip = models.CharField(
        verbose_name=_('Billing address zip'), max_length=30, blank=True)
    billing_address_city = models.CharField(
        verbose_name=_('Billing address city'), max_length=100, blank=True)

    # If the reservation was imported from another system, you can store the original ID in the field below.
    origin_id = models.CharField(verbose_name=_('Original ID'),
                                 max_length=50,
                                 editable=False,
                                 null=True)

    objects = ReservationQuerySet.as_manager()

    class Meta:
        verbose_name = _("reservation")
        verbose_name_plural = _("reservations")
        ordering = ('id', )

    def _save_dt(self, attr, dt):
        """
        Any DateTime object is converted to UTC time zone aware DateTime
        before save

        If there is no time zone on the object, resource's time zone will
        be assumed through its unit's time zone
        """
        save_dt(self, attr, dt, self.resource.unit.time_zone)

    def _get_dt(self, attr, tz):
        return get_dt(self, attr, tz)

    @property
    def begin_tz(self):
        return self.begin

    @begin_tz.setter
    def begin_tz(self, dt):
        self._save_dt('begin', dt)

    def get_begin_tz(self, tz):
        return self._get_dt("begin", tz)

    @property
    def end_tz(self):
        return self.end

    @end_tz.setter
    def end_tz(self, dt):
        """
        Any DateTime object is converted to UTC time zone aware DateTime
        before save

        If there is no time zone on the object, resource's time zone will
        be assumed through its unit's time zone
        """
        self._save_dt('end', dt)

    def get_end_tz(self, tz):
        return self._get_dt("end", tz)

    def is_active(self):
        return self.end >= timezone.now() and self.state not in (
            Reservation.CANCELLED, Reservation.DENIED)

    def is_own(self, user):
        if not (user and user.is_authenticated):
            return False
        return user == self.user

    def need_manual_confirmation(self):
        return self.resource.need_manual_confirmation

    def are_extra_fields_visible(self, user):
        # the following logic is used also implemented in ReservationQuerySet
        # so if this is changed that probably needs to be changed as well

        if self.is_own(user):
            return True
        return self.resource.can_view_reservation_extra_fields(user)

    def can_view_access_code(self, user):
        if self.is_own(user):
            return True
        return self.resource.can_view_access_codes(user)

    def set_state(self, new_state, user):
        # Make sure it is a known state
        assert new_state in (Reservation.REQUESTED, Reservation.CONFIRMED,
                             Reservation.DENIED, Reservation.CANCELLED,
                             Reservation.WAITING_FOR_PAYMENT)

        old_state = self.state
        if new_state == old_state:
            if old_state == Reservation.CONFIRMED:
                reservation_modified.send(sender=self.__class__,
                                          instance=self,
                                          user=user)
            return

        if new_state == Reservation.CONFIRMED:
            self.approver = user
            reservation_confirmed.send(sender=self.__class__,
                                       instance=self,
                                       user=user)
        elif old_state == Reservation.CONFIRMED:
            self.approver = None

        user_is_staff = self.user is not None and self.user.is_staff

        # Notifications
        if new_state == Reservation.REQUESTED:
            self.send_reservation_requested_mail()
            self.send_reservation_requested_mail_to_officials()
        elif new_state == Reservation.CONFIRMED:
            if self.need_manual_confirmation():
                self.send_reservation_confirmed_mail()
            elif self.access_code:
                self.send_reservation_created_with_access_code_mail()
            else:
                if not user_is_staff:
                    # notifications are not sent from staff created reservations to avoid spam
                    self.send_reservation_created_mail()
        elif new_state == Reservation.DENIED:
            self.send_reservation_denied_mail()
        elif new_state == Reservation.CANCELLED:
            order = self.get_order()
            if order:
                if order.state == order.CANCELLED:
                    self.send_reservation_cancelled_mail()
            else:
                if user != self.user:
                    self.send_reservation_cancelled_mail()
            reservation_cancelled.send(sender=self.__class__,
                                       instance=self,
                                       user=user)

        self.state = new_state
        self.save()

    def can_modify(self, user):
        if not user:
            return False

        if self.state == Reservation.WAITING_FOR_PAYMENT:
            return False

        if self.get_order():
            return self.resource.can_modify_paid_reservations(user)

        # reservations that need manual confirmation and are confirmed cannot be
        # modified or cancelled without reservation approve permission
        cannot_approve = not self.resource.can_approve_reservations(user)
        if self.need_manual_confirmation(
        ) and self.state == Reservation.CONFIRMED and cannot_approve:
            return False

        return self.user == user or self.resource.can_modify_reservations(user)

    def can_add_comment(self, user):
        if self.is_own(user):
            return True
        return self.resource.can_access_reservation_comments(user)

    def can_view_field(self, user, field):
        if field not in RESERVATION_EXTRA_FIELDS:
            return True
        if self.is_own(user):
            return True
        return self.resource.can_view_reservation_extra_fields(user)

    def can_view_catering_orders(self, user):
        if self.is_own(user):
            return True
        return self.resource.can_view_catering_orders(user)

    def can_add_product_order(self, user):
        return self.is_own(user)

    def can_view_product_orders(self, user):
        if self.is_own(user):
            return True
        return self.resource.can_view_product_orders(user)

    def get_order(self):
        return getattr(self, 'order', None)

    def format_time(self):
        tz = self.resource.unit.get_tz()
        begin = self.begin.astimezone(tz)
        end = self.end.astimezone(tz)
        return format_dt_range(translation.get_language(), begin, end)

    def __str__(self):
        if self.state != Reservation.CONFIRMED:
            state_str = ' (%s)' % self.state
        else:
            state_str = ''
        return "%s: %s%s" % (self.format_time(), self.resource, state_str)

    def clean(self, **kwargs):
        """
        Check restrictions that are common to all reservations.

        If this reservation isn't yet saved and it will modify an existing reservation,
        the original reservation need to be provided in kwargs as 'original_reservation', so
        that it can be excluded when checking if the resource is available.
        """
        if self.end <= self.begin:
            raise ValidationError(
                _("You must end the reservation after it has begun"))

        # Check that begin and end times are on valid time slots.
        opening_hours = self.resource.get_opening_hours(
            self.begin.date(), self.end.date())
        for dt in (self.begin, self.end):
            days = opening_hours.get(dt.date(), [])
            day = next((day for day in days if day['opens'] is not None
                        and day['opens'] <= dt <= day['closes']), None)
            if day and not is_valid_time_slot(dt, self.resource.slot_size,
                                              day['opens']):
                raise ValidationError(
                    _("Begin and end time must match time slots"),
                    code='invalid_time_slot')

        original_reservation = self if self.pk else kwargs.get(
            'original_reservation', None)
        if self.resource.check_reservation_collision(self.begin, self.end,
                                                     original_reservation):
            raise ValidationError(
                _("The resource is already reserved for some of the period"))

        if (self.end - self.begin) < self.resource.min_period:
            raise ValidationError(
                _("The minimum reservation length is %(min_period)s") %
                {'min_period': humanize_duration(self.resource.min_period)})

        if self.access_code:
            validate_access_code(self.access_code,
                                 self.resource.access_code_type)

    def get_notification_context(self,
                                 language_code,
                                 user=None,
                                 notification_type=None):
        if not user:
            user = self.user
        with translation.override(language_code):
            reserver_name = self.reserver_name
            reserver_email_address = self.reserver_email_address
            if not reserver_name and self.user and self.user.get_display_name(
            ):
                reserver_name = self.user.get_display_name()
            if not reserver_email_address and user and user.email:
                reserver_email_address = user.email
            context = {
                'resource': self.resource.name,
                'begin': localize_datetime(self.begin),
                'end': localize_datetime(self.end),
                'begin_dt': self.begin,
                'end_dt': self.end,
                'time_range': self.format_time(),
                'reserver_name': reserver_name,
                'reserver_email_address': reserver_email_address,
            }
            directly_included_fields = (
                'number_of_participants',
                'host_name',
                'event_subject',
                'event_description',
                'reserver_phone_number',
                'billing_first_name',
                'billing_last_name',
                'billing_email_address',
                'billing_phone_number',
                'billing_address_street',
                'billing_address_zip',
                'billing_address_city',
            )
            for field in directly_included_fields:
                context[field] = getattr(self, field)
            if self.resource.unit:
                context['unit'] = self.resource.unit.name
                context['unit_id'] = self.resource.unit.id
            if self.can_view_access_code(user) and self.access_code:
                context['access_code'] = self.access_code

            if notification_type == NotificationType.RESERVATION_CONFIRMED:
                if self.resource.reservation_confirmed_notification_extra:
                    context[
                        'extra_content'] = self.resource.reservation_confirmed_notification_extra
            elif notification_type == NotificationType.RESERVATION_REQUESTED:
                if self.resource.reservation_requested_notification_extra:
                    context[
                        'extra_content'] = self.resource.reservation_requested_notification_extra

            # Get last main and ground plan images. Normally there shouldn't be more than one of each
            # of those images.
            images = self.resource.images.filter(
                type__in=('main', 'ground_plan')).order_by('-sort_order')
            main_image = next((i for i in images if i.type == 'main'), None)
            ground_plan_image = next(
                (i for i in images if i.type == 'ground_plan'), None)

            if main_image:
                main_image_url = main_image.get_full_url()
                if main_image_url:
                    context['resource_main_image_url'] = main_image_url
            if ground_plan_image:
                ground_plan_image_url = ground_plan_image.get_full_url()
                if ground_plan_image_url:
                    context[
                        'resource_ground_plan_image_url'] = ground_plan_image_url

            order = getattr(self, 'order', None)
            if order:
                context['order'] = order.get_notification_context(
                    language_code)

        return context

    def send_reservation_mail(self,
                              notification_type,
                              user=None,
                              attachments=None):
        """
        Stuff common to all reservation related mails.

        If user isn't given use self.user.
        """
        try:
            notification_template = NotificationTemplate.objects.get(
                type=notification_type)
        except NotificationTemplate.DoesNotExist:
            return

        if user:
            email_address = user.email
        else:
            if not (self.reserver_email_address or self.user):
                return
            email_address = self.reserver_email_address or self.user.email
            user = self.user

        language = user.get_preferred_language() if user else DEFAULT_LANG
        context = self.get_notification_context(
            language, notification_type=notification_type)

        try:
            rendered_notification = notification_template.render(
                context, language)
        except NotificationTemplateException as e:
            logger.error(e, exc_info=True, extra={'user': user.uuid})
            return

        send_respa_mail(email_address, rendered_notification['subject'],
                        rendered_notification['body'],
                        rendered_notification['html_body'], attachments)

    def send_reservation_requested_mail(self):
        self.send_reservation_mail(NotificationType.RESERVATION_REQUESTED)

    def send_reservation_requested_mail_to_officials(self):
        notify_users = self.resource.get_users_with_perm(
            'can_approve_reservation')
        if len(notify_users) > 100:
            raise Exception("Refusing to notify more than 100 users (%s)" %
                            self)
        for user in notify_users:
            self.send_reservation_mail(
                NotificationType.RESERVATION_REQUESTED_OFFICIAL, user=user)

    def send_reservation_denied_mail(self):
        self.send_reservation_mail(NotificationType.RESERVATION_DENIED)

    def send_reservation_confirmed_mail(self):
        reservations = [self]
        ical_file = build_reservations_ical_file(reservations)
        attachment = ('reservation.ics', ical_file, 'text/calendar')
        self.send_reservation_mail(NotificationType.RESERVATION_CONFIRMED,
                                   attachments=[attachment])

    def send_reservation_cancelled_mail(self):
        self.send_reservation_mail(NotificationType.RESERVATION_CANCELLED)

    def send_reservation_created_mail(self):
        reservations = [self]
        ical_file = build_reservations_ical_file(reservations)
        attachment = 'reservation.ics', ical_file, 'text/calendar'
        self.send_reservation_mail(NotificationType.RESERVATION_CREATED,
                                   attachments=[attachment])

    def send_reservation_created_with_access_code_mail(self):
        reservations = [self]
        ical_file = build_reservations_ical_file(reservations)
        attachment = 'reservation.ics', ical_file, 'text/calendar'
        self.send_reservation_mail(
            NotificationType.RESERVATION_CREATED_WITH_ACCESS_CODE,
            attachments=[attachment])

    def send_access_code_created_mail(self):
        self.send_reservation_mail(
            NotificationType.RESERVATION_ACCESS_CODE_CREATED)

    def save(self, *args, **kwargs):
        self.duration = DateTimeTZRange(self.begin, self.end, '[)')

        if not self.access_code:
            access_code_type = self.resource.access_code_type
            if self.resource.is_access_code_enabled(
            ) and self.resource.generate_access_codes:
                self.access_code = generate_access_code(access_code_type)

        return super().save(*args, **kwargs)
Esempio n. 30
0
class TreeAction(TreeItem):
    key = models.CharField(max_length=256, choices=settings.CHOICES["actions"])
    value = models.DateTimeField()