Пример #1
0
class ZipCode(models.Model):
    code = models.CharField(max_length=5, unique=True)
    bounds = GeometryField(geography=True)
    center = PointField()

    def __str__(self):
        return self.code
Пример #2
0
class ApiarySiteOnApproval(models.Model):
    apiary_site = models.ForeignKey('ApiarySite', )
    approval = models.ForeignKey('Approval', )
    available = models.BooleanField(default=False)
    site_status = models.CharField(default=SITE_STATUS_CURRENT,
                                   max_length=20,
                                   db_index=True)
    # site_available = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)
    wkb_geometry = PointField(srid=4326, blank=True,
                              null=True)  # store approved coordinates
    site_category = models.ForeignKey(
        'SiteCategory',
        null=True,
        blank=True,
    )
    objects = GeoManager()

    def __str__(self):
        return 'id:{}: (apiary_site: {}, approval: {})'.format(
            self.id, self.apiary_site.id, self.approval.id)

    class Meta:
        app_label = 'disturbance'
        unique_together = [
            'apiary_site',
            'approval',
        ]
Пример #3
0
class Station(models.Model):
    code = models.CharField(max_length=4, unique=True)
    name = models.CharField(max_length=200)
    state = models.CharField(max_length=2)
    point = PointField()
    xml_url = models.CharField(max_length=200)
    last_fetched = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return self.name

    def fetch(self):
        r = requests.get(self.xml_url, timeout=5)
        doc = drill.parse(r.text)
        self.last_fetched = timezone.now()
        self.save(update_fields=('last_fetched', ))
        return self.weather.create(data=doc.json(),
                                   date_fetched=self.last_fetched)

    def latest(self):
        if self.last_fetched and (timezone.now() - self.last_fetched
                                  ) < datetime.timedelta(minutes=15):
            return self.weather.latest('date_fetched')
        else:
            return self.fetch()
Пример #4
0
class Occurrence(models.Model):
    geom = PointField()
    version = models.IntegerField(default=0)
    released_versions = models.IntegerField(default=0)
    occurrence_cat = models.ForeignKey(OccurrenceCategory, on_delete=models.SET_NULL, blank=True, null=True)
    released = models.BooleanField(default=False)
    verified = models.BooleanField(default=False)
    inclusion_date = models.DateTimeField(default=timezone.now)
    observation = models.OneToOneField(OccurrenceObservation, on_delete=models.CASCADE)
    class Meta:
        abstract = True
Пример #5
0
class Location(QuicksellModel):
    """Object's physical location."""

    coordinates = PointField(unique=True)
    address = TextField(max_length=1024)

    @classmethod
    def default_pk(cls):
        return cls.objects.get_or_create(coordinates=Point(x=55.751426,
                                                           y=37.618879),
                                         address="The Kremlin")[0].id
Пример #6
0
class Presence(CommonUserdata):
    class Meta:
        verbose_name = 'Гео-данные'
        verbose_name_plural = 'Гео-данные'

    def __str__(self):
        return u'{0} --- {1} {2}'.format(
            self.time_start.strftime('%Y-%m-%d %H:%M %Z'),
            self.time_end.strftime('%Y-%m-%d %H:%M %Z'), self.point.coords)

    point = PointField('Гео-точка')
Пример #7
0
class Place(models.Model):
    name = models.CharField(max_length=200)
    state = models.CharField(max_length=2)
    point = PointField()
    zipcode = models.ForeignKey(ZipCode,
                                related_name='places',
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL)

    def __str__(self):
        return self.name
Пример #8
0
class Station(models.Model):
    id = models.IntegerField(editable=False)
    location_label = models.CharField(editable=False)
    station_code = models.CharField(editable=False)
    station_label = models.CharField(editable=False)
    code = models.CharField(editable=False)
    parameter_id = models.IntegerField(editable=False)
    geo = PointField(editable=False, srid=4326)

    class Meta:
        managed = False
        db_table = 'measurements_station_geo'
Пример #9
0
class Migration(migrations.Migration):

    dependencies = [("aira", "0021_lint")]

    operations = [
        migrations.AddField(model_name="agrifield",
                            name="location",
                            field=PointField(null=True)),
        migrations.AlterField(model_name="agrifield",
                              name="latitude",
                              field=FloatField(null=True)),
        migrations.AlterField(model_name="agrifield",
                              name="longitude",
                              field=FloatField(null=True)),
        migrations.RunPython(latlon2point, point2latlon),
        migrations.RemoveField(model_name="agrifield", name="latitude"),
        migrations.RemoveField(model_name="agrifield", name="longitude"),
        migrations.AlterField(model_name="agrifield",
                              name="location",
                              field=PointField(null=False)),
    ]
Пример #10
0
class LocativeModel(TimeStampedModel):
    point = PointField()
    altitude = models.FloatField(blank=True, null=True)
    accuracy = models.FloatField(blank=True, null=True)
    altitude_accuracy = models.FloatField(blank=True, null=True)

    objects = GeoManager()

    class Meta(TimeStampedModel.Meta):
        abstract = True

    def latlon():  #@NoSelf
        doc = """Returns the latitude/longitude pair as a dict for the API, etc."""  #@UnusedVariable

        def fget(self):
            #return self.point.geojson
            return {'lat': self.point.y, 'lon': self.point.x}

        return locals()

    latlon = property(**latlon())
Пример #11
0
class Salon(models.Model):
    name = models.CharField(max_length=255, null=True)
    timezone = TimeZoneField(default=settings.TIME_ZONE)
    address = models.CharField(max_length=255)
    city = models.CharField(max_length=64, null=True, blank=True)
    state = models.CharField(max_length=25, null=True, blank=True)
    country = models.CharField(max_length=25, null=True, blank=True)
    zip_code = models.CharField(max_length=16, null=True, blank=True)
    location = PointField(geography=True, null=True, blank=True)
    is_address_geocoded = models.BooleanField(default=False)
    public_phone = models.CharField(max_length=20, blank=True, null=True, default=None)
    last_geo_coded = models.DateTimeField(blank=True, null=True, default=None)

    class Meta:
        db_table = 'salon'

    def __str__(self) -> str:
        if self.name is not None:
            return '{0} ({1})'.format(self.name, self.get_full_address())
        return '[No name] ({0})'.format(self.get_full_address())

    def get_full_address(self) -> str:
        # TODO: change this to proper address generation
        return self.address

    def geo_code_address(self):
        geo_coded_address = GeocodeValidAddress(self.address).geo_code()
        if geo_coded_address:
            self.city = geo_coded_address.city
            self.state = geo_coded_address.state
            self.zip_code = geo_coded_address.zip_code
            self.location = geo_coded_address.location
            self.country = geo_coded_address.country
            self.is_address_geocoded = True
            logger.info('Geo-coding Success', exc_info=True)
        else:
            logger.info("Geo-coding returned None")
        self.last_geo_coded = timezone.now()
        self.save(update_fields=['city', 'state', 'zip_code',
                                 'location', 'country', 'is_address_geocoded', 'last_geo_coded'])
Пример #12
0
class Place(models.Model):
    objects = PlaceManager()

    title = models.TextField()
    latlng = PointField()

    start_date = models.OneToOneField(ExtendedDate,
                                      null=True,
                                      blank=True,
                                      on_delete=models.SET_NULL,
                                      related_name='place_start_date')

    is_ended = models.BooleanField(
        default=True,
        help_text="Is the site or archive still at this location?")
    end_date = models.OneToOneField(ExtendedDate,
                                    null=True,
                                    blank=True,
                                    on_delete=models.SET_NULL,
                                    related_name='place_end_date')

    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

    def latitude(self):
        return self.latlng.coords[1]

    def longitude(self):
        return self.latlng.coords[0]

    def match_string(self, latlng):
        s = '{},{}'.format(self.latitude(), self.longitude())
        return s == latlng

    def get_absolute_url(self):
        return reverse('place-detail-view', kwargs={'pk': self.id})
Пример #13
0
class Migration(migrations.Migration):
    dependencies = [
        ('parkings', '0024_permitlookupitem_index'),
    ]

    operations = [
        migrations.CreateModel(
            name='ParkingCheck',
            fields=[
                ('id', models.AutoField(
                    auto_created=True, primary_key=True,
                    serialize=False, verbose_name='ID')),
                ('created_at', models.DateTimeField(
                    auto_now_add=True, db_index=True, verbose_name='time created')),
                ('time', models.DateTimeField(verbose_name='time')),
                ('time_overridden', models.BooleanField(
                    verbose_name='time was overridden')),
                ('registration_number', models.CharField(
                    max_length=20, verbose_name='registration number')),
                ('location', PointField(
                    srid=4326, verbose_name='location')),
                ('result', JSONField(
                    blank=True,
                    encoder=DjangoJSONEncoder,
                    verbose_name='result')),
                ('allowed', models.BooleanField(
                    verbose_name='parking was allowed')),
                ('found_parking', models.ForeignKey(
                    to='parkings.Parking', on_delete=models.SET_NULL,
                    blank=True, null=True, verbose_name='found parking')),
            ],
            options={
                'ordering': ('-created_at', '-id'),
                'verbose_name': 'parking check',
                'verbose_name_plural': 'parking checks'
            },
        ),
    ]
Пример #14
0
class Location(models.Model):
    event = models.OneToOneField(
        Event,
        on_delete=models.CASCADE,
    )

    # NOTE: Coorindates are stored as (lng, lat)
    # This is done to match DeckGL's convention
    point = PointField(blank=True, null=True)
    polygon = PolygonField(blank=True, null=True)

    @property
    def lng_lat(self):
        return [self.point.coords[0], self.point.coords[1]
                ] if self.point else None

    class Meta:
        constraints = [
            models.CheckConstraint(
                name="value_either_point_or_polygon",
                check=(models.Q(point__isnull=False, polygon__isnull=True)
                       | models.Q(point__isnull=True, polygon__isnull=False)))
        ]
Пример #15
0
class Institution(models.Model):
    objects = InstitutionManager()

    external_id = models.PositiveIntegerField(unique=True)
    title = models.TextField(unique=True)
    latlng = PointField()

    address = models.TextField(null=True, blank=True)
    city = models.TextField()
    state = models.CharField(max_length=2)

    website_url = models.URLField()
    image = models.URLField(null=True, blank=True)
    admissions_url = models.URLField(null=True, blank=True)

    private = models.BooleanField()

    student_population = models.PositiveIntegerField()
    undergraduate_population = models.PositiveIntegerField()

    undergrad_vet_population = models.PositiveIntegerField(null=True)
    undergrad_vet_graduation_rate = models.PositiveIntegerField(null=True)
    grad_vet_population = models.PositiveIntegerField(null=True)
    grad_vet_graduation_rate = models.PositiveIntegerField(null=True)

    two_year_program = models.BooleanField(null=True)
    four_year_program = models.BooleanField(null=True)

    accredited = models.BooleanField()
    accreditation_type = models.CharField(
        null=True, max_length=1, choices=ACCREDITATION_CHOICES)
    regional_accreditor = models.TextField(null=True, blank=True)

    standardized_test_required = models.BooleanField()
    standardized_test_notes = models.TextField(null=True, blank=True)

    vet_center = models.BooleanField()
    sva_chapter = models.BooleanField()

    clep_credits_accepted = models.BooleanField()
    jst_credits_accepted = models.BooleanField(null=True, blank=True)
    dsst_credits_accepted = models.BooleanField()
    online_credits_accepted = models.BooleanField()
    application_fee_waived = models.BooleanField()

    vet_grants_scholarships = models.BooleanField()
    vet_grants_scholarships_notes = models.TextField(null=True, blank=True)

    yellow_ribbon = models.BooleanField()
    yellow_ribbon_slots = models.TextField(null=True, blank=True)
    yellow_ribbon_contribution = models.TextField(null=True, blank=True)

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

    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['title']

    def __str__(self):
        return self.title

    def set_accreditation_type(self, value):
        self.accreditation_type = None

        value = value.lower()
        for s in ACCREDITATION_CHOICES:
            if value == s[1]:
                self.accreditation_type = s[0]

    def get_institution_type(self):
        return 'Private' if self.private else 'Public'

    def set_institution_type(self, value):
        self.private = 'private' in value.lower()

    def set_program_duration(self, value):
        value = value.lower()
        self.two_year_program = '2 year' in value
        self.four_year_program = '4 year' in value

    def set_latitude(self, value):
        if not self.latlng:
            self.latlng = Point(float(value), 0)
        else:
            self.latlng = Point(float(value), self.latlng.coords[1])

    def set_longitude(self, value):
        if not self.latlng:
            self.latlng = Point(0, float(value))
        else:
            self.latlng = Point(self.latlng.coords[0], float(value))
Пример #16
0
class Instance(models.Model):
    """A series of answers by an individual for a specific form"""

    UPLOADED_TO = "instances/"

    STATUS_READY = "READY"
    STATUS_DUPLICATED = "DUPLICATED"
    STATUS_EXPORTED = "EXPORTED"

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    uuid = models.TextField(null=True, blank=True)
    export_id = models.TextField(null=True,
                                 blank=True,
                                 default=generate_id_for_dhis_2)
    correlation_id = models.BigIntegerField(null=True, blank=True)
    name = models.TextField(null=True, blank=True)
    file = models.FileField(upload_to=UPLOADED_TO, null=True, blank=True)
    file_name = models.TextField(null=True, blank=True)
    location = PointField(null=True, blank=True, dim=3, srid=4326)
    org_unit = models.ForeignKey("OrgUnit",
                                 on_delete=models.DO_NOTHING,
                                 null=True,
                                 blank=True)
    form = models.ForeignKey("Form",
                             on_delete=models.PROTECT,
                             null=True,
                             blank=True,
                             related_name="instances")
    project = models.ForeignKey("Project",
                                blank=True,
                                null=True,
                                on_delete=models.DO_NOTHING)
    json = JSONField(null=True, blank=True)
    accuracy = models.DecimalField(null=True, decimal_places=2, max_digits=7)
    device = models.ForeignKey("Device",
                               null=True,
                               blank=True,
                               on_delete=models.DO_NOTHING)
    period = models.TextField(null=True, blank=True, db_index=True)

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

    objects = InstanceQuerySet.as_manager()

    deleted = models.BooleanField(default=False)

    def convert_location_from_field(self, field_name=None):
        f = field_name
        if f is None:
            f = self.form.location_field

        if self.json and f:
            location = self.json.get(f, None)
            if location:
                latitude, longitude, altitude, accuracy = [
                    float(x) for x in location.split(" ")
                ]
                self.location = Point(x=longitude,
                                      y=latitude,
                                      z=altitude,
                                      srid=4326)
                self.accuracy = accuracy
                self.save()

    def convert_device(self):
        if self.json and not self.device:
            device_field = self.form.device_field
            if not device_field:
                device_field = "deviceid"
            imei = self.json.get(device_field, None)
            if imei is not None:
                device, created = Device.objects.get_or_create(imei=imei)
                self.device = device
                self.save()
                if self.project:
                    self.device.projects.add(self.project)

    def convert_correlation(self):
        if not self.correlation_id:
            identifier = str(self.id)
            if self.form.correlation_field is not None and self.json:
                identifier += self.json.get(self.form.correlation_field, None)
                identifier = identifier.zfill(3)
            random_number = random.choice("1234567890")
            value = int(identifier + random_number)
            suffix = "{:02d}".format(value % 97)
            self.correlation_id = identifier + random_number + suffix
            self.save()

    def get_and_save_json_of_xml(self):
        if self.json:
            file_content = self.json
        elif self.file:
            if "amazonaws" in self.file.url:
                file = urlopen(self.file.url)
            else:
                file = self.file
            soup = as_soup(file)
            form_version_id = extract_form_version_id(soup)
            if form_version_id:
                form_versions = self.form.form_versions.filter(
                    version_id=form_version_id)
                form_version = form_versions.first()
                if form_version:

                    self.json = flat_parse_xml_soup(
                        soup,
                        [rg["name"] for rg in form_version.repeat_groups()])
                else:
                    # warn old form, but keep it working ? or throw error
                    self.json = flat_parse_xml_soup(soup, [])
            else:
                self.json = flat_parse_xml_soup(soup, [])
            file_content = self.json
            self.save()
        else:
            file_content = {}
        return file_content

    def get_form_version(self):
        json = self.get_and_save_json_of_xml()

        try:
            return self.form.form_versions.get(version_id=json["_version"])
        except (KeyError, FormVersion.DoesNotExist):
            return None

    def export(self, launcher=None):
        from iaso.dhis2.datavalue_exporter import DataValueExporter
        from iaso.dhis2.export_request_builder import ExportRequestBuilder, NothingToExportError

        try:
            export_request = ExportRequestBuilder().build_export_request(
                filters={"instance_id": self.id}, launcher=None)

            DataValueExporter().export_instances(export_request, True)
        except NothingToExportError as error:
            print("Export failed for instance", self)

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

    def as_dict(self):
        file_content = self.get_and_save_json_of_xml()

        return {
            "uuid":
            self.uuid,
            "export_id":
            self.export_id,
            "file_name":
            self.file_name,
            "file_content":
            file_content,
            "file_url":
            self.file.url if self.file else None,
            "id":
            self.id,
            "form_id":
            self.form_id,
            "created_at":
            self.created_at.timestamp() if self.created_at else None,
            "updated_at":
            self.updated_at.timestamp() if self.updated_at else None,
            "org_unit":
            self.org_unit.as_dict(
                with_groups=False) if self.org_unit else None,
            "latitude":
            self.location.y if self.location else None,
            "longitude":
            self.location.x if self.location else None,
            "altitude":
            self.location.z if self.location else None,
            "period":
            self.period,
            "status":
            getattr(self, "status", None),
            "correlation_id":
            self.correlation_id,
        }

    def as_dict_with_parents(self):
        file_content = self.get_and_save_json_of_xml()
        return {
            "uuid": self.uuid,
            "export_id": self.export_id,
            "file_name": self.file_name,
            "file_content": file_content,
            "file_url": self.file.url if self.file else None,
            "id": self.id,
            "form_id": self.form_id,
            "created_at":
            self.created_at.timestamp() if self.created_at else None,
            "updated_at":
            self.updated_at.timestamp() if self.updated_at else None,
            "org_unit":
            self.org_unit.as_dict_with_parents() if self.org_unit else None,
            "latitude": self.location.y if self.location else None,
            "longitude": self.location.x if self.location else None,
            "altitude": self.location.z if self.location else None,
            "period": self.period,
            "status": getattr(self, "status", None),
            "correlation_id": self.correlation_id,
        }

    def as_full_model(self):
        file_content = self.get_and_save_json_of_xml()
        form_version = self.get_form_version()

        return {
            "uuid":
            self.uuid,
            "id":
            self.id,
            "device_id":
            self.device.imei if self.device else None,
            "file_name":
            self.file_name,
            "file_url":
            self.file.url if self.file else None,
            "form_id":
            self.form_id,
            "form_name":
            self.form.name,
            "form_descriptor":
            form_version.get_or_save_form_descriptor()
            if form_version is not None else None,
            "created_at":
            self.created_at.timestamp() if self.created_at else None,
            "updated_at":
            self.updated_at.timestamp() if self.updated_at else None,
            "org_unit":
            self.org_unit.as_dict_with_parents(
                light=False, light_parents=False) if self.org_unit else None,
            "latitude":
            self.location.y if self.location else None,
            "longitude":
            self.location.x if self.location else None,
            "altitude":
            self.location.z if self.location else None,
            "period":
            self.period,
            "file_content":
            file_content,
            "files": [
                f.file.url if f.file else None
                for f in self.instancefile_set.all()
            ],
            "status":
            getattr(self, "status", None),
            "correlation_id":
            self.correlation_id,
            "last_export_success_at":
            self.last_export_success_at.timestamp()
            if self.last_export_success_at else None,
            "export_statuses": [{
                "status":
                export_status.status,
                "created_at":
                export_status.created_at.timestamp()
                if export_status.created_at else None,
                "export_request": {
                    "launcher": {
                        "full_name":
                        export_status.export_request.launcher.get_full_name()
                        if export_status.export_request.launcher else
                        "AUTO UPLOAD",
                        "email":
                        export_status.export_request.launcher.email
                        if export_status.export_request.launcher else
                        "AUTO UPLOAD",
                    },
                    "last_error_message":
                    f"{export_status.last_error_message}, {export_status.export_request.last_error_message}",
                },
            } for export_status in Paginator(
                self.exportstatus_set.order_by("-id"), 3).object_list],
            "deleted":
            self.deleted,
        }

    def as_small_dict(self):
        return {
            "id":
            self.id,
            "file_url":
            self.file.url if self.file else None,
            "created_at":
            self.created_at.timestamp() if self.created_at else None,
            "updated_at":
            self.updated_at.timestamp() if self.updated_at else None,
            "period":
            self.period,
            "latitude":
            self.location.y if self.location else None,
            "longitude":
            self.location.x if self.location else None,
            "altitude":
            self.location.z if self.location else None,
            "files": [
                f.file.url if f.file else None
                for f in self.instancefile_set.all()
            ],
            "status":
            getattr(self, "status", None),
            "correlation_id":
            self.correlation_id,
        }

    def soft_delete(self, user: typing.Optional[User] = None):
        with transaction.atomic():
            original = copy(self)
            self.deleted = True
            self.save()
            log_modification(original, self, INSTANCE_API, user=user)
class Migration(migrations.Migration):

    initial = True

    dependencies = [("people", "0001_initial")]

    operations = [
        migrations.CreateModel(
            name="Calendar",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                (
                    "nb_id",
                    models.IntegerField(
                        blank=True,
                        help_text=
                        "L'identifiant de la ressource correspondante sur NationBuilder, si importé.",
                        null=True,
                        unique=True,
                        verbose_name="ID sur NationBuilder",
                    ),
                ),
                (
                    "label",
                    models.CharField(max_length=50,
                                     unique=True,
                                     verbose_name="nom"),
                ),
                (
                    "description",
                    models.TextField(blank=True, verbose_name="description"),
                ),
            ],
            options={"verbose_name": "agenda"},
        ),
        migrations.CreateModel(
            name="Event",
            fields=[
                (
                    "created",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="created",
                    ),
                ),
                (
                    "modified",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="modified",
                    ),
                ),
                (
                    "id",
                    models.UUIDField(
                        default=uuid.uuid4,
                        help_text=
                        "UUID interne à l'API pour identifier la ressource",
                        primary_key=True,
                        serialize=False,
                        verbose_name="UUID",
                    ),
                ),
                (
                    "nb_id",
                    models.IntegerField(
                        blank=True,
                        help_text=
                        "L'identifiant de la ressource correspondante sur NationBuilder, si importé.",
                        null=True,
                        unique=True,
                        verbose_name="ID sur NationBuilder",
                    ),
                ),
                (
                    "coordinates",
                    PointField(
                        blank=True,
                        geography=True,
                        null=True,
                        srid=4326,
                        verbose_name="coordonnées",
                    ),
                ),
                (
                    "location_name",
                    models.CharField(blank=True,
                                     max_length=255,
                                     verbose_name="nom du lieu"),
                ),
                (
                    "location_address",
                    models.CharField(blank=True,
                                     max_length=255,
                                     verbose_name="adresse complète"),
                ),
                (
                    "location_address1",
                    models.CharField(blank=True,
                                     max_length=100,
                                     verbose_name="adresse (1ère ligne)"),
                ),
                (
                    "location_address2",
                    models.CharField(blank=True,
                                     max_length=100,
                                     verbose_name="adresse (2ème ligne)"),
                ),
                (
                    "location_city",
                    models.CharField(blank=True,
                                     max_length=100,
                                     verbose_name="ville"),
                ),
                (
                    "location_zip",
                    models.CharField(blank=True,
                                     max_length=20,
                                     verbose_name="code postal"),
                ),
                (
                    "location_state",
                    models.CharField(blank=True,
                                     max_length=40,
                                     verbose_name="état"),
                ),
                (
                    "location_country",
                    django_countries.fields.CountryField(blank=True,
                                                         max_length=2,
                                                         verbose_name="pays"),
                ),
                (
                    "contact_name",
                    models.CharField(blank=True,
                                     max_length=255,
                                     verbose_name="nom du contact"),
                ),
                (
                    "contact_email",
                    models.EmailField(
                        blank=True,
                        max_length=254,
                        verbose_name="adresse email du contact",
                    ),
                ),
                (
                    "contact_phone",
                    models.CharField(
                        blank=True,
                        max_length=30,
                        verbose_name="numéro de téléphone du contact",
                    ),
                ),
                (
                    "name",
                    models.CharField(
                        help_text="Le nom du groupe de l'événement",
                        max_length=255,
                        verbose_name="nom",
                    ),
                ),
                (
                    "description",
                    models.TextField(
                        blank=True,
                        help_text="Une description de l'événement, en MarkDown",
                        verbose_name="description",
                    ),
                ),
                (
                    "nb_path",
                    models.CharField(blank=True,
                                     max_length=255,
                                     verbose_name="NationBuilder path"),
                ),
                (
                    "start_time",
                    models.DateTimeField(
                        verbose_name="date et heure de début"),
                ),
                ("end_time",
                 models.DateTimeField(verbose_name="date et heure de fin")),
            ],
            options={
                "verbose_name":
                "événement",
                "verbose_name_plural":
                "événements",
                "ordering": ("-start_time", "-end_time"),
                "permissions":
                (("every_event", "Peut éditer tous les événements"), ),
            },
        ),
        migrations.CreateModel(
            name="EventTag",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                (
                    "label",
                    models.CharField(max_length=50,
                                     unique=True,
                                     verbose_name="nom"),
                ),
                (
                    "description",
                    models.TextField(blank=True, verbose_name="description"),
                ),
            ],
            options={"verbose_name": "tag"},
        ),
        migrations.CreateModel(
            name="RSVP",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                (
                    "created",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="created",
                    ),
                ),
                (
                    "modified",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="modified",
                    ),
                ),
                (
                    "guests",
                    models.PositiveIntegerField(
                        default=0,
                        verbose_name="nombre d'invités supplémentaires"),
                ),
                ("canceled",
                 models.BooleanField(default=False, verbose_name="Annulé")),
                (
                    "event",
                    models.ForeignKey(
                        editable=False,
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="rsvps",
                        to="events.Event",
                    ),
                ),
                (
                    "person",
                    models.ForeignKey(
                        editable=False,
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="rsvps",
                        to="people.Person",
                    ),
                ),
            ],
            options={
                "verbose_name": "RSVP",
                "verbose_name_plural": "RSVP"
            },
        ),
        migrations.AddField(
            model_name="event",
            name="attendees",
            field=models.ManyToManyField(related_name="events",
                                         through="events.RSVP",
                                         to="people.Person"),
        ),
        migrations.AddField(
            model_name="event",
            name="calendar",
            field=models.ForeignKey(
                on_delete=django.db.models.deletion.CASCADE,
                related_name="events",
                to="events.Calendar",
            ),
        ),
        migrations.AddField(
            model_name="event",
            name="organizers",
            field=models.ManyToManyField(related_name="organized_events",
                                         to="people.Person"),
        ),
        migrations.AddField(
            model_name="event",
            name="tags",
            field=models.ManyToManyField(blank=True,
                                         related_name="events",
                                         to="events.EventTag"),
        ),
        migrations.AlterUniqueTogether(name="rsvp",
                                       unique_together={("event", "person")}),
        migrations.AddIndex(
            model_name="event",
            index=models.Index(fields=["start_time", "end_time"],
                               name="events_even_start_t_0e446b_idx"),
        ),
        migrations.AddIndex(
            model_name="event",
            index=models.Index(fields=["nb_path"],
                               name="events_even_nb_path_2e6795_idx"),
        ),
    ]
Пример #18
0
class ArchivalCollectionSuggestion(models.Model):
    person = models.TextField(verbose_name="Your Name")
    person_title = models.TextField(verbose_name="Your Title")
    email = models.EmailField(verbose_name="Your Email Address")

    repository_title = models.TextField()
    collection_title = models.TextField()

    latlng = PointField()
    title = models.TextField(verbose_name="Address")

    description = models.TextField(null=True, blank=True)
    finding_aid_url = models.URLField(null=True, blank=True)
    linear_feet = models.FloatField(null=True, blank=True)
    record_format = models.ManyToManyField(ArchivalRecordFormat, blank=True)

    inclusive_start = models.OneToOneField(ExtendedDate,
                                           null=True,
                                           blank=True,
                                           on_delete=models.SET_NULL,
                                           related_name='suggestion_start')
    inclusive_end = models.OneToOneField(ExtendedDate,
                                         null=True,
                                         blank=True,
                                         on_delete=models.SET_NULL,
                                         related_name='suggestion_end')

    archival_collection = models.ForeignKey(ArchivalCollection,
                                            null=True,
                                            blank=True,
                                            on_delete=models.SET_NULL)

    created_at = models.DateTimeField(auto_now_add=True)
    modified_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['repository_title', 'collection_title']

    def get_or_create_repository(self):
        try:
            repo = ArchivalRepository.objects.get(title=self.repository_title)
        except ArchivalRepository.DoesNotExist:
            (place, created) = Place.objects.get_or_create(title=self.title,
                                                           latlng=self.latlng,
                                                           start_date=None,
                                                           end_date=None)
            repo = ArchivalRepository.objects.create(
                place=place, title=self.repository_title)
        return repo

    def get_or_create_collection(self, repo):
        try:
            collection = ArchivalCollection.objects.get(
                collection_title=self.collection_title, repository=repo)
        except ArchivalCollection.DoesNotExist:
            collection = ArchivalCollection.objects.create(
                repository=repo,
                collection_title=self.collection_title,
                description=self.description,
                finding_aid_url=self.finding_aid_url,
                linear_feet=self.linear_feet,
                inclusive_start=self.inclusive_start,
                inclusive_end=self.inclusive_end)

        return collection

    def add_notes(self, collection):
        notes = 'Suggested by {}, {}'.format(self.person, self.person_title)
        if collection.notes:
            collection.notes = collection.notes + ' ' + notes
        else:
            collection.notes = notes
        collection.save()

    def convert_to_archival_collection(self):
        if self.archival_collection is not None:
            return self.archival_collection

        repo = self.get_or_create_repository()
        collection = self.get_or_create_collection(repo)
        self.add_notes(collection)

        self.archival_collection = collection
        self.save()

        return collection
Пример #19
0
class OrgUnit(models.Model):
    VALIDATION_NEW = "NEW"
    VALIDATION_VALID = "VALID"
    VALIDATION_REJECTED = "REJECTED"

    VALIDATION_STATUS_CHOICES = (
        (VALIDATION_NEW, _("new")),
        (VALIDATION_VALID, _("valid")),
        (VALIDATION_REJECTED, _("rejected")),
    )

    name = models.CharField(max_length=255)
    uuid = models.TextField(null=True, blank=True, db_index=True)
    custom = models.BooleanField(default=False)
    validated = models.BooleanField(
        default=True, db_index=True)  # TO DO : remove in a later migration
    validation_status = models.CharField(max_length=25,
                                         choices=VALIDATION_STATUS_CHOICES,
                                         default=VALIDATION_NEW)
    version = models.ForeignKey("SourceVersion",
                                null=True,
                                blank=True,
                                on_delete=models.CASCADE)
    parent = models.ForeignKey("OrgUnit",
                               on_delete=models.CASCADE,
                               null=True,
                               blank=True)
    path = PathField(null=True, blank=True, unique=True)
    aliases = ArrayField(CITextField(max_length=255, blank=True),
                         size=100,
                         null=True,
                         blank=True)

    org_unit_type = models.ForeignKey(OrgUnitType,
                                      on_delete=models.CASCADE,
                                      null=True,
                                      blank=True)

    sub_source = models.TextField(
        null=True,
        blank=True)  # sometimes, in a given source, there are sub sources
    source_ref = models.TextField(null=True, blank=True, db_index=True)
    geom = MultiPolygonField(null=True, blank=True, srid=4326, geography=True)
    simplified_geom = MultiPolygonField(null=True,
                                        blank=True,
                                        srid=4326,
                                        geography=True)
    catchment = MultiPolygonField(null=True,
                                  blank=True,
                                  srid=4326,
                                  geography=True)
    geom_ref = models.IntegerField(null=True, blank=True)

    gps_source = models.TextField(null=True, blank=True)
    location = PointField(null=True,
                          blank=True,
                          geography=True,
                          dim=3,
                          srid=4326)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    creator = models.ForeignKey(User,
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL)

    objects = OrgUnitManager.from_queryset(OrgUnitQuerySet)()

    class Meta:
        indexes = [GistIndex(fields=["path"], buffering=True)]

    def save(self, *args, skip_calculate_path: bool = False, **kwargs):
        """Override default save() to make sure that the path property is calculated and saved,
        for this org unit and its children.

        :param skip_calculate_path: use with caution - can be useful in scripts where the extra transactions
                                    would be a burden, but the path needs to be set afterwards
        """

        if skip_calculate_path:
            super().save(*args, **kwargs)
        else:
            with transaction.atomic():
                super().save(*args, **kwargs)
                OrgUnit.objects.bulk_update(self.calculate_paths(), ["path"])

    def calculate_paths(self,
                        force_recalculate: bool = False
                        ) -> typing.List["OrgUnit"]:
        """Calculate the path for this org unit and all its children.

        This method will check if this org unit path should change. If it is the case (or if force_recalculate is
        True), it will update the path property for the org unit and its children, and return all the modified
        records.

        Please note that this method does not save the modified records. Instead, they are updated in bulk in the
        save() method.

        :param force_recalculate: calculate path for all descendants, even if this org unit path does not change
        """

        # For now, we will skip org units that have a parent without a path.
        # The idea is that a management command (set_org_unit_path) will handle the initial seeding of the
        # path field, starting at the top of the pyramid. Once this script has been run and the field is filled for
        # all org units, this should not happen anymore.
        # TODO: remove condition below
        if self.parent is not None and self.parent.path is None:
            return []

        # keep track of updated records
        updated_records = []

        base_path = [] if self.parent is None else list(self.parent.path)
        new_path = [*base_path, str(self.pk)]
        path_has_changed = new_path != self.path

        if path_has_changed:
            self.path = new_path
            updated_records += [self]

        if path_has_changed or force_recalculate:
            for child in self.orgunit_set.all():
                updated_records += child.calculate_paths(force_recalculate)

        return updated_records

    def __str__(self):
        return "%s %s %d" % (self.org_unit_type, self.name,
                             self.id if self.id else -1)

    def as_dict_for_mobile_lite(self):
        return {
            "n": self.name,
            "id": self.id,
            "p": self.parent_id,
            "out": self.org_unit_type_id,
            "c_a": self.created_at.timestamp() if self.created_at else None,
            "lat": self.location.y if self.location else None,
            "lon": self.location.x if self.location else None,
            "alt": self.location.z if self.location else None,
        }

    def as_dict_for_mobile(self):
        return {
            "name":
            self.name,
            "id":
            self.id,
            "parent_id":
            self.parent_id,
            "org_unit_type_id":
            self.org_unit_type_id,
            "org_unit_type_name":
            self.org_unit_type.name if self.org_unit_type else None,
            "validation_status":
            self.validation_status if self.org_unit_type else None,
            "created_at":
            self.created_at.timestamp() if self.created_at else None,
            "updated_at":
            self.updated_at.timestamp() if self.updated_at else None,
            "latitude":
            self.location.y if self.location else None,
            "longitude":
            self.location.x if self.location else None,
            "altitude":
            self.location.z if self.location else None,
        }

    def as_dict(self, with_groups=True):
        res = {
            "name":
            self.name,
            "short_name":
            self.name,
            "id":
            self.id,
            "source":
            self.version.data_source.name if self.version else None,
            "source_ref":
            self.source_ref,
            "parent_id":
            self.parent_id,
            "org_unit_type_id":
            self.org_unit_type_id,
            "org_unit_type_name":
            self.org_unit_type.name if self.org_unit_type else None,
            "created_at":
            self.created_at.timestamp() if self.created_at else None,
            "updated_at":
            self.updated_at.timestamp() if self.updated_at else None,
            "aliases":
            self.aliases,
            "validation_status":
            self.validation_status,
            "latitude":
            self.location.y if self.location else None,
            "longitude":
            self.location.x if self.location else None,
            "altitude":
            self.location.z if self.location else None,
            "has_geo_json":
            True if self.simplified_geom else False,
            "version":
            self.version.number if self.version else None,
        }

        if hasattr(self, "search_index"):
            res["search_index"] = self.search_index
        return res

    def as_dict_with_parents(self, light=False, light_parents=True):
        res = {
            "name":
            self.name,
            "short_name":
            self.name,
            "id":
            self.id,
            "sub_source":
            self.sub_source,
            "sub_source_id":
            self.sub_source,
            "source_ref":
            self.source_ref,
            "source_url":
            self.version.data_source.credentials.url
            if self.version and self.version.data_source
            and self.version.data_source.credentials else None,
            "parent_id":
            self.parent_id,
            "validation_status":
            self.validation_status,
            "parent_name":
            self.parent.name if self.parent else None,
            "parent":
            self.parent.as_dict_with_parents(light=light_parents,
                                             light_parents=light_parents)
            if self.parent else None,
            "org_unit_type_id":
            self.org_unit_type_id,
            "created_at":
            self.created_at.timestamp() if self.created_at else None,
            "updated_at":
            self.updated_at.timestamp() if self.updated_at else None,
            "aliases":
            self.aliases,
            "latitude":
            self.location.y if self.location else None,
            "longitude":
            self.location.x if self.location else None,
            "altitude":
            self.location.z if self.location else None,
            "has_geo_json":
            True if self.simplified_geom else False,
        }
        if not light:  # avoiding joins here
            res["groups"] = [
                group.as_dict(with_counts=False)
                for group in self.groups.all()
            ]
            res["org_unit_type_name"] = self.org_unit_type.name if self.org_unit_type else None
            res["org_unit_type"] = self.org_unit_type.as_dict(
            ) if self.org_unit_type else None
            res["source"] = self.version.data_source.name if self.version else None
            res["source_id"] = self.version.data_source.id if self.version else None
            res["version"] = self.version.number if self.version else None
        if hasattr(self, "search_index"):
            res["search_index"] = self.search_index
        return res

    def as_small_dict(self):
        res = {
            "name":
            self.name,
            "id":
            self.id,
            "parent_id":
            self.parent_id,
            "validation_status":
            self.validation_status,
            "parent_name":
            self.parent.name if self.parent else None,
            "source":
            self.version.data_source.name if self.version else None,
            "source_ref":
            self.source_ref,
            "parent":
            self.parent.as_small_dict() if self.parent else None,
            "org_unit_type_name":
            self.org_unit_type.name if self.org_unit_type else None,
        }
        if hasattr(self, "search_index"):
            res["search_index"] = self.search_index
        return res

    def as_dict_for_csv(self):
        return {
            "name": self.name,
            "id": self.id,
            "source_ref": self.source_ref,
            "parent_id": self.parent_id,
            "org_unit_type": self.org_unit_type.name,
        }

    def as_location(self):
        res = {
            "id":
            self.id,
            "name":
            self.name,
            "short_name":
            self.name,
            "latitude":
            self.location.y if self.location else None,
            "longitude":
            self.location.x if self.location else None,
            "altitude":
            self.location.z if self.location else None,
            "has_geo_json":
            True if self.simplified_geom else False,
            "org_unit_type":
            self.org_unit_type.name if self.org_unit_type else None,
            "org_unit_type_depth":
            self.org_unit_type.depth if self.org_unit_type else None,
            "source_id":
            self.version.data_source.id if self.version else None,
            "source_name":
            self.version.data_source.name if self.version else None,
        }
        if hasattr(self, "search_index"):
            res["search_index"] = self.search_index
        return res

    def source_path(self):
        """DHIS2-friendly path built using source refs"""

        path_components = []
        cur = self
        while cur:
            if cur.source_ref:
                path_components.insert(0, cur.source_ref)
            cur = cur.parent
        if len(path_components) > 0:
            return "/" + ("/".join(path_components))
        return None
