示例#1
0
 def test_closing_shop_gives_all_employees_salary(self, shop, admin_user):
     shop2 = ShopFactory()
     employee1 = Employment.objects.create(user=admin_user,
                                           shop=shop,
                                           role=EmployeeRoleChoices.CASHIER,
                                           timespan=DateRange(
                                               date(2020, 12, 1)))
     employee2 = Employment.objects.create(user=admin_user,
                                           shop=shop,
                                           role=EmployeeRoleChoices.CASHIER,
                                           timespan=DateRange(
                                               date(2020, 12, 1),
                                               date(2021, 1, 1)))
     employee3 = Employment.objects.create(user=admin_user,
                                           shop=shop2,
                                           role=EmployeeRoleChoices.CASHIER,
                                           timespan=DateRange(
                                               date(2020, 12, 1)))
     shop.close()
     employee1_salary = Salary.objects.get()  # only single salary
     assert employee1_salary.value == 2000
     assert employee1_salary.when == timezone.now().date()
     employee1.refresh_from_db()
     employee2.refresh_from_db()
     employee3.refresh_from_db()
     assert employee1.timespan.upper == timezone.now().date()
     assert employee2.timespan.upper == date(2021, 1, 1)
     assert employee3.timespan.upper is None
示例#2
0
    def get_changeform_initial_data(self, request):
        """Permet de préremplir le champs `dates' en fonction de la dernière élection"""
        initial = super().get_changeform_initial_data(request)
        initial.setdefault("dates", self.default_date_range)

        if "person" in request.GET:
            try:
                initial["person"] = Person.objects.get(
                    pk=request.GET["person"])
            except Person.DoesNotExist:
                pass

        if "conseil" in request.GET:
            try:
                initial["conseil"] = self.get_conseil_queryset(request).get(
                    code=request.GET["conseil"])
            except ObjectDoesNotExist:
                pass

        if "debut" in request.GET and "fin" in request.GET:
            try:
                initial["dates"] = DateRange(
                    datetime.strptime(request.GET["debut"], "%Y-%m-%d").date(),
                    datetime.strptime(request.GET["fin"], "%Y-%m-%d").date(),
                )
            except ValueError:
                pass

        return initial
class MandatDeputeEuropeen(MandatAbstrait):
    DEFAULT_DATE_RANGE = DateRange(date(2019, 6, 1), date(2024, 5, 31))

    person = models.ForeignKey(
        "people.Person",
        verbose_name="Élu",
        on_delete=models.CASCADE,
        related_name="mandats_depute_europeen",
        related_query_name="mandat_depute_depute_europeen",
    )

    reference = models.ForeignKey(
        "data_france.DeputeEuropeen",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name="Référence dans les données du RNE",
        help_text=
        "La fiche correspondant à cet élu dans le répertoire national des élus",
        related_name="elus",
        related_query_name="elu",
    )

    def titre_complet(self, conseil_avant=False):
        return genrer(self.person.gender, "député européen",
                      "députée européenne", "député")

    class Meta:
        verbose_name = "Mandat de député⋅e européen⋅ne"
        verbose_name_plural = "Mandats de député⋅e européen⋅ne"
        ordering = ("person", )
 def test_extending_when_there_is_a_newer_sub(self):
     now = timezone.now().date() + timedelta(days=10)
     Subscription.objects.create(range=DateRange(now, None),
                                 plan_id=1,
                                 admin=self.admin)
     response = self.client.post(self.url)
     self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
     self.assertIn(b'not a newest one', response.content)
示例#5
0
class TestDateRangeField(FieldValues):
    """
    Values for `ListField` with CharField as child.
    """
    serializer_class = DateRangeSerializer

    valid_inputs = [
        ({'lower': '2001-01-01',
          'upper': '2001-02-02',
          'bounds': '[)'},
         DateRange(
             **{'lower': datetime.date(2001, 1, 1),
                'upper': datetime.date(2001, 2, 2),
                'bounds': '[)'})),
        ({'upper': '2001-02-02',
          'bounds': '[)'},
         DateRange(
             **{'upper': datetime.date(2001, 2, 2),
                'bounds': '[)'})),
        ({'lower': '2001-01-01',
          'bounds': '[)'},
         DateRange(
             **{'lower': datetime.date(2001, 1, 1),
                'bounds': '[)'})),
        ({'empty': True},
         DateRange(**{'empty': True})),
        ({}, DateRange()),
    ]
    invalid_inputs = [
        ({'lower': 'a'}, ['Date has wrong format. Use one of these'
                          ' formats instead: '
                          'YYYY-MM-DD.']),
        ('not a dict', ['Expected a dictionary of items but got type "str".']),
    ]
    outputs = [
        (DateRange(
            **{'lower': datetime.date(2001, 1, 1),
               'upper': datetime.date(2001, 2, 2)}),
            {'lower': '2001-01-01',
             'upper': '2001-02-02',
             'bounds': '[)'}),
        (DateRange(**{'empty': True}),
         {'empty': True}),
        (DateRange(), {'bounds': '[)', 'lower': None, 'upper': None}),
    ]
    field = DateRangeField()

    def test_no_source_on_child(self):
        with pytest.raises(AssertionError) as exc_info:
            DateRangeField(child=serializers.IntegerField(source='other'))

        assert str(exc_info.value) == (
            "The `source` argument is not meaningful when applied to a `child=` field. "
            "Remove `source=` from the field declaration."
        )
