Repository: svthalia/concrexit

Issue:
Fixtures are flaky
### Describe the bug
createfixtures may fail randomly in CI. I got this error:

<details>
<summary>Error</summary>

```
Creating a membergroup
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "activemembers_committee_name_en_key"
DETAIL:  Key (name)=(Amet) already exists.


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/runner/work/concrexit/concrexit/website/manage.py", line 24, in <module>
    execute_from_command_line(sys.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
    utility.execute()
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/core/management/__init__.py", line 440, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/core/management/base.py", line 402, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/core/management/base.py", line 448, in execute
    output = self.handle(*args, **options)
  File "/home/runner/work/concrexit/concrexit/website/utils/management/commands/createfixtures.py", line 702, in handle
    self.create_member_group(Society)
  File "/home/runner/work/concrexit/concrexit/website/utils/management/commands/createfixtures.py", line 215, in create_member_group
    member_group.photo.save(member_group.name + ".jpeg", ContentFile(icon))
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/fields/files.py", line 98, in save
    self.instance.save()
  File "/home/runner/work/concrexit/concrexit/website/activemembers/models.py", line 110, in save
    super().save(**kwargs)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/base.py", line 812, in save
    self.save_base(
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/base.py", line 862, in save_base
    parent_inserted = self._save_parents(cls, using, update_fields)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/base.py", line 904, in _save_parents
    updated = self._save_table(
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/base.py", line 1006, in _save_table
    results = self._do_insert(
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/base.py", line 1047, in _do_insert
    return manager._insert(
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/query.py", line 1790, in _insert
    return query.get_compiler(using=using).execute_sql(returning_fields)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1660, in execute_sql
    cursor.execute(sql, params)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/backends/utils.py", line 67, in execute
    return self._execute_with_wrappers(
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/utils.py", line 91, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "/home/runner/.cache/pypoetry/virtualenvs/concrexit-HBEO5f-9-py3.9/lib/python3.9/site-packages/django/db/backends/utils.py", line 89, in _execute
    return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: duplicate key value violates unique constraint "activemembers_committee_name_en_key"
DETAIL:  Key (name)=(Amet) already exists.

make: *** [Makefile:195: fixtures] Error 1
```

</details>

### How to reproduce
It randomly happens

### Expected behaviour
Createfixtures always works

### Additional context
It happened here: https://github.com/svthalia/concrexit/actions/runs/3380853146/jobs/5614104395



Discussion:
In this case, it seems like the code tries to generate two committees that are both called "Amet".
Also note that fixtures dont always clean() right now. We create events that dont validate actually.
I just saw this https://github.com/svthalia/concrexit/actions/runs/4493757940/jobs/7905375790 which seems to be a problem with fixtures, while this commit was merged into it. So it seems there's still something flaky about the fixtures, unfortunately.

Resolving patch:
diff --git a/website/partners/migrations/0029_alter_partner_address.py b/website/partners/migrations/0029_alter_partner_address.py
new file mode 100644
--- /dev/null
+++ b/website/partners/migrations/0029_alter_partner_address.py
@@ -0,0 +1,26 @@
+# Generated by Django 5.0.2 on 2024-05-22 19:06
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+    dependencies = [
+        ("partners", "0028_alter_partner_alternate_logo_alter_partner_logo_and_more"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="partner",
+            name="address",
+            field=models.CharField(
+                max_length=100,
+                validators=[
+                    django.core.validators.RegexValidator(
+                        message="Enter a valid address",
+                        regex="^([^/@:;%_]+) \\d+([^/@:;%_]*)",
+                    )
+                ],
+            ),
+        ),
+    ]
diff --git a/website/partners/models.py b/website/partners/models.py
--- a/website/partners/models.py
+++ b/website/partners/models.py
@@ -53,12 +53,8 @@ class Partner(models.Model):
         max_length=100,
         validators=[
             RegexValidator(
-                regex=(
-                    r"^([1-9][e][\s])*([ëéÉËa-zA-Z]"
-                    r"+(([\.][\s])|([\s]))?)+[1-9][0-9]"
-                    r"*(([-][1-9][0-9]*)|([\s]?[ëéÉËa-zA-Z]+))?$"
-                ),
-                message=_("Enter a valid address"),
+                regex=r"^([^/@:;%_]+) \d+([^/@:;%_]*)",
+                message="Enter a valid address",
             )
         ],
     )
diff --git a/website/utils/management/commands/createfixtures.py b/website/utils/management/commands/createfixtures.py
--- a/website/utils/management/commands/createfixtures.py
+++ b/website/utils/management/commands/createfixtures.py
@@ -49,7 +49,7 @@
 
 
 def _generate_title():
-    words = _faker.words(random.randint(1, 3))
+    words = _faker.words(random.randint(2, 4))
     return " ".join([word.capitalize() for word in words])
 
 
@@ -237,7 +237,7 @@ def create_member_group(self, group_model):
         if random.random() < 0.1:
             month = timedelta(days=30)
             member_group.until = _faker.date_time_between_dates(
-                member_group.since, member_group.since + 30 * month
+                member_group.since + 12 * month, member_group.since + 60 * month
             ).date()
 
         member_group.active = random.random() < 0.9
@@ -273,12 +273,16 @@ def create_member_group_membership(self, member, group):
 
         today = date.today()
         membership.since = _faker.date_time_between_dates(
-            group.since, group.until
+            group.since,
+            group.until - timedelta(days=3)
+            if group.until
+            else group.since + timedelta(days=365),
         ).date()
 
         if random.random() < 0.2 and membership.since < today:
             membership.until = _faker.date_time_between_dates(
-                membership.since, group.until
+                membership.since,
+                group.until if group.until else group.since + timedelta(days=2 * 365),
             ).date()
 
         membership.full_clean()


Test patch: