示例#1
0
 def __init__(self, *args, **kwargs):
     super(SpatialQueryForm, self).__init__(*args, **kwargs)
     lookups = GeometryField.get_lookups()
     for lookup in self.data:
         if lookup in lookups:
             self.fields[lookup] = fields.GeometryField(
                 required=False, widget=forms.BaseGeometryWidget())
             break
示例#2
0
 def test_geometry_value_annotation(self):
     p = Point(1, 1, srid=4326)
     point = City.objects.annotate(p=Value(p, GeometryField(
         srid=4326))).first().p
     self.assertEqual(point, p)
示例#3
0
class SpatialUnit(ResourceModelMixin, RandomIDModel):
    """A single spatial unit: has a type, an optional geometry, a
    type-dependent set of attributes, and a set of relationships to
    other spatial units.

    """

    # All spatial units are associated with a single project.
    project = models.ForeignKey(Project,
                                on_delete=models.CASCADE,
                                related_name='spatial_units')

    # Spatial unit type: used to manage range of allowed attributes.
    type = models.CharField(max_length=10)

    # Spatial unit geometry is optional: some spatial units may only
    # have a textual description of their location.
    geometry = GeometryField(null=True)

    # JSON attributes field with management of allowed members.
    attributes = JSONAttributeField(default={})

    # Spatial unit-spatial unit relationships: includes spatial
    # containment and split/merge relationships.
    relationships = models.ManyToManyField(
        'self',
        through='SpatialRelationship',
        through_fields=('su1', 'su2'),
        symmetrical=False,
        related_name='relationships_set',
    )

    history = HistoricalRecords()

    class Meta:
        ordering = ('type', )

    class TutelaryMeta:
        perm_type = 'spatial'
        path_fields = ('project', 'id')
        actions = (('spatial.list', {
            'description':
            _("List existing spatial units of a project"),
            'error_message':
            messages.SPATIAL_LIST,
            'permissions_object':
            'project'
        }), ('spatial.create', {
            'description': _("Add a spatial unit to a project"),
            'error_message': messages.SPATIAL_CREATE,
            'permissions_object': 'project'
        }), ('spatial.view', {
            'description': _("View an existing spatial unit"),
            'error_message': messages.SPATIAL_VIEW
        }), ('spatial.update', {
            'description': _("Update an existing spatial unit"),
            'error_message': messages.SPATIAL_UPDATE
        }), ('spatial.delete', {
            'description': _("Delete an existing spatial unit"),
            'error_message': messages.SPATIAL_DELETE
        }), ('spatial.resources.add', {
            'description': _("Add resources to this spatial unit"),
            'error_message': messages.SPATIAL_ADD_RESOURCE
        }))

    def __str__(self):
        return "<SpatialUnit: {}>".format(self.name)

    def __repr__(self):
        repr_string = ('<SpatialUnit id={obj.id}'
                       ' project={obj.project.slug}'
                       ' type={obj.type}>')
        return repr_string.format(obj=self)

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

    @property
    def ui_class_name(self):
        return _("Location")

    def get_absolute_url(self):
        return iri_to_uri(
            reverse(
                'locations:detail',
                kwargs={
                    'organization': self.project.organization.slug,
                    'project': self.project.slug,
                    'location': self.id,
                },
            ))

    @cached_property
    def location_type_label(self):
        if not self.project.current_questionnaire:
            return dict(TYPE_CHOICES)[self.type]

        question = Question.objects.get(
            questionnaire_id=self.project.current_questionnaire,
            name='location_type')
        label = question.options.get(name=self.type).label_xlat
        if label is None or isinstance(label, str):
            return label
        else:
            return label.get(get_language(),
                             label[question.questionnaire.default_language])
示例#4
0
 def output_field(self):
     return GeometryField(srid=self.source_expressions[0].field.srid)
示例#5
0
class ObjectRecord(models.Model):
    index = models.PositiveIntegerField(
        default=1,
        help_text=_("Incremental index number of the object record."),
    )
    object = models.ForeignKey(Object, on_delete=models.CASCADE, related_name="records")
    version = models.PositiveSmallIntegerField(
        _("version"),
        help_text=_("Version of the OBJECTTYPE for data in the object record"),
    )
    data = JSONField(
        _("data"), help_text=_("Object data, based on OBJECTTYPE"), default=dict
    )
    start_at = models.DateField(
        _("start at"), help_text=_("Legal start date of the object record")
    )
    end_at = models.DateField(
        _("end at"), null=True, help_text=_("Legal end date of the object record")
    )
    registration_at = models.DateField(
        _("registration at"),
        default=datetime.date.today,
        help_text=_("The date when the record was registered in the system"),
    )
    correct = models.OneToOneField(
        "core.ObjectRecord",
        verbose_name="correction for",
        on_delete=models.CASCADE,
        related_name="corrected",
        null=True,
        blank=True,
        help_text=_("Object record which corrects the current record"),
    )
    geometry = GeometryField(
        _("geometry"),
        blank=True,
        null=True,
        help_text=_(
            "Point, linestring or polygon object which represents the coordinates of the object"
        ),
    )

    class Meta:
        unique_together = ("object", "index")

    def __str__(self):
        return f"{self.version} ({self.start_at})"

    def clean(self):
        super().clean()

        check_objecttype(self.object.object_type, self.version, self.data)

    def save(self, *args, **kwargs):
        if not self.id and self.object.last_record:
            self.index = self.object.last_record.index + 1

            #  add end_at to previous record
            previous_record = self.object.last_record
            previous_record.end_at = self.start_at
            previous_record.save()

        super().save(*args, **kwargs)