示例#6
0
def test_convert_old_acl_date_earlier_inc():
    rulestr = "( date <= 01.01.2015 )"
    access_rules = convert_old_acl(rulestr)
    assert len(access_rules) == 1
    assert_access_rule_with_flags(access_rules[0],
                                  invert=False,
                                  blocking=False,
                                  dateranges=[
                                      DateRange(datetime.date(1, 1, 1),
                                                datetime.date(2015, 1, 1),
                                                '(]')
                                  ])
示例#7
0
def test_convert_old_acl_date_later():
    rulestr = "( date > 01.01.2015 )"
    access_rules = convert_old_acl(rulestr)
    assert len(access_rules) == 1
    assert_access_rule_with_flags(access_rules[0],
                                  invert=False,
                                  blocking=False,
                                  dateranges=[
                                      DateRange(datetime.date(2015, 1, 1),
                                                datetime.date(9999, 12, 31),
                                                '()')
                                  ])
示例#8
0
def test_convert_old_acl_group_and_date():
    rulestr = "( group x ) AND ( date > 01.01.2015 )"
    access_rules = convert_old_acl(rulestr)
    assert len(access_rules) == 1
    assert_access_rule_with_flags(access_rules[0],
                                  invert=False,
                                  blocking=False,
                                  group_ids=[99990000],
                                  dateranges=[
                                      DateRange(datetime.date(2015, 1, 1),
                                                datetime.date(9999, 12, 31),
                                                '()')
                                  ])
示例#9
0
    def _get_billing_cycle_number(self, billing_cycle):
        """Gets the 1-indexed number of the billing cycle relative to the provided billing cycle"""
        begins_before_initial_date = billing_cycle.date_range.lower < self.initial_billing_cycle.date_range.lower
        if begins_before_initial_date:
            raise ProvidedBillingCycleBeginsBeforeInitialBillingCycle(
                '{} precedes initial cycle {}'.format(
                    billing_cycle, self.initial_billing_cycle))

        billing_cycle_number = BillingCycle.objects.filter(
            date_range__contained_by=DateRange(
                self.initial_billing_cycle.date_range.lower,
                billing_cycle.date_range.upper,
                bounds='[]',
            ), ).count()

        return billing_cycle_number
示例#10
0
class MandatDepute(MandatAbstrait):
    DEFAULT_DATE_RANGE = DateRange(date(2017, 7, 1), date(2022, 6, 30))

    person = models.ForeignKey(
        "people.Person",
        verbose_name="Élu",
        on_delete=models.CASCADE,
        related_name="mandats_deputes",
        related_query_name="mandat_depute",
    )

    conseil = models.ForeignKey(
        "data_france.CirconscriptionLegislative",
        verbose_name="Circonscription",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )

    reference = models.ForeignKey(
        "data_france.Depute",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name="Référence dans les données AN",
        help_text="La fiche correspondant à cet élu dans la base de l'AN",
        related_name="elus",
        related_query_name="elu",
    )

    def titre_complet(self, conseil_avant=False):
        titre = genrer(self.person.gender, "député⋅e")

        if not self.conseil:
            circo = "circonscription inconnue"
        else:
            circo = str(self.conseil)

        if conseil_avant:
            return f"{circo}, {titre}"

        return f"{titre} de la {circo}"

    class Meta:
        verbose_name = "Mandat de député⋅e"
        verbose_name_plural = "Mandats de député⋅e"
        ordering = ("person", "conseil")
示例#11
0
class Migration(migrations.Migration):

    dependencies = [
        ('common', '0035_additionalagreement_language'),
    ]

    operations = [
        migrations.AddField(
            model_name='additionalagreement',
            name='active_date_range',
            field=django.contrib.postgres.fields.ranges.DateRangeField(
                default=DateRange(datetime.date(2017, 2, 15),
                                  datetime.date(2017, 4, 13))),
            preserve_default=False,
        ),
        migrations.AlterUniqueTogether(
            name='additionalagreement',
            unique_together=set([('cinema', 'film', 'active_from', 'dimension',
                                  'language')]),
        ),
    ]