class Migration(migrations.Migration):

    initial = True

    dependencies = [("people", "0029_person_mandates")]

    operations = [
        migrations.CreateModel(
            name="Payment",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                (
                    "created",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="created",
                    ),
                ),
                (
                    "modified",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="modified",
                    ),
                ),
                (
                    "coordinates",
                    PointField(
                        blank=True,
                        geography=True,
                        null=True,
                        srid=4326,
                        verbose_name="coordonnées",
                    ),
                ),
                (
                    "coordinates_type",
                    models.PositiveSmallIntegerField(
                        choices=[
                            (0, "Coordonnées manuelles"),
                            (10, "Coordonnées automatiques précises"),
                            (
                                20,
                                "Coordonnées automatiques approximatives (niveau rue)",
                            ),
                            (30,
                             "Coordonnées automatiques approximatives (ville)"
                             ),
                            (50,
                             "Coordonnées automatiques (qualité inconnue)"),
                            (255, "Coordonnées introuvables"),
                        ],
                        editable=False,
                        help_text=
                        "Comment les coordonnées ci-dessus ont-elle été acquises",
                        null=True,
                        verbose_name="type de coordonnées",
                    ),
                ),
                (
                    "location_name",
                    models.CharField(blank=True,
                                     max_length=255,
                                     verbose_name="nom du lieu"),
                ),
                (
                    "location_address1",
                    models.CharField(blank=True,
                                     max_length=100,
                                     verbose_name="adresse (1ère ligne)"),
                ),
                (
                    "location_address2",
                    models.CharField(blank=True,
                                     max_length=100,
                                     verbose_name="adresse (2ème ligne)"),
                ),
                (
                    "location_city",
                    models.CharField(blank=True,
                                     max_length=100,
                                     verbose_name="ville"),
                ),
                (
                    "location_zip",
                    models.CharField(blank=True,
                                     max_length=20,
                                     verbose_name="code postal"),
                ),
                (
                    "location_state",
                    models.CharField(blank=True,
                                     max_length=40,
                                     verbose_name="état"),
                ),
                (
                    "location_country",
                    django_countries.fields.CountryField(blank=True,
                                                         max_length=2,
                                                         verbose_name="pays"),
                ),
                (
                    "location_address",
                    models.CharField(
                        blank=True,
                        help_text=
                        "L'adresse telle qu'elle a éventuellement été copiée depuis NationBuilder. Ne plus utiliser.",
                        max_length=255,
                        verbose_name="adresse complète",
                    ),
                ),
                ("email",
                 models.EmailField(max_length=255, verbose_name="email")),
                ("first_name",
                 models.CharField(max_length=255, verbose_name="prénom")),
                (
                    "last_name",
                    models.CharField(max_length=255,
                                     verbose_name="nom de famille"),
                ),
                (
                    "type",
                    models.CharField(
                        choices=[
                            ("don", "don"),
                            ("inscription à un événement", "événement"),
                        ],
                        max_length=255,
                        verbose_name="type",
                    ),
                ),
                ("price",
                 models.IntegerField(verbose_name="prix en centimes d'euros")),
                (
                    "status",
                    models.IntegerField(
                        choices=[
                            (0, "En attente"),
                            (1, "Terminé"),
                            (2, "Abandonné"),
                            (3, "Annulé"),
                            (4, "Refusé"),
                        ],
                        default=0,
                        verbose_name="status",
                    ),
                ),
                (
                    "meta",
                    django.contrib.postgres.fields.jsonb.JSONField(
                        blank=True, default=dict),
                ),
                (
                    "systempay_responses",
                    django.contrib.postgres.fields.jsonb.JSONField(
                        blank=True, default=list),
                ),
                (
                    "person",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="people.Person"),
                ),
            ],
            options={"abstract": False},
        )
    ]