示例#6
0
 def test_geography_value(self):
     p = Polygon(((1, 1), (1, 2), (2, 2), (2, 1), (1, 1)))
     area = City.objects.annotate(a=functions.Area(
         Value(p, GeometryField(srid=4326, geography=True)))).first().a
     self.assertAlmostEqual(area.sq_km, 12305.1, 0)
示例#7
0
class Zaak(AuditTrailMixin, APIMixin, models.Model):
    """
    Modelleer de structuur van een ZAAK.

    Een samenhangende hoeveelheid werk met een welgedefinieerde aanleiding
    en een welgedefinieerd eindresultaat, waarvan kwaliteit en doorlooptijd
    bewaakt moeten worden.
    """

    uuid = models.UUIDField(unique=True,
                            default=uuid.uuid4,
                            help_text="Unieke resource identifier (UUID4)")

    # Relate 'is_deelzaak_van'
    # De relatie vanuit een zaak mag niet verwijzen naar
    # dezelfde zaak d.w.z. moet verwijzen naar een andere
    # zaak. Die andere zaak mag geen relatie ?is deelzaak
    # van? hebben (d.w.z. deelzaken van deelzaken worden
    # niet ondersteund).
    hoofdzaak = models.ForeignKey(
        "self",
        limit_choices_to={"hoofdzaak__isnull": True},
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name="deelzaken",
        verbose_name="is deelzaak van",
        help_text=_("URL-referentie naar de ZAAK, waarom verzocht is door de "
                    "initiator daarvan, die behandeld wordt in twee of meer "
                    "separate ZAAKen waarvan de onderhavige ZAAK er één is."),
    )

    identificatie = models.CharField(
        max_length=40,
        blank=True,
        help_text="De unieke identificatie van de ZAAK binnen de organisatie "
        "die verantwoordelijk is voor de behandeling van de ZAAK.",
        validators=[alphanumeric_excluding_diacritic],
        db_index=True,
    )
    bronorganisatie = RSINField(
        help_text="Het RSIN van de Niet-natuurlijk persoon zijnde de "
        "organisatie die de zaak heeft gecreeerd. Dit moet een geldig "
        "RSIN zijn van 9 nummers en voldoen aan "
        "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef")
    omschrijving = models.CharField(
        max_length=80,
        blank=True,
        help_text="Een korte omschrijving van de zaak.")
    toelichting = models.TextField(max_length=1000,
                                   blank=True,
                                   help_text="Een toelichting op de zaak.")

    _zaaktype_url = models.URLField(
        _("extern zaaktype"),
        blank=True,
        max_length=1000,
        help_text=_(
            "URL-referentie naar extern ZAAKTYPE (in een andere Catalogi API)."
        ),
    )
    _zaaktype = models.ForeignKey(
        "catalogi.ZaakType",
        on_delete=models.CASCADE,
        help_text="URL-referentie naar het ZAAKTYPE (in de Catalogi API).",
        null=True,
        blank=True,
    )
    zaaktype = FkOrURLField(
        fk_field="_zaaktype",
        url_field="_zaaktype_url",
        help_text="URL-referentie naar het ZAAKTYPE (in de Catalogi API).",
    )

    registratiedatum = models.DateField(
        help_text="De datum waarop de zaakbehandelende organisatie de ZAAK "
        "heeft geregistreerd. Indien deze niet opgegeven wordt, "
        "wordt de datum van vandaag gebruikt.",
        default=date.today,
    )
    verantwoordelijke_organisatie = RSINField(
        help_text=
        "Het RSIN van de Niet-natuurlijk persoon zijnde de organisatie "
        "die eindverantwoordelijk is voor de behandeling van de "
        "zaak. Dit moet een geldig RSIN zijn van 9 nummers en voldoen aan "
        "https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef")

    startdatum = models.DateField(
        help_text="De datum waarop met de uitvoering van de zaak is gestart",
        db_index=True,
    )
    einddatum = models.DateField(
        blank=True,
        null=True,
        help_text="De datum waarop de uitvoering van de zaak afgerond is.",
    )
    einddatum_gepland = models.DateField(
        blank=True,
        null=True,
        help_text="De datum waarop volgens de planning verwacht wordt dat de "
        "zaak afgerond wordt.",
    )
    uiterlijke_einddatum_afdoening = models.DateField(
        blank=True,
        null=True,
        help_text="De laatste datum waarop volgens wet- en regelgeving de zaak "
        "afgerond dient te zijn.",
    )
    publicatiedatum = models.DateField(
        _("publicatiedatum"),
        null=True,
        blank=True,
        help_text=_(
            "Datum waarop (het starten van) de zaak gepubliceerd is of wordt."
        ),
    )

    producten_of_diensten = ArrayField(
        models.URLField(_("URL naar product/dienst"), max_length=1000),
        default=list,
        help_text=_(
            "De producten en/of diensten die door de zaak worden voortgebracht. "
            "Dit zijn URLs naar de resources zoals die door de producten- "
            "en dienstencatalogus-API wordt ontsloten. "
            "De producten/diensten moeten bij het zaaktype vermeld zijn."),
        blank=True,
    )

    communicatiekanaal = models.URLField(
        _("communicatiekanaal"),
        blank=True,
        max_length=1000,
        help_text=
        _("Het medium waarlangs de aanleiding om een zaak te starten is ontvangen. "
          "URL naar een communicatiekanaal in de VNG-Referentielijst van communicatiekanalen."
          ),
    )

    vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField(
        _("vertrouwlijkheidaanduiding"),
        help_text=
        _("Aanduiding van de mate waarin het zaakdossier van de ZAAK voor de openbaarheid bestemd is."
          ),
    )

    betalingsindicatie = models.CharField(
        _("betalingsindicatie"),
        max_length=20,
        blank=True,
        choices=BetalingsIndicatie.choices,
        help_text=_("Indicatie of de, met behandeling van de zaak gemoeide, "
                    "kosten betaald zijn door de desbetreffende betrokkene."),
    )
    laatste_betaaldatum = models.DateTimeField(
        _("laatste betaaldatum"),
        blank=True,
        null=True,
        help_text=_(
            "De datum waarop de meest recente betaling is verwerkt "
            "van kosten die gemoeid zijn met behandeling van de zaak."),
    )

    zaakgeometrie = GeometryField(
        blank=True,
        null=True,
        help_text="Punt, lijn of (multi-)vlak geometrie-informatie.",
    )

    verlenging_reden = models.CharField(
        _("reden verlenging"),
        max_length=200,
        blank=True,
        help_text=
        _("Omschrijving van de reden voor het verlengen van de behandeling van de zaak."
          ),
    )
    verlenging_duur = DurationField(
        _("duur verlenging"),
        blank=True,
        null=True,
        help_text=_(
            "Het aantal werkbare dagen waarmee de doorlooptijd van de "
            "behandeling van de ZAAK is verlengd (of verkort) ten opzichte "
            "van de eerder gecommuniceerde doorlooptijd."),
    )
    verlenging = GegevensGroepType({
        "reden": verlenging_reden,
        "duur": verlenging_duur
    })

    opschorting_indicatie = models.BooleanField(
        _("indicatie opschorting"),
        default=False,
        blank=True,
        help_text=_(
            "Aanduiding of de behandeling van de ZAAK tijdelijk is opgeschort."
        ),
    )
    opschorting_reden = models.CharField(
        _("reden opschorting"),
        max_length=200,
        blank=True,
        help_text=
        _("Omschrijving van de reden voor het opschorten van de behandeling van de zaak."
          ),
    )
    opschorting = GegevensGroepType({
        "indicatie": opschorting_indicatie,
        "reden": opschorting_reden
    })

    selectielijstklasse = models.URLField(
        _("selectielijstklasse"),
        blank=True,
        max_length=1000,
        help_text=
        _("URL-referentie naar de categorie in de gehanteerde 'Selectielijst Archiefbescheiden' die, gezien "
          "het zaaktype en het resultaattype van de zaak, bepalend is voor het archiefregime van de zaak."
          ),
    )

    # Archiving
    archiefnominatie = models.CharField(
        _("archiefnominatie"),
        max_length=40,
        null=True,
        blank=True,
        choices=Archiefnominatie.choices,
        help_text=
        _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden."
          ),
        db_index=True,
    )
    archiefstatus = models.CharField(
        _("archiefstatus"),
        max_length=40,
        choices=Archiefstatus.choices,
        default=Archiefstatus.nog_te_archiveren,
        help_text=
        _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden."
          ),
        db_index=True,
    )
    archiefactiedatum = models.DateField(
        _("archiefactiedatum"),
        null=True,
        blank=True,
        help_text=
        _("De datum waarop het gearchiveerde zaakdossier vernietigd moet worden dan wel overgebracht moet "
          "worden naar een archiefbewaarplaats. Wordt automatisch berekend bij het aanmaken of wijzigen van "
          "een RESULTAAT aan deze ZAAK indien nog leeg."),
        db_index=True,
    )

    objects = ZaakQuerySet.as_manager()

    class Meta:
        verbose_name = "zaak"
        verbose_name_plural = "zaken"
        unique_together = ("bronorganisatie", "identificatie")

    def __str__(self):
        return self.identificatie

    def save(self, *args, **kwargs):
        if not self.identificatie:
            self.identificatie = generate_unique_identification(
                self, "registratiedatum")

        if (self.betalingsindicatie == BetalingsIndicatie.nvt
                and self.laatste_betaaldatum):
            self.laatste_betaaldatum = None

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

    @property
    def current_status_uuid(self):
        status = self.status_set.first()
        return status.uuid if status else None

    @property
    def is_closed(self) -> bool:
        return self.einddatum is not None

    def unique_representation(self):
        return f"{self.bronorganisatie} - {self.identificatie}"