示例#12
0
    def extend_builder_plan(self, request, *args, **kwargs):
        admin = self.get_object()
        serializer = self.get_serializer(data=request.data)

        if serializer.is_valid():
            try:
                subscription = Profile.get_active_subscription(admin.id)
            except Subscription.DoesNotExist:
                return Response({'detail': 'No active subscription.'}, status=status.HTTP_400_BAD_REQUEST)

            if Subscription.objects.filter(admin=admin, range__gt=subscription.range).exists():
                return Response({'detail': 'Current subscription is not a newest one.'},
                                status=status.HTTP_400_BAD_REQUEST)

            if subscription.plan.name != 'builder':
                return Response({'detail': 'Current subscription is not on a builder plan.'},
                                status=status.HTTP_400_BAD_REQUEST)

            now = timezone.now().date()
            subscription.range = DateRange(subscription.start, now + timedelta(days=serializer.data['days']))
            subscription.save()
            return Response(SubscriptionSerializer(subscription, context=self.get_serializer_context()).data,
                            status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
# Crée une contrainte d'exclusion qui empêche l'existence de deux mandats pour les mêmes
# personnes et conseils si leurs plages de dates se chevauchent (l'opérateur && exprime
# l'intersection)
AJOUTER_EXCLUSION = """
CREATE EXTENSION IF NOT EXISTS btree_gist;
ALTER TABLE {table}
ADD CONSTRAINT {table}_unique_within_dates
EXCLUDE USING gist (person_id WITH =, conseil_id WITH =, dates WITH &&);
"""

RETIRER_EXCLUSION = """
ALTER TABLE {table} DROP CONSTRAINT {table}_unique_within_dates;
"""

# faux défaut
default_date_range = DateRange(lower=datetime.date(2000, 1, 1),
                               upper=datetime.date(2000, 1, 2))


class Migration(migrations.Migration):

    dependencies = [
        ("elus", "0007_nettoyage_champs"),
    ]

    operations = [
        migrations.RunSQL(sql=CREER_EXTENSION_BTREE_GIST,
                          reverse_sql=migrations.RunSQL.noop),
        migrations.AddField(
            model_name="mandatdepartemental",
            name="dates",
            field=django.contrib.postgres.fields.ranges.DateRangeField(
示例#14
0
class MandatMunicipal(MandatAbstrait):
    DEFAULT_DATE_RANGE = DateRange(date(2020, 6, 28), date(2026, 3, 31))

    MANDAT_INCONNU = "INC"
    MANDAT_CONSEILLER_MAJORITE = "MAJ"
    MANDAT_CONSEILLER_OPPOSITION = "OPP"
    MANDAT_MAIRE = "MAI"
    MANDAT_MAIRE_ADJOINT = "ADJ"
    MANDAT_MAIRE_DA = "MDA"

    MANDAT_CHOICES = (
        (MANDAT_INCONNU, "Situation au conseil inconnue"),
        (MANDAT_CONSEILLER_MAJORITE, "Conseiller⋅e municipal⋅e majoritaire"),
        (MANDAT_CONSEILLER_OPPOSITION, "Conseiller⋅e municipal⋅e minoritaire"),
        (MANDAT_MAIRE, "Maire"),
        (MANDAT_MAIRE_ADJOINT, "Adjoint⋅e au maire"),
        (MANDAT_MAIRE_DA, "Maire d'une commune déléguée ou associée"),
    )

    MANDAT_EPCI_PAS_DE_MANDAT = "NON"
    MANDAT_EPCI_MANDAT_INCONNU = "INC"
    MANDAT_EPCI_MAJORITE = "MAJ"
    MANDAT_EPCI_OPPOSITION = "OPP"
    MANDAT_EPCI_PRESIDENT = "PRE"
    MANDAT_EPCI_VICE_PRESIDENT = "VPR"
    MANDAT_EPCI_CHOICES = (
        (MANDAT_EPCI_PAS_DE_MANDAT, "Pas de mandat communautaire"),
        (MANDAT_EPCI_MANDAT_INCONNU, "Délégué⋅e, situation inconnue"),
        (MANDAT_EPCI_MAJORITE, "Délégué⋅e majoritaire"),
        (MANDAT_EPCI_OPPOSITION, "Délégué⋅e minoritaire"),
        (MANDAT_EPCI_PRESIDENT, "Président"),
        (MANDAT_EPCI_VICE_PRESIDENT, "Vice-Président"),
    )

    reference = models.ForeignKey(
        "data_france.EluMunicipal",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name="Référence dans le RNE",
        help_text=
        "La fiche correspondant à cet élu dans le Répertoire National des Élus",
        related_name="elus",
        related_query_name="elu",
    )

    person = models.ForeignKey(
        "people.Person",
        verbose_name="Élu",
        on_delete=models.CASCADE,
        related_name="mandats_municipaux",
        related_query_name="mandat_municipal",
    )

    conseil = models.ForeignKey(
        "data_france.Commune",
        verbose_name="Commune",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )

    mandat = models.CharField(
        "Type de mandat",
        max_length=3,
        choices=MANDAT_CHOICES,
        blank=False,
        default=MANDAT_INCONNU,
    )

    delegations = ArrayField(
        verbose_name="Délégations",
        base_field=models.CharField(max_length=20,
                                    choices=DELEGATIONS_CHOICES),
        null=False,
        blank=True,
        default=list,
    )

    communautaire = models.CharField(
        "Élu EPCI",
        max_length=3,
        choices=MANDAT_EPCI_CHOICES,
        default=MANDAT_EPCI_PAS_DE_MANDAT,
    )

    class Meta:
        verbose_name_plural = "Mandats municipaux"
        ordering = ("conseil", "person")

    def __str__(self):
        if hasattr(self, "person") and hasattr(self, "conseil"):
            if self.conseil is None:
                return f"{self.person}, {genrer(self.person.gender, 'élu⋅e')} {genrer(self.person.gender, 'municipal⋅e')} (commune inconnue)"
            return f"{self.person}, {genrer(self.person.gender, 'élu⋅e')} à {self.conseil.nom_complet}"

        return "Nouveau mandat municipal"

    def titre_complet(self, conseil_avant=False):
        if self.mandat in [
                self.MANDAT_INCONNU,
                self.MANDAT_CONSEILLER_MAJORITE,
                self.MANDAT_CONSEILLER_OPPOSITION,
        ]:
            if self.conseil and (self.conseil.code == "75056"):
                # cas spécial de paris :
                titre = genrer(self.person.gender, "conseiller⋅ère")
            else:
                titre = genrer(self.person.gender,
                               "Conseiller⋅ère municipal⋅e")
        elif self.mandat == self.MANDAT_MAIRE_ADJOINT:
            titre = f"{genrer(self.person.gender, 'Adjoint⋅e')} au maire"
        else:
            titre = self.get_mandat_display()

        if self.conseil is None:
            return titre

        if conseil_avant:
            return f"{self.conseil.nom_complet}, {titre}"
        return f"{titre} {self.conseil.nom_avec_charniere}"

    def get_absolute_url(self):
        return reverse(
            viewname="elus:modifier_mandat_municipal",
            kwargs={"pk": self.id},
        )

    def get_delete_url(self):
        return reverse(
            viewname="elus:supprimer_mandat_municipal",
            kwargs={"pk": self.id},
        )
示例#15
0
 def active_date_range(self):
     return DateRange(get_random_date(), get_random_date(future=True))
示例#16
0
class MandatRegional(MandatAbstrait):
    DEFAULT_DATE_RANGE = DateRange(date(2021, 7, 1), date(2027, 3, 31))

    MANDAT_INCONNU = "INC"
    MANDAT_CONSEILLER_MAJORITE = "MAJ"
    MANDAT_CONSEILLER_OPPOSITION = "OPP"
    MANDAT_PRESIDENT = "PRE"
    MANDAT_VICE_PRESIDENT = "VPR"

    MANDAT_CHOICES = (
        (MANDAT_INCONNU, "Situation au conseil inconnue"),
        (MANDAT_CONSEILLER_MAJORITE, "Conseiller⋅e majoritaire"),
        (MANDAT_CONSEILLER_OPPOSITION, "Conseiller⋅e minoritaire"),
        (MANDAT_PRESIDENT, "Président"),
        (MANDAT_VICE_PRESIDENT, "Vice-Président"),
    )

    person = models.ForeignKey(
        "people.Person",
        verbose_name="Élu",
        on_delete=models.CASCADE,
        related_name="mandats_regionaux",
        related_query_name="mandat_regional",
    )

    conseil = models.ForeignKey(
        "data_france.CollectiviteRegionale",
        verbose_name="Conseil régional (ou de collectivité unique)",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )

    mandat = models.CharField(
        "Type de mandat",
        max_length=3,
        choices=MANDAT_CHOICES,
        blank=False,
        default=MANDAT_INCONNU,
    )

    delegations = ArrayField(
        verbose_name="Délégations",
        base_field=models.CharField(max_length=20,
                                    choices=DELEGATIONS_CHOICES),
        null=False,
        blank=True,
        default=list,
    )

    reference = models.ForeignKey(
        "data_france.EluRegional",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name="Référence dans le RNE",
        help_text=
        "La fiche correspondant à cet élu dans le Répertoire National des Élus",
        related_name="elus",
        related_query_name="elu",
    )

    class Meta:
        verbose_name = "Mandat régional"
        verbose_name_plural = "Mandats régionaux"
        ordering = ("conseil", "person")

    def __str__(self):
        if hasattr(self, "person") and hasattr(self, "conseil"):
            if self.person.gender == "M":
                elu = "élu"
            elif self.person.gender == "F":
                elu = "élue"
            else:
                elu = "élu⋅e"
                if self.conseil is None:
                    return f"{self.person}, {genrer(self.person.gender, 'élu⋅e')} {genrer(self.person.gender, 'régional⋅e')} (lieu inconnu)"
            return f"{self.person}, {elu} au {self.conseil.nom}"

        return "Nouveau mandat régional"

    def titre_complet(self, conseil_avant=False):
        ctu = (self.conseil and self.conseil.type
               == CollectiviteRegionale.TYPE_COLLECTIVITE_UNIQUE)

        if self.mandat in [
                self.MANDAT_INCONNU,
                self.MANDAT_CONSEILLER_MAJORITE,
                self.MANDAT_CONSEILLER_OPPOSITION,
        ]:
            if ctu:
                titre = genrer(self.person.gender, "Conseiller⋅ère")
            else:
                titre = genrer(self.person.gender, "Conseiller⋅ère régional⋅e")
        else:
            qualif = "" if ctu else " du conseil régional"
            titre = genrer(
                self.person.gender,
                f"{self.get_mandat_display()}{qualif}",
            )

        if self.conseil is None:
            return titre

        nom_conseil = self.conseil.nom

        if conseil_avant:
            return f"{nom_conseil}, {titre}"

        if nom_conseil.startswith("Assemblée"):
            return f"{titre} à l'{nom_conseil}"

        return f"{titre} de {nom_conseil}"

    def get_absolute_url(self):
        return reverse(
            viewname="elus:modifier_mandat_regional",
            kwargs={"pk": self.id},
        )

    def get_delete_url(self):
        return reverse(
            viewname="elus:supprimer_mandat_regional",
            kwargs={"pk": self.id},
        )
示例#17
0
class MandatDepartemental(MandatAbstrait):
    DEFAULT_DATE_RANGE = DateRange(date(2021, 7, 1), date(2027, 3, 31))

    MANDAT_INCONNU = "INC"
    MANDAT_CONSEILLER_MAJORITE = "MAJ"
    MANDAT_CONSEILLER_OPPOSITION = "OPP"
    MANDAT_PRESIDENT = "PRE"
    MANDAT_VICE_PRESIDENT = "VPR"

    MANDAT_CHOICES = (
        (MANDAT_INCONNU, "Situation au conseil inconnue"),
        (MANDAT_CONSEILLER_MAJORITE, "Conseiller⋅e majoritaire"),
        (MANDAT_CONSEILLER_OPPOSITION, "Conseiller⋅e minoritaire"),
        (MANDAT_PRESIDENT, "Président⋅e"),
        (MANDAT_VICE_PRESIDENT, "Vice-Président⋅e"),
    )

    person = models.ForeignKey(
        "people.Person",
        verbose_name="Élu",
        on_delete=models.CASCADE,
        related_name="mandats_departementaux",
        related_query_name="mandat_departemental",
    )

    conseil = models.ForeignKey(
        "data_france.CollectiviteDepartementale",
        verbose_name="Conseil départemental (ou de métropole)",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )

    mandat = models.CharField(
        "Type de mandat",
        max_length=3,
        choices=MANDAT_CHOICES,
        blank=False,
        default=MANDAT_INCONNU,
    )

    delegations = ArrayField(
        verbose_name="Délégations",
        base_field=models.CharField(max_length=20,
                                    choices=DELEGATIONS_CHOICES),
        null=False,
        blank=True,
        default=list,
    )

    reference = models.ForeignKey(
        "data_france.EluDepartemental",
        on_delete=models.SET_NULL,
        null=True,
        blank=True,
        verbose_name="Référence dans le RNE",
        help_text=
        "La fiche correspondant à cet élu dans le Répertoire National des Élus",
        related_name="elus",
        related_query_name="elu",
    )

    class Meta:
        verbose_name = "Mandat départemental"
        verbose_name_plural = "Mandats départementaux"
        ordering = ("conseil", "person")

    def __str__(self):
        if hasattr(self, "person") and hasattr(self, "conseil"):
            if self.conseil is None:
                return f"{self.person}, {genrer(self.person.gender, 'élu⋅e')} {genrer(self.person.gender, 'départemental⋅e')} (lieu inconnu)"
            return f"{self.person}, {genrer(self.person.gender, 'élu.e')} au {self.conseil.nom}"

        return "Nouveau mandat départemental"

    def titre_complet(self, conseil_avant=False):
        if self.mandat in [
                self.MANDAT_INCONNU,
                self.MANDAT_CONSEILLER_MAJORITE,
                self.MANDAT_CONSEILLER_OPPOSITION,
        ]:
            titre = genrer(self.person.gender, "Conseiller⋅ère")
        else:
            titre = genrer(self.person.gender, self.get_mandat_display())

        if self.conseil is None:
            return titre

        if conseil_avant:
            return f"{self.conseil.nom}, {titre}"
        return f"{titre} {self.conseil.nom_avec_charniere}"

    def get_absolute_url(self):
        return reverse(
            viewname="elus:modifier_mandat_departemental",
            kwargs={"pk": self.id},
        )

    def get_delete_url(self):
        return reverse(
            viewname="elus:supprimer_mandat_departemental",
            kwargs={"pk": self.id},
        )
示例#18
0
class MandatConsulaire(MandatAbstrait):
    DEFAULT_DATE_RANGE = DateRange(date(2021, 6, 1), date(2027, 5, 31))

    class Mandat(models.TextChoices):
        CONSEILLER = "C", "Conseiller⋅ère consulaire"
        MEMBRE_AFE = "M", "Conseiller⋅ère consulaire et membre de l'AFE"
        DELEGUE = "D", "Délégué⋅e consulaire"

    person = models.ForeignKey(
        "people.Person",
        verbose_name="Élu",
        on_delete=models.CASCADE,
        related_name="mandats_consulaires",
        related_query_name="mandat_consulaire",
    )

    conseil = models.ForeignKey(
        "data_france.CirconscriptionConsulaire",
        verbose_name="Circonscription consulaire",
        blank=True,
        null=True,
        on_delete=models.CASCADE,
    )

    mandat = models.CharField(
        verbose_name="Mandat",
        max_length=1,
        blank=False,
        choices=Mandat.choices,
        default=Mandat.CONSEILLER,
    )

    def __str__(self):
        if hasattr(self, "person") and hasattr(self, "conseil"):
            if self.conseil is None:
                return f"{self.person}, {genrer(self.person.gender, self.get_mandat_display())} (circonscription inconnue)"
            return f"{self.person}, {genrer(self.person.gender, self.get_mandat_display())} ({self.conseil.nom})"

        return "Nouveau mandat consulaire"

    def titre_complet(self, conseil_avant=False):
        titre = genrer(self.person.gender, self.get_mandat_display())
        conseil = (self.conseil.nom if self.conseil else
                   "Circonscription consulaire non renseignée")

        if conseil_avant:
            return f"{conseil}, {titre}"
        return f"{titre} ({conseil})"

    def get_absolute_url(self):
        return reverse(
            viewname="elus:modifier_mandat_consulaire",
            kwargs={"pk": self.id},
        )

    def get_delete_url(self):
        return reverse(
            viewname="elus:supprimer_mandat_consulaire",
            kwargs={"pk": self.id},
        )

    class Meta:
        verbose_name_plural = "mandats consulaires"
示例#19
0
class DateRangeSerializer(serializers.Serializer):

    range = DateRangeField(initial=DateRange(None, None))
示例#20
0
class TestDateRangeField(FieldValues):
    """
    Values for `ListField` with CharField as child.
    """
    serializer_class = DateRangeSerializer

    valid_inputs = [
        ({
            'lower': '2001-01-01',
            'upper': '2001-02-02',
            'bounds': '[)'
        },
         DateRange(
             **{
                 'lower': datetime.date(2001, 1, 1),
                 'upper': datetime.date(2001, 2, 2),
                 'bounds': '[)'
             })),
        ({
            'upper': '2001-02-02',
            'bounds': '[)'
        }, DateRange(**{
            'upper': datetime.date(2001, 2, 2),
            'bounds': '[)'
        })),
        ({
            'lower': '2001-01-01',
            'bounds': '[)'
        }, DateRange(**{
            'lower': datetime.date(2001, 1, 1),
            'bounds': '[)'
        })),
        ({
            'empty': True
        }, DateRange(**{'empty': True})),
        ({}, DateRange()),
    ]
    invalid_inputs = [
        ({
            'lower': 'a'
        }, [
            'Date has wrong format. Use one of these'
            ' formats instead: '
            'YYYY-MM-DD.'
        ]),
        ('not a dict', ['Expected a dictionary of items but got type "str".']),
        ({
            'lower': '2001-02-02',
            'upper': '2001-01-01'
        }, ['The start of the range must not exceed the end of the range.']),
    ]
    outputs = [
        (DateRange(
            **{
                'lower': datetime.date(2001, 1, 1),
                'upper': datetime.date(2001, 2, 2)
            }), {
                'lower': '2001-01-01',
                'upper': '2001-02-02',
                'bounds': '[)'
            }),
        (DateRange(**{'empty': True}), {
            'empty': True
        }),
        (DateRange(), {
            'bounds': '[)',
            'lower': None,
            'upper': None
        }),
        ({
            'lower': '2001-01-01',
            'upper': '2001-02-02',
            'bounds': '[)'
        }, {
            'lower': '2001-01-01',
            'upper': '2001-02-02',
            'bounds': '[)'
        }),
        ({
            'lower': datetime.date(2001, 1, 1),
            'upper': datetime.date(2001, 2, 2),
            'bounds': '[)'
        }, {
            'lower': '2001-01-01',
            'upper': '2001-02-02',
            'bounds': '[)'
        }),
        ({
            'upper': '2001-02-02',
            'bounds': '[)'
        }, {
            'lower': None,
            'upper': '2001-02-02',
            'bounds': '[)'
        }),
        ({
            'lower': '2001-01-01',
            'bounds': '[)'
        }, {
            'lower': '2001-01-01',
            'upper': None,
            'bounds': '[)'
        }),
        ({}, {}),
    ]
    field = DateRangeField()

    def test_no_source_on_child(self):
        with pytest.raises(AssertionError) as exc_info:
            DateRangeField(child=serializers.IntegerField(source='other'))

        assert str(exc_info.value) == (
            "The `source` argument is not meaningful when applied to a `child=` field. "
            "Remove `source=` from the field declaration.")

    def test_initial_value_of_field(self):
        serializer = DateRangeSerializer()
        assert serializer.data['range'] == {
            'lower': None,
            'upper': None,
            'bounds': '[)'
        }

    def test_allow_empty(self):
        serializer = DateRangeWithAllowEmptyFalseSerializer(data={"range": {}})
        with pytest.raises(serializers.ValidationError) as exc_info:
            serializer.is_valid(raise_exception=True)
            assert exc_info.value.detail == [
                "This dictionary may not be empty."
            ]

        serializer = DateRangeWithAllowEmptyTrueSerializer(data={"range": {}})
        assert serializer.is_valid()
示例#21
0
    def handle(self, **options):
        SpecialDaysOperation.objects.all().delete()
        Timetable.objects.all().delete()
        VehicleJourney.objects.all().delete()
        JourneyPattern.objects.all().delete()
        JourneyPatternSection.objects.all().delete()
        JourneyPatternTimingLink.objects.all().delete()
        Route.objects.all().delete()
        Line.objects.all().delete()
        Operator.objects.all().delete()

        for tnds_zone in settings.TNDS_ZONES:
            local_filename, headers = urlretrieve('ftp://%s:%[email protected]/%s.zip' %
                                                  (settings.TNDS_USERNAME, settings.TNDS_PASSWORD, tnds_zone),
                                                  filename=os.path.join(settings.TNDS_NEW_DIR, '%s.zip' % tnds_zone))
            traveline_zip_file = zipfile.ZipFile(local_filename)
            for filename in traveline_zip_file.namelist():
                logger.info("Processing file %s" % filename)
                try:
                    with traveline_zip_file.open(filename) as xml_file:
                        content = xmltodict.parse(xml_file)

                        if content['TransXChange']['Services']['Service'].get('Mode') in ["bus", "coach"]:
                            # Operator
                            operator = content['TransXChange']['Operators']['Operator']
                            bus_operator, created = Operator.objects.get_or_create(
                                id=operator['@id'],
                                defaults={'code': operator['OperatorCode'],
                                          'short_name': operator['OperatorShortName'],
                                          'trading_name': operator['TradingName']})

                            # Service / Line
                            service = content['TransXChange']['Services']['Service']
                            bus_line = Line.objects.create(
                                line_id=service['Lines']['Line']['@id'],
                                line_name=service['Lines']['Line']['LineName'],
                                area=tnds_zone,
                                filename=content['TransXChange']['@FileName'],
                                description=service.get('Description', ''),
                                operator=bus_operator,
                                standard_origin=service['StandardService']['Origin'],
                                standard_destination=service['StandardService']['Destination'],
                                start_date=service['OperatingPeriod']['StartDate'],
                                end_date=service['OperatingPeriod']['EndDate'],
                                regular_days_of_week=
                                    list(service['OperatingProfile']['RegularDayType']['DaysOfWeek'].keys())
                                if 'OperatingProfile' in service and
                                   'RegularDayType' in service['OperatingProfile'] and
                                   'DaysOfWeek' in service['OperatingProfile']['RegularDayType']
                                    else ('MondayToFriday',),
                                bank_holiday_operation=
                                    list(service['OperatingProfile']['BankHolidayOperation']['DaysOfOperation'].keys())
                                if 'OperatingProfile' in service and
                                   'BankHolidayOperation' in service['OperatingProfile'] and
                                   'DaysOfOperation' in service['OperatingProfile']['BankHolidayOperation'] else None
                            )

                            # Routes
                            routes = content['TransXChange']['RouteSections']['RouteSection']
                            if routes.__class__ is not list:
                                routes = list([routes])
                                routes_desc = list([content['TransXChange']['Routes']['Route']])
                            else:
                                routes_desc = content['TransXChange']['Routes']['Route']
                            route_objects = []
                            for route_id in range(0, len(routes)):
                                route = routes[route_id]
                                stops = []
                                if route['RouteLink'].__class__ is list:
                                    next = None
                                    for stop in route['RouteLink']:
                                        # check if To from next item and From from current are the same
                                        if next and next != stop['From']['StopPointRef']:
                                            logger.error('route links are not sequentials in route %s' %
                                                         routes_desc[route_id]['@id'])
                                        stops.append(stop['From']['StopPointRef'])
                                        next = stop['To']['StopPointRef']
                                    stops.append(route['RouteLink'][-1]['To']['StopPointRef'])
                                else:
                                    stops.append(route['RouteLink']['From']['StopPointRef'])
                                    stops.append(route['RouteLink']['To']['StopPointRef'])
                                route_objects.append(
                                    Route(id=tnds_zone+'-'+routes_desc[route_id]['@id'], line=bus_line,
                                          stops_list=','.join(stops),
                                          description=routes_desc[route_id]['Description']))
                            if route_objects:
                                Route.objects.bulk_create(route_objects)


                            # Journey Pattern Sections
                            journey_pattern_sections = \
                                content['TransXChange']['JourneyPatternSections']['JourneyPatternSection']
                            if journey_pattern_sections.__class__ is not list:
                                journey_pattern_sections = list([journey_pattern_sections])
                            journey_pattern_section_objects = []
                            journey_pattern_timing_link_objects = []
                            for journey_pattern_section in journey_pattern_sections:
                                journey_pattern_section_objects.append(
                                    JourneyPatternSection(id=tnds_zone+'-'+journey_pattern_section['@id']))
                                journey_pattern_timing_links = journey_pattern_section['JourneyPatternTimingLink']
                                if journey_pattern_timing_links.__class__ is not list:
                                    journey_pattern_timing_links = list([journey_pattern_timing_links])
                                for journey_pattern_timing_link in journey_pattern_timing_links:
                                    wait_time = xml_timedelta_to_python(journey_pattern_timing_link['To']['WaitTime']) \
                                        if 'WaitTime' in journey_pattern_timing_link['To'] else None
                                    if not Stop.objects.filter(atco_code=journey_pattern_timing_link['From']['StopPointRef']):
                                        logger.error("Stop %s cannot be found in the database, creating a temporal entry" %
                                                     journey_pattern_timing_link['From']['StopPointRef'])
                                        Stop.objects.create(atco_code=journey_pattern_timing_link['From']['StopPointRef'],
                                                            naptan_code="Unknown", common_name="Unknown", indicator="Unknown",
                                                            locality_name="", longitude=0, latitude=0)
                                    if not Stop.objects.filter(atco_code=journey_pattern_timing_link['To']['StopPointRef']):
                                        logger.error("Stop %s cannot be found in the database, creating a temporal entry" %
                                                     journey_pattern_timing_link['To']['StopPointRef'])
                                        Stop.objects.create(atco_code=journey_pattern_timing_link['To']['StopPointRef'],
                                                            naptan_code="Unknown", common_name="Unknown", indicator="Unknown",
                                                            locality_name="", longitude=0, latitude=0)
                                    journey_pattern_timing_link_objects.append(JourneyPatternTimingLink(
                                        id=tnds_zone+'-'+journey_pattern_timing_link['@id'],
                                        stop_from_id=journey_pattern_timing_link['From']['StopPointRef'],
                                        stop_to_id=journey_pattern_timing_link['To']['StopPointRef'],
                                        stop_from_timing_status=journey_pattern_timing_link['From']['TimingStatus'],
                                        stop_to_timing_status=journey_pattern_timing_link['To']['TimingStatus'],
                                        stop_from_sequence_number=journey_pattern_timing_link['From']['@SequenceNumber'],
                                        stop_to_sequence_number=journey_pattern_timing_link['To']['@SequenceNumber'],
                                        run_time=xml_timedelta_to_python(journey_pattern_timing_link['RunTime']),
                                        wait_time=wait_time,
                                        journey_pattern_section_id=tnds_zone+'-'+journey_pattern_section['@id']
                                    ))
                            if journey_pattern_section_objects:
                                JourneyPatternSection.objects.bulk_create(journey_pattern_section_objects)
                            if journey_pattern_timing_link_objects:
                                JourneyPatternTimingLink.objects.bulk_create(journey_pattern_timing_link_objects)

                            # Journey Pattern
                            journey_patterns = \
                                content['TransXChange']['Services']['Service']['StandardService']['JourneyPattern']
                            if journey_patterns.__class__ is not list:
                                journey_patterns = list([journey_patterns])
                            journey_pattern_objects = []
                            for journey_pattern in journey_patterns:
                                journey_pattern_objects.append(JourneyPattern(
                                    id=tnds_zone+'-'+journey_pattern['@id'], direction=journey_pattern['Direction'],
                                    route_id=tnds_zone+'-'+journey_pattern['RouteRef'],
                                    section_id=tnds_zone+'-'+journey_pattern['JourneyPatternSectionRefs'])
                                )
                            if journey_pattern_objects:
                                JourneyPattern.objects.bulk_create(journey_pattern_objects)

                            # Journey
                            journeys = content['TransXChange']['VehicleJourneys']['VehicleJourney']
                            journeys = list([journeys]) if journeys.__class__ is not list else journeys
                            order_journey = 1
                            vehicle_journey_objects = []
                            special_days_operation = []
                            for journey in journeys:
                                regular_days = []
                                nonoperation_bank_holidays = []
                                operation_bank_holidays = []
                                if 'OperatingProfile' in journey:
                                    element = journey['OperatingProfile']
                                    if 'RegularDayType' in element and 'DaysOfWeek' in element['RegularDayType']:
                                        week_days_element = element['RegularDayType']['DaysOfWeek']
                                        for day in list(week_days_element.keys()):
                                            if 'To' in day:
                                                day_range_bounds = [WEEKDAYS[i] for i in day.split('To')]
                                                day_range = range(day_range_bounds[0], day_range_bounds[1] + 1)
                                                regular_days += [calendar.day_name[i] for i in day_range]
                                            elif day == 'Weekend':
                                                regular_days += ["Saturday", "Sunday"]
                                            else:
                                                regular_days.append(day)

                                    # Special Days:
                                    if 'SpecialDaysOperation' in element:
                                        if 'DaysOfNonOperation' in element['SpecialDaysOperation'] and element['SpecialDaysOperation']['DaysOfNonOperation']:
                                            noopdays = element['SpecialDaysOperation']['DaysOfNonOperation']['DateRange']
                                            noopdays = list([noopdays]) if noopdays.__class__ is not list else noopdays
                                            nonoperation_days = \
                                                list(map(lambda x:
                                                         SpecialDaysOperation(vehicle_journey_id=journey['PrivateCode'],
                                                                              days=DateRange(lower=x['StartDate'],
                                                                                             upper=x['EndDate'],
                                                                                             bounds="[]"),
                                                                              operates=False), noopdays))
                                            special_days_operation += nonoperation_days
                                        if 'DaysOfOperation' in element['SpecialDaysOperation'] and element['SpecialDaysOperation']['DaysOfOperation']:
                                            opdays = element['SpecialDaysOperation']['DaysOfNonOperation']['DateRange']
                                            opdays = list([opdays]) if opdays.__class__ is not list else opdays
                                            operation_days = \
                                                list(map(lambda x:
                                                         SpecialDaysOperation(vehicle_journey_id=journey['PrivateCode'],
                                                                              days=DateRange(lower=x['StartDate'],
                                                                                             upper=x['EndDate'],
                                                                                             bounds="[]"),
                                                                              operates=True), opdays))
                                            special_days_operation += operation_days

                                    # Bank Holidays
                                    if 'BankHolidayOperation' in element:
                                        if 'DaysOfNonOperation' in element['BankHolidayOperation'] and element['BankHolidayOperation']['DaysOfNonOperation']:
                                            nonoperation_bank_holidays = list(
                                                element['BankHolidayOperation']['DaysOfNonOperation'].keys())
                                        if 'DaysOfOperation' in element['BankHolidayOperation'] and element['BankHolidayOperation']['DaysOfOperation']:
                                            operation_bank_holidays = list(
                                                element['BankHolidayOperation']['DaysOfOperation'].keys())
                                vehicle_journey_objects.append(VehicleJourney(
                                    id=journey['PrivateCode'], journey_pattern_id=tnds_zone+'-'+journey['JourneyPatternRef'],
                                    departure_time=journey['DepartureTime'], days_of_week=' '.join(regular_days),
                                    nonoperation_bank_holidays=' '.join(nonoperation_bank_holidays),
                                    operation_bank_holidays=' '.join(operation_bank_holidays), order=order_journey)
                                )
                                order_journey += 1
                            if vehicle_journey_objects:
                                VehicleJourney.objects.bulk_create(vehicle_journey_objects)
                                SpecialDaysOperation.objects.bulk_create(special_days_operation)
                        xml_file.close()
                except Exception as e:
                    logger.exception("Error while trying to process file %s, exception was %s" % (filename, e))
        for vehicle_journey in VehicleJourney.objects.all():
            Timetable.objects.bulk_create(vehicle_journey.generate_timetable())

        for tnds_zone in settings.TNDS_ZONES:
            os.rename(os.path.join(settings.TNDS_NEW_DIR, '%s.zip' % tnds_zone),
                      os.path.join(settings.TNDS_DIR, '%s.zip' % tnds_zone))
示例#22
0
import reversion
from django.contrib.gis.db.models.functions import Distance
from django.contrib.postgres.fields import ArrayField, DateRangeField
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
from django.db import models, connection
from django.urls import reverse
from django.utils import timezone
from django.utils.functional import cached_property
from django.utils.html import format_html
from psycopg2._range import DateRange

from agir.lib.display import genrer
from agir.lib.history import HistoryMixin

MUNICIPAL_DEFAULT_DATE_RANGE = DateRange(date(2020, 6, 28), date(2026, 3, 31))
DEPARTEMENTAL_DEFAULT_DATE_RANGE = DateRange(date(2015, 3, 29),
                                             date(2021, 3, 31))
REGIONAL_DEFAULT_DATE_RANGE = DateRange(date(2015, 12, 13), date(2021, 3, 31))

DELEGATIONS_CHOICES = (
    ("social", "Action sociale"),
    ("civiles juridiques", "Affaires civiles et juridiques"),
    ("économie", "Affaires économiques"),
    ("école", "Affaires scolaires"),
    ("agriculture", "Agriculture"),
    ("veterans", "Anciens combattants"),
    ("cantines", "Cantines"),
    ("commerce", "Commerce"),
    ("cimetières", "Cimetières"),
    ("international", "Coopération internationale"),