Пример #21
0
class Migration(migrations.Migration):

    initial = True

    dependencies = [migrations.swappable_dependency(settings.AUTH_USER_MODEL)]

    operations = [
        migrations.CreateModel(
            name="Person",
            fields=[
                (
                    "created",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="created",
                    ),
                ),
                (
                    "modified",
                    models.DateTimeField(
                        default=django.utils.timezone.now,
                        editable=False,
                        verbose_name="modified",
                    ),
                ),
                (
                    "id",
                    models.UUIDField(
                        default=uuid.uuid4,
                        help_text="UUID interne à l'API pour identifier la ressource",
                        primary_key=True,
                        serialize=False,
                        verbose_name="UUID",
                    ),
                ),
                (
                    "nb_id",
                    models.IntegerField(
                        blank=True,
                        help_text="L'identifiant de la ressource correspondante sur NationBuilder, si importé.",
                        null=True,
                        unique=True,
                        verbose_name="ID sur NationBuilder",
                    ),
                ),
                (
                    "coordinates",
                    PointField(
                        blank=True,
                        geography=True,
                        null=True,
                        srid=4326,
                        verbose_name="coordonnées",
                    ),
                ),
                (
                    "location_name",
                    models.CharField(
                        blank=True, max_length=255, verbose_name="nom du lieu"
                    ),
                ),
                (
                    "location_address",
                    models.CharField(
                        blank=True, max_length=255, verbose_name="adresse complète"
                    ),
                ),
                (
                    "location_address1",
                    models.CharField(
                        blank=True, max_length=100, verbose_name="adresse (1ère ligne)"
                    ),
                ),
                (
                    "location_address2",
                    models.CharField(
                        blank=True, max_length=100, verbose_name="adresse (2ème ligne)"
                    ),
                ),
                (
                    "location_city",
                    models.CharField(blank=True, max_length=100, verbose_name="ville"),
                ),
                (
                    "location_zip",
                    models.CharField(
                        blank=True, max_length=20, verbose_name="code postal"
                    ),
                ),
                (
                    "location_state",
                    models.CharField(blank=True, max_length=40, verbose_name="état"),
                ),
                (
                    "location_country",
                    django_countries.fields.CountryField(
                        blank=True, max_length=2, verbose_name="pays"
                    ),
                ),
                (
                    "email",
                    models.EmailField(
                        help_text="L'adresse email de la personne, utilisée comme identifiant",
                        max_length=254,
                        unique=True,
                        verbose_name="adresse email",
                    ),
                ),
                (
                    "subscribed",
                    models.BooleanField(
                        default=True,
                        help_text="L'utilisateur souhaite-t-il recevoir les newsletters ?",
                        verbose_name="inscrit à la newsletter",
                    ),
                ),
                (
                    "first_name",
                    models.CharField(blank=True, max_length=255, verbose_name="prénom"),
                ),
                (
                    "last_name",
                    models.CharField(
                        blank=True, max_length=255, verbose_name="nom de famille"
                    ),
                ),
                (
                    "bounced",
                    models.BooleanField(
                        default=False,
                        help_text="Indique que des mails envoyés ont été rejetés par le serveur distant",
                        verbose_name="email rejeté",
                    ),
                ),
                (
                    "bounced_date",
                    models.DateTimeField(
                        blank=True,
                        help_text="Si des mails ont été rejetés, indique la date du dernier rejet",
                        null=True,
                        verbose_name="date de rejet de l'email",
                    ),
                ),
                (
                    "role",
                    models.OneToOneField(
                        on_delete=django.db.models.deletion.PROTECT,
                        related_name="person",
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
            ],
            options={
                "verbose_name": "personne",
                "verbose_name_plural": "personnes",
                "ordering": ("-created",),
                "default_permissions": ("add", "change", "delete", "view"),
            },
        ),
        migrations.CreateModel(
            name="PersonTag",
            fields=[
                (
                    "id",
                    models.AutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                (
                    "label",
                    models.CharField(max_length=50, unique=True, verbose_name="nom"),
                ),
                (
                    "description",
                    models.TextField(blank=True, verbose_name="description"),
                ),
            ],
            options={"verbose_name": "tag"},
        ),
        migrations.AddField(
            model_name="person",
            name="tags",
            field=models.ManyToManyField(
                blank=True, related_name="people", to="people.PersonTag"
            ),
        ),
    ]