示例#8
0
class SpatialUnit(ResourceModelMixin, RandomIDModel):
    """A single spatial unit: has a type, an optional geometry, a
    type-dependent set of attributes, and a set of relationships to
    other spatial units.

    """

    # All spatial units are associated with a single project.
    project = models.ForeignKey(Project,
                                on_delete=models.CASCADE,
                                related_name='spatial_units')

    # Spatial unit type: used to manage range of allowed attributes.
    type = models.CharField(max_length=100)

    # Spatial unit geometry is optional: some spatial units may only
    # have a textual description of their location.
    geometry = GeometryField(null=True, geography=True)

    # Area, auto-calculated via trigger (see spatial/migrations/#0005)
    area = models.FloatField(default=0)

    # JSON attributes field with management of allowed members.
    attributes = JSONAttributeField(default={})

    # Spatial unit-spatial unit relationships: includes spatial
    # containment and split/merge relationships.
    relationships = models.ManyToManyField(
        'self',
        through='SpatialRelationship',
        through_fields=('su1', 'su2'),
        symmetrical=False,
        related_name='relationships_set',
    )

    # Denormalized duplication of label from the QuestionOption related to the
    # SpatialUnit
    label = JSONField(null=True)

    # Audit history
    created_date = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)

    history = HistoricalRecords()

    class Meta:
        ordering = ('type', )

    class TutelaryMeta:
        perm_type = 'spatial'
        path_fields = ('project', 'id')
        actions = (('spatial.list', {
            'description':
            _("List existing spatial units of a project"),
            'error_message':
            messages.SPATIAL_LIST,
            'permissions_object':
            'project'
        }), ('spatial.create', {
            'description': _("Add a spatial unit to a project"),
            'error_message': messages.SPATIAL_CREATE,
            'permissions_object': 'project'
        }), ('spatial.view', {
            'description': _("View an existing spatial unit"),
            'error_message': messages.SPATIAL_VIEW
        }), ('spatial.update', {
            'description': _("Update an existing spatial unit"),
            'error_message': messages.SPATIAL_UPDATE
        }), ('spatial.delete', {
            'description': _("Delete an existing spatial unit"),
            'error_message': messages.SPATIAL_DELETE
        }), ('spatial.resources.add', {
            'description': _("Add resources to this spatial unit"),
            'error_message': messages.SPATIAL_ADD_RESOURCE
        }))

    def __str__(self):
        return "<SpatialUnit: {}>".format(self.name)

    def __repr__(self):
        repr_string = ('<SpatialUnit id={obj.id}'
                       ' project={obj.project.slug}'
                       ' type={obj.type}>')
        return repr_string.format(obj=self)

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

    @property
    def ui_class_name(self):
        return _("Location")

    def get_absolute_url(self):
        return iri_to_uri(
            reverse(
                'locations:detail',
                kwargs={
                    'organization': self.project.organization.slug,
                    'project': self.project.slug,
                    'location': self.id,
                },
            ))

    @cached_property
    def location_type_label(self):
        # Handle no questionnaire
        if (not self.project.current_questionnaire) or (self.label is None):
            return dict(TYPE_CHOICES)[self.type]

        # Handle non-translatable label
        if isinstance(self.label, str):
            return self.label

        # Handle translated label
        translated_label = self.label.get(get_language())
        if translated_label:
            return translated_label

        # If label failed to translate, fallback to default language
        rel_questionnaire = Questionnaire.objects.get(
            id=self.project.current_questionnaire)
        return self.label.get(rel_questionnaire.default_language)
示例#9
0
class LineSubstring(GeoFunc):
    output_field = GeometryField()
示例#10
0
 def test_deconstruct_empty(self):
     field = GeometryField()
     *_, kwargs = field.deconstruct()
     self.assertEqual(kwargs, {"srid": 4326})
示例#11
0
 def test_geometry_value_annotation_different_srid(self):
     p = Point(1, 1, srid=32140)
     point = City.objects.annotate(p=Value(p, GeometryField(srid=4326))).first().p
     self.assertTrue(point.equals_exact(p.transform(4326, clone=True), 10 ** -5))
     self.assertEqual(point.srid, 4326)
示例#12
0
class SpatialUnit(ResourceModelMixin, RandomIDModel):
    """A single spatial unit: has a type, an optional geometry, a
    type-dependent set of attributes, and a set of relationships to
    other spatial units.

    """

    # All spatial units are associated with a single project.
    project = models.ForeignKey(Project,
                                on_delete=models.CASCADE,
                                related_name='spatial_units')

    # Spatial unit type: used to manage range of allowed attributes.
    type = models.CharField(max_length=2, choices=TYPE_CHOICES, default='PA')

    # Spatial unit geometry is optional: some spatial units may only
    # have a textual description of their location.
    geometry = GeometryField(null=True)

    # JSON attributes field with management of allowed members.
    attributes = JSONAttributeField(default={})

    # Spatial unit-spatial unit relationships: includes spatial
    # containment and split/merge relationships.
    relationships = models.ManyToManyField(
        'self',
        through='SpatialRelationship',
        through_fields=('su1', 'su2'),
        symmetrical=False,
        related_name='relationships_set',
    )

    history = HistoricalRecords()

    class Meta:
        ordering = ('type', )

    class TutelaryMeta:
        perm_type = 'spatial'
        path_fields = ('project', 'id')
        actions = (('spatial.list', {
            'description':
            _("List existing spatial units of a project"),
            'error_message':
            messages.SPATIAL_LIST,
            'permissions_object':
            'project'
        }), ('spatial.create', {
            'description': _("Add a spatial unit to a project"),
            'error_message': messages.SPATIAL_CREATE,
            'permissions_object': 'project'
        }), ('spatial.view', {
            'description': _("View an existing spatial unit"),
            'error_message': messages.SPATIAL_VIEW
        }), ('spatial.update', {
            'description': _("Update an existing spatial unit"),
            'error_message': messages.SPATIAL_UPDATE
        }), ('spatial.delete', {
            'description': _("Delete an existing spatial unit"),
            'error_message': messages.SPATIAL_DELETE
        }), ('spatial.resources.add', {
            'description': _("Add resources to this spatial unit"),
            'error_message': messages.SPATIAL_ADD_RESOURCE
        }))

    def __str__(self):
        return "<SpatialUnit: {}>".format(self.name)

    def __repr__(self):
        return str(self)

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

    @property
    def ui_class_name(self):
        return _("Location")

    @property
    def ui_detail_url(self):
        return reverse(
            'locations:detail',
            kwargs={
                'organization': self.project.organization.slug,
                'project': self.project.slug,
                'location': self.id,
            },
        )
示例#13
0
class ST_LineSubstring(Func):
    function = 'ST_LineSubstring'
    output_field = GeometryField()
class Zaak(APIMixin, models.Model):
    """
    Modelleer de structuur van een ZAAK.

    Een samenhangende hoeveelheid werk met een welgedefinieerde aanleiding
    en een welgedefinieerd eindresultaat, waarvan kwaliteit en doorlooptijd
    bewaakt moeten worden.
    """
    uuid = models.UUIDField(unique=True,
                            default=uuid.uuid4,
                            help_text="Unieke resource identifier (UUID4)")
    identificatie = models.CharField(
        max_length=40,
        blank=True,
        help_text='De unieke identificatie van de ZAAK binnen de organisatie '
        'die verantwoordelijk is voor de behandeling van de ZAAK.',
        validators=[alphanumeric_excluding_diacritic])
    bronorganisatie = RSINField(
        help_text='Het RSIN van de Niet-natuurlijk persoon zijnde de '
        'organisatie die de zaak heeft gecreeerd. Dit moet een geldig '
        'RSIN zijn van 9 nummers en voldoen aan '
        'https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef')
    omschrijving = models.CharField(
        max_length=80,
        blank=True,
        help_text='Een korte omschrijving van de zaak.')
    zaaktype = models.URLField(
        help_text="URL naar het zaaktype in de CATALOGUS waar deze voorkomt")
    registratiedatum = models.DateField(
        help_text='De datum waarop de zaakbehandelende organisatie de ZAAK '
        'heeft geregistreerd. Indien deze niet opgegeven wordt, '
        'wordt de datum van vandaag gebruikt.',
        default=date.today)
    verantwoordelijke_organisatie = RSINField(
        help_text=
        'Het RSIN van de Niet-natuurlijk persoon zijnde de organisatie '
        'die eindverantwoordelijk is voor de behandeling van de '
        'zaak. Dit moet een geldig RSIN zijn van 9 nummers en voldoen aan '
        'https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef')

    startdatum = models.DateField(
        help_text='De datum waarop met de uitvoering van de zaak is gestart')
    einddatum = models.DateField(
        blank=True,
        null=True,
        help_text='De datum waarop de uitvoering van de zaak afgerond is.',
    )
    einddatum_gepland = models.DateField(
        blank=True,
        null=True,
        help_text='De datum waarop volgens de planning verwacht wordt dat de '
        'zaak afgerond wordt.',
    )
    uiterlijke_einddatum_afdoening = models.DateField(
        blank=True,
        null=True,
        help_text='De laatste datum waarop volgens wet- en regelgeving de zaak '
        'afgerond dient te zijn.')

    toelichting = models.TextField(max_length=1000,
                                   blank=True,
                                   help_text='Een toelichting op de zaak.')
    zaakgeometrie = GeometryField(
        blank=True,
        null=True,
        help_text="Punt, lijn of (multi-)vlak geometrie-informatie.")

    class Meta:
        verbose_name = 'zaak'
        verbose_name_plural = 'zaken'
        unique_together = ('bronorganisatie', 'identificatie')

    def __str__(self):
        return self.identificatie

    def save(self, *args, **kwargs):
        if not self.identificatie:
            self.identificatie = str(uuid.uuid4())
        super().save(*args, **kwargs)

    @property
    def current_status_uuid(self):
        status = self.status_set.order_by('-datum_status_gezet').first()
        return status.uuid if status else None
示例#15
0
class Record(TaggedModel, AttachableModel, LinkableModel):
    name = models.CharField(max_length=255)
    date_collected = models.DateField()
    published = models.CharField(
        max_length=55,
        choices=[('published', 'Published'), ('draft', 'Draft')],
        default='draft'
    )
    description = models.TextField(blank=True)
    feature = models.ForeignKey(Feature, on_delete=models.SET_NULL, blank=True, null=True, related_name='records')
    medium = models.CharField(
        choices=[
            ('lake_sediment', 'Lake Sediment'),
            ('marine_sediment', 'Marine Sediment'),
            ('glacier_ice', 'Glacier Ice'),
            ('peat', 'Peat'),
            ('lake_water', 'Lake Water'),
            ('river_water', 'River Water'),
            ('ocean_water', 'Ocean Water'),
            ('wood', 'Wood'),
            ('surficial_sediment', 'Surficial sediment'),
            ('rock', 'Rock'),
            ('mollusk_shell', 'Mollusk shell'),
            ('coral', 'Coral'),
            ('speleothem', 'Speleothem'),
            ('sclerosponge', 'Sclerosponge'),
            ('air', 'Air'),
            ('hybrid', 'Hybrid'),
            ('other', 'Other')
        ],
        max_length=55
    )
    type = models.CharField(
        choices=[
            ('samples', 'Samples'),
            ('core', 'Core'),
            ('section', 'Section'),
            ('sensor', 'Sensor'),
            ('other', 'Other')
        ],
        max_length=55
    )
    resolution = models.FloatField(blank=True, null=True, default=None)
    min_year = models.FloatField(blank=True, null=True, default=None)
    max_year = models.FloatField(blank=True, null=True, default=None)
    position_units = models.CharField(max_length=55, blank=True, default='cm')

    geometry = GeometryField(blank=True, null=True)
    geo_elev = models.FloatField(default=0)
    geo_error = models.FloatField(default=0)

    modified = models.DateTimeField('modified', auto_now=True)
    created = models.DateTimeField('created', auto_now_add=True)

    class Meta:
        ordering = ['-modified']

    def save(self, *args, **kwargs):
        # keep the list of people who published this record synced
        # sort by earliest publication
        if not self.pk:
            super().save(*args, **kwargs)

        self.record_authorships.filter(role='published').delete()
        all_people = []
        for ref in self.record_uses.order_by('publication__year'):
            for authorship in ref.publication.authorships.filter(role='author').order_by('order'):
                if authorship.person not in all_people:
                    all_people.append(authorship.person)

        for i, person in enumerate(all_people):
            RecordAuthorship.objects.create(
                record=self,
                person=person,
                role='published',
                order=20 + i
            )

        # saving at the end ensures the reversion signal gets sent
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name
示例#16
0
class Zaak(APIMixin, models.Model):
    """
    Modelleer de structuur van een ZAAK.

    Een samenhangende hoeveelheid werk met een welgedefinieerde aanleiding
    en een welgedefinieerd eindresultaat, waarvan kwaliteit en doorlooptijd
    bewaakt moeten worden.
    """
    uuid = models.UUIDField(unique=True,
                            default=uuid.uuid4,
                            help_text="Unieke resource identifier (UUID4)")

    # Relate 'is_deelzaak_van'
    # De relatie vanuit een zaak mag niet verwijzen naar
    # dezelfde zaak d.w.z. moet verwijzen naar een andere
    # zaak. Die andere zaak mag geen relatie ?is deelzaak
    # van? hebben (d.w.z. deelzaken van deelzaken worden
    # niet ondersteund).
    hoofdzaak = models.ForeignKey(
        'self',
        limit_choices_to={'hoofdzaak__isnull': True},
        null=True,
        blank=True,
        on_delete=models.CASCADE,
        related_name='deelzaken',
        verbose_name='is deelzaak van',
        help_text=_("De verwijzing naar de ZAAK, waarom verzocht is door de "
                    "initiator daarvan, die behandeld wordt in twee of meer "
                    "separate ZAAKen waarvan de onderhavige ZAAK er één is."))

    identificatie = models.CharField(
        max_length=40,
        blank=True,
        help_text='De unieke identificatie van de ZAAK binnen de organisatie '
        'die verantwoordelijk is voor de behandeling van de ZAAK.',
        validators=[alphanumeric_excluding_diacritic])
    bronorganisatie = RSINField(
        help_text='Het RSIN van de Niet-natuurlijk persoon zijnde de '
        'organisatie die de zaak heeft gecreeerd. Dit moet een geldig '
        'RSIN zijn van 9 nummers en voldoen aan '
        'https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef')
    omschrijving = models.CharField(
        max_length=80,
        blank=True,
        help_text='Een korte omschrijving van de zaak.')
    toelichting = models.TextField(max_length=1000,
                                   blank=True,
                                   help_text='Een toelichting op de zaak.')
    zaaktype = models.URLField(
        _("zaaktype"),
        help_text="URL naar het zaaktype in de CATALOGUS waar deze voorkomt",
        max_length=1000)
    registratiedatum = models.DateField(
        help_text='De datum waarop de zaakbehandelende organisatie de ZAAK '
        'heeft geregistreerd. Indien deze niet opgegeven wordt, '
        'wordt de datum van vandaag gebruikt.',
        default=date.today)
    verantwoordelijke_organisatie = RSINField(
        help_text=
        'Het RSIN van de Niet-natuurlijk persoon zijnde de organisatie '
        'die eindverantwoordelijk is voor de behandeling van de '
        'zaak. Dit moet een geldig RSIN zijn van 9 nummers en voldoen aan '
        'https://nl.wikipedia.org/wiki/Burgerservicenummer#11-proef')

    startdatum = models.DateField(
        help_text='De datum waarop met de uitvoering van de zaak is gestart')
    einddatum = models.DateField(
        blank=True,
        null=True,
        help_text='De datum waarop de uitvoering van de zaak afgerond is.',
    )
    einddatum_gepland = models.DateField(
        blank=True,
        null=True,
        help_text='De datum waarop volgens de planning verwacht wordt dat de '
        'zaak afgerond wordt.',
    )
    uiterlijke_einddatum_afdoening = models.DateField(
        blank=True,
        null=True,
        help_text='De laatste datum waarop volgens wet- en regelgeving de zaak '
        'afgerond dient te zijn.')
    publicatiedatum = models.DateField(
        _("publicatiedatum"),
        null=True,
        blank=True,
        help_text=_(
            "Datum waarop (het starten van) de zaak gepubliceerd is of wordt.")
    )

    producten_of_diensten = ArrayField(
        models.URLField(_("URL naar product/dienst"), max_length=1000),
        default=list,
        help_text=_(
            "De producten en/of diensten die door de zaak worden voortgebracht. "
            "Dit zijn URLs naar de resources zoals die door de producten- "
            "en dienstencatalogus-API wordt ontsloten. "
            "De producten/diensten moeten bij het zaaktype vermeld zijn."))

    communicatiekanaal = models.URLField(
        _("communicatiekanaal"),
        blank=True,
        max_length=1000,
        help_text=
        _("Het medium waarlangs de aanleiding om een zaak te starten is ontvangen. "
          "URL naar een communicatiekanaal in de VNG-Referentielijst van communicatiekanalen."
          ))

    vertrouwelijkheidaanduiding = VertrouwelijkheidsAanduidingField(
        _("vertrouwlijkheidaanduiding"),
        help_text=
        _("Aanduiding van de mate waarin het zaakdossier van de ZAAK voor de openbaarheid bestemd is."
          ))

    betalingsindicatie = models.CharField(
        _("betalingsindicatie"),
        max_length=20,
        blank=True,
        choices=BetalingsIndicatie.choices,
        help_text=_("Indicatie of de, met behandeling van de zaak gemoeide, "
                    "kosten betaald zijn door de desbetreffende betrokkene."))
    laatste_betaaldatum = models.DateTimeField(
        _("laatste betaaldatum"),
        blank=True,
        null=True,
        help_text=_(
            "De datum waarop de meest recente betaling is verwerkt "
            "van kosten die gemoeid zijn met behandeling van de zaak."))

    zaakgeometrie = GeometryField(
        blank=True,
        null=True,
        help_text="Punt, lijn of (multi-)vlak geometrie-informatie.")

    verlenging_reden = models.CharField(
        _("reden verlenging"),
        max_length=200,
        blank=True,
        help_text=
        _("Omschrijving van de reden voor het verlengen van de behandeling van de zaak."
          ))
    verlenging_duur = DaysDurationField(
        _("duur verlenging"),
        blank=True,
        null=True,
        help_text=_(
            "Het aantal werkbare dagen waarmee de doorlooptijd van de "
            "behandeling van de ZAAK is verlengd (of verkort) ten opzichte "
            "van de eerder gecommuniceerde doorlooptijd."))
    verlenging = GegevensGroepType({
        'reden': verlenging_reden,
        'duur': verlenging_duur,
    })

    opschorting_indicatie = models.BooleanField(
        _("indicatie opschorting"),
        default=False,
        help_text=_(
            "Aanduiding of de behandeling van de ZAAK tijdelijk is opgeschort."
        ))
    opschorting_reden = models.CharField(
        _("reden opschorting"),
        max_length=200,
        blank=True,
        help_text=
        _("Omschrijving van de reden voor het opschorten van de behandeling van de zaak."
          ))
    opschorting = GegevensGroepType({
        'indicatie': opschorting_indicatie,
        'reden': opschorting_reden,
    })

    selectielijstklasse = models.URLField(
        _("selectielijstklasse"),
        blank=True,
        max_length=1000,
        help_text=
        _("URL-referentie naar de categorie in de gehanteerde 'Selectielijst Archiefbescheiden' die, gezien "
          "het zaaktype en het resultaattype van de zaak, bepalend is voor het archiefregime van de zaak."
          ))

    relevante_andere_zaken = ArrayField(models.URLField(
        _("URL naar andere zaak"), max_length=1000),
                                        blank=True,
                                        default=list)

    # Archiving
    archiefnominatie = models.CharField(
        _("archiefnominatie"),
        max_length=40,
        null=True,
        blank=True,
        choices=Archiefnominatie.choices,
        help_text=
        _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden."
          ))
    archiefstatus = models.CharField(
        _("archiefstatus"),
        max_length=40,
        choices=Archiefstatus.choices,
        default=Archiefstatus.nog_te_archiveren,
        help_text=
        _("Aanduiding of het zaakdossier blijvend bewaard of na een bepaalde termijn vernietigd moet worden."
          ))
    archiefactiedatum = models.DateField(
        _("archiefactiedatum"),
        null=True,
        blank=True,
        help_text=
        _("De datum waarop het gearchiveerde zaakdossier vernietigd moet worden dan wel overgebracht moet "
          "worden naar een archiefbewaarplaats. Wordt automatisch berekend bij het aanmaken of wijzigen van "
          "een RESULTAAT aan deze ZAAK indien nog leeg."))

    class Meta:
        verbose_name = 'zaak'
        verbose_name_plural = 'zaken'
        unique_together = ('bronorganisatie', 'identificatie')

    def __str__(self):
        return self.identificatie

    def save(self, *args, **kwargs):
        if not self.identificatie:
            self.identificatie = str(uuid.uuid4())

        if self.betalingsindicatie == BetalingsIndicatie.nvt and self.laatste_betaaldatum:
            self.laatste_betaaldatum = None

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

    @property
    def current_status_uuid(self):
        status = self.status_set.order_by('-datum_status_gezet').first()
        return status.uuid if status else None

    def get_brondatum(self,
                      afleidingswijze: str,
                      datum_kenmerk: str = None,
                      objecttype: str = None,
                      procestermijn: str = None) -> date:
        """
        To calculate the Archiefactiedatum, we first need the "brondatum" which is like the start date of the storage
        period.

        :param afleidingswijze:
            One of the `Afleidingswijze` choices.
        :param datum_kenmerk:
            A `string` representing an arbitrary attribute name. Currently only needed when `afleidingswijze` is
            `eigenschap` or `zaakobject`.
        :param objecttype:
            A `string` representing an arbitrary objecttype name. Currently only needed when `afleidingswijze` is
            `zaakobject`.
        :param procestermijn:
            A `string` representing an ISO8601 period that is considered the process term of the Zaak. Currently only
            needed when `afleidingswijze` is `termijn`.
        :return:
            A specific date that marks the start of the storage period, or `None`.
        """
        if afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.afgehandeld:
            return self.einddatum

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.hoofdzaak:
            # TODO: Document that hoofdzaak can not an external zaak
            return self.hoofdzaak.einddatum if self.hoofdzaak else None

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.eigenschap:
            if not datum_kenmerk:
                raise DetermineProcessEndDateException(
                    _('Geen datumkenmerk aanwezig om de eigenschap te achterhalen voor het bepalen van de brondatum.'
                      ))

            eigenschap = self.zaakeigenschap_set.filter(
                _naam=datum_kenmerk).first()
            if eigenschap:
                if not eigenschap.waarde:
                    return None

                try:
                    return parse_isodatetime(eigenschap.waarde).date()
                except ValueError:
                    raise DetermineProcessEndDateException(
                        _('Geen geldige datumwaarde in eigenschap "{}": {}').
                        format(datum_kenmerk, eigenschap.waarde))
            else:
                raise DetermineProcessEndDateException(
                    _('Geen eigenschap gevonden die overeenkomt met het datumkenmerk "{}" voor het bepalen van de '
                      'brondatum.').format(datum_kenmerk))

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.ander_datumkenmerk:
            # The brondatum, and therefore the archiefactiedatum, needs to be determined manually.
            return None

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.zaakobject:
            if not objecttype:
                raise DetermineProcessEndDateException(
                    _('Geen objecttype aanwezig om het zaakobject te achterhalen voor het bepalen van de brondatum.'
                      ))
            if not datum_kenmerk:
                raise DetermineProcessEndDateException(
                    _('Geen datumkenmerk aanwezig om het attribuut van het zaakobject te achterhalen voor het bepalen '
                      'van de brondatum.'))

            for zaak_object in self.zaakobject_set.filter(
                    object_type=objecttype):
                object = zaak_object._get_object()
                if datum_kenmerk in object:
                    try:
                        return parse_isodatetime(object[datum_kenmerk]).date()
                    except ValueError:
                        raise DetermineProcessEndDateException(
                            _('Geen geldige datumwaarde in attribuut "{}": {}'
                              ).format(datum_kenmerk, object[datum_kenmerk]))

            raise DetermineProcessEndDateException(
                _('Geen attribuut gevonden die overeenkomt met het datumkenmerk "{}" voor het bepalen van de '
                  'brondatum.').format(datum_kenmerk))

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.termijn:
            if self.einddatum is None:
                # TODO: Not sure if we should raise an error instead.
                return None
            if procestermijn is None:
                raise DetermineProcessEndDateException(
                    _('Geen procestermijn aanwezig voor het bepalen van de brondatum.'
                      ))
            try:
                return self.einddatum + isodate.parse_duration(procestermijn)
            except (ValueError, TypeError) as e:
                raise DetermineProcessEndDateException(
                    _('Geen geldige periode in procestermijn: {}').format(
                        procestermijn))

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.gerelateerde_zaak:
            # TODO: Determine what this means...
            raise NotImplementedError

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.ingangsdatum_besluit:
            # TODO: Relation from Zaak to Besluit is not implemented yet...
            raise NotImplementedError

        elif afleidingswijze == BrondatumArchiefprocedureAfleidingswijze.vervaldatum_besluit:
            # TODO: Relation from Zaak to Besluit is not implemented yet...
            raise NotImplementedError

        raise ValueError(f'Onbekende "Afleidingswijze": {afleidingswijze}')