internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: svthalia/concrexit

Issue:
Show ul and ol buttons in TinyMCE editors
### Is your feature request related to a problem? Please describe.
`<ul>`s and `<ol>`s are nice, but the current TinyMCE editor doesn't show shortcuts to use them

### Describe the solution you'd like
Add buttons to the TinyMCE toolbar

### Motivation
Make people use rich texts

### Describe alternatives you've considered
Not doing it

### Additional context



Discussion:

Resolving patch:
diff --git a/website/thaliawebsite/settings.py b/website/thaliawebsite/settings.py
--- a/website/thaliawebsite/settings.py
+++ b/website/thaliawebsite/settings.py
@@ -783,9 +783,9 @@ def from_env(
 TINYMCE_DEFAULT_CONFIG = {
     "max_height": 500,
     "menubar": False,
-    "plugins": "autolink autoresize link image code media paste",
-    "toolbar": "h2 h3 | bold italic underline strikethrough | image media | link unlink | "
-    "bullist numlist | undo redo | code",
+    "plugins": "autolink autoresize link image code media paste lists",
+    "toolbar": "h2 h3 | bold italic underline strikethrough | image media | link unlink "
+    "| bullist numlist | undo redo | code",
     "contextmenu": "bold italic underline strikethrough | link",
     "paste_as_text": True,
     "relative_urls": False,


Test patch:
Repository: svthalia/concrexit

Issue:
Food API: AttributeError: 'Event' object has no attribute 'member_registration'
Sentry Issue: [CONCREXIT-KG](https://sentry.io/organizations/thalia/issues/3768057031/?referrer=github_integration)

```
AttributeError: 'Event' object has no attribute 'member_registration'
(11 additional frame(s) were not displayed)
...
  File "rest_framework/serializers.py", line 253, in data
    self._data = self.to_representation(self.instance)
  File "rest_framework/serializers.py", line 522, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "rest_framework/serializers.py", line 522, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "rest_framework/fields.py", line 1838, in to_representation
    return method(value)
  File "events/api/v2/serializers/event.py", line 83, in _registration_status
    if self.context["request"].member and len(instance.member_registration) > 0:
```


Discussion:

Resolving patch:
diff --git a/website/pizzas/api/v2/views.py b/website/pizzas/api/v2/views.py
--- a/website/pizzas/api/v2/views.py
+++ b/website/pizzas/api/v2/views.py
@@ -1,3 +1,5 @@
+from django.db.models import Prefetch
+
 from oauth2_provider.contrib.rest_framework import IsAuthenticatedOrTokenHasScope
 from rest_framework import filters as framework_filters
 from rest_framework import status
@@ -11,6 +13,7 @@
 )
 from rest_framework.response import Response
 
+from events.models.event_registration import EventRegistration
 from payments.exceptions import PaymentError
 from payments.services import delete_payment
 from pizzas.api.v2 import filters
@@ -45,12 +48,25 @@ class FoodEventDetailView(RetrieveAPIView):
     """Returns one single food event."""
 
     serializer_class = FoodEventSerializer
-    queryset = FoodEvent.objects.all()
     permission_classes = [
         IsAuthenticatedOrTokenHasScope,
     ]
     required_scopes = ["food:read"]
 
+    def get_queryset(self):
+        events = FoodEvent.objects.all()
+        if self.request.member:
+            events = events.prefetch_related(
+                Prefetch(
+                    "event__eventregistration_set",
+                    to_attr="member_registration",
+                    queryset=EventRegistration.objects.filter(
+                        member=self.request.member
+                    ).select_properties("queue_position"),
+                )
+            )
+        return events
+
 
 class FoodEventProductsListView(ListAPIView):
     """Returns an overview of all products."""


Test patch:
Repository: svthalia/concrexit

Issue:
Cannot create new shifts: ValueError: 'Shift' instance needs to have a primary key value before this relationship can be used.
Sentry Issue: [CONCREXIT-KK](https://sentry.io/organizations/thalia/issues/3788518453/?referrer=github_integration)

```
ValueError: 'Shift' instance needs to have a primary key value before this relationship can be used.
(14 additional frame(s) were not displayed)
...
  File "django/forms/models.py", line 492, in _post_clean
    self.instance.full_clean(exclude=exclude, validate_unique=False)
  File "django/db/models/base.py", line 1452, in full_clean
    self.clean()
  File "sales/models/shift.py", line 69, in clean
    if self.orders.filter(created_at__lt=self.start):
  File "django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "django/db/models/fields/related_descriptors.py", line 687, in get_queryset
    raise ValueError(
```


Discussion:
We should only do this if we have a pk

Resolving patch:
diff --git a/website/sales/models/shift.py b/website/sales/models/shift.py
--- a/website/sales/models/shift.py
+++ b/website/sales/models/shift.py
@@ -66,7 +66,7 @@ def clean(self):
         super().clean()
         errors = {}
 
-        if self.orders.filter(created_at__lt=self.start):
+        if self.pk is not None and self.orders.filter(created_at__lt=self.start):
             errors.update(
                 {
                     "start": _(


Test patch:
Repository: svthalia/concrexit

Issue:
Unprocessed Thalia Pay payment without bank account
Apparently it is possible that people either pay with Thalia pay without having a valid bank account, or, it is possible to remove a bank account after a Thalia Pay payment is made but not processed (in which case it should not be possible)


Sentry Issue: [CONCREXIT-HD](https://sentry.io/organizations/thalia/issues/3640470247/?referrer=github_integration)

```
AttributeError: 'NoneType' object has no attribute 'last_used'
(8 additional frame(s) were not displayed)
...
  File "django/utils/decorators.py", line 46, in _wrapper
    return bound_method(*args, **kwargs)
  File "django/contrib/auth/decorators.py", line 23, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "django/views/generic/base.py", line 119, in dispatch
    return handler(request, *args, **kwargs)
  File "payments/admin_views.py", line 107, in post
    services.process_batch(batch)
  File "payments/services.py", line 151, in process_batch
    bank_account.last_used = batch.withdrawal_date
```


Discussion:

Resolving patch:
diff --git a/website/payments/services.py b/website/payments/services.py
--- a/website/payments/services.py
+++ b/website/payments/services.py
@@ -149,8 +149,13 @@ def process_batch(batch):
     payments = batch.payments_set.select_related("paid_by")
     for payment in payments:
         bank_account = payment.paid_by.bank_accounts.last()
-        bank_account.last_used = batch.withdrawal_date
-        bank_account.save()
+        if not bank_account:  # pragma: no cover
+            # This should not happen, cannot haver, does not happen (right... ;p), but if it does, we don't want to crash, but just remove the payment from the batch (make it unprocessed)
+            payment.batch = None
+            payment.save()
+        else:
+            bank_account.last_used = batch.withdrawal_date
+            bank_account.save()
 
     batch.save()
 
@@ -215,7 +220,7 @@ def execute_data_minimisation(dry_run=False):
     queryset_bankaccounts = BankAccount.objects.all()
     queryset_bankaccounts = queryset_bankaccounts.filter(
         valid_until__lt=timezone.now()
-    )  # Keep valid bank accounts
+    )  # We must always keep valid bank accounts. so we only select the ones that are not valid anymore (valid_until < now)
     queryset_bankaccounts = queryset_bankaccounts.exclude(  # Also keep bank accounts that
         Q(
             owner__paid_payment_set__type=Payment.TPAY


Test patch:
Repository: svthalia/concrexit

Issue:
Paparazcie committee members cannot edit promo requests
Members of the paparazcie cannot edit the promo requests in the back-end. I can, so it might be an issue with permissions. 


Discussion:
Can you maybe post the permissions of the Paparazcie committee object? Only admins can see those.

Resolving patch:
diff --git a/website/promotion/admin.py b/website/promotion/admin.py
--- a/website/promotion/admin.py
+++ b/website/promotion/admin.py
@@ -23,8 +23,8 @@ class PromotionRequestAdmin(admin.ModelAdmin):
     actions = ["mark_not_started", "mark_started", "mark_finished", "mark_published"]
 
     def has_change_permission(self, request, obj=None):
-        if obj is not None and not is_organiser(request.member, obj.event):
-            return False
+        if obj is not None and obj.event and is_organiser(request.member, obj.event):
+            return True
         return super().has_change_permission(request, obj)
 
     def mark_not_started(self, request, queryset):


Test patch:
Repository: svthalia/concrexit

Issue:
Summary download not working
### Describe the bug
Some summaries (for example Data Mining 2019-2020 Practice Exam Midterms) are not downloaded when clicked. They also do not work when trying to view them through the Site Administration. Pressing these buttons only leads to a new empty tab. 

Some summaries still work however.

### How to reproduce
Steps to reproduce the behaviour:
1. Go to 'education'
2. Scroll down to 'data mining'
3. Click on 'data mining'
4. Click on '2019-2020'
5. Click on 'Practice Exam Midterm'

### Expected behaviour
Clicking this button should download the associated file.



Discussion:
![Screenshot 2022-11-01 at 13 37 09](https://user-images.githubusercontent.com/1799914/199234014-07844e53-8209-4a3e-ac07-7f469b521315.png)


Resolving patch:
diff --git a/website/education/views.py b/website/education/views.py
--- a/website/education/views.py
+++ b/website/education/views.py
@@ -124,13 +124,13 @@ class ExamDetailView(DetailView):
 
     def get(self, request, *args, **kwargs) -> HttpResponse:
         response = super().get(request, *args, **kwargs)
-        exam = response.context_data["object"]
-        exam.download_count += 1
-        exam.save()
+        obj = response.context_data["object"]
+        obj.download_count += 1
+        obj.save()
 
-        ext = os.path.splitext(exam.file.name)[1]
-        filename = f"{exam.course.name}-exam{exam.year}{ext}"
-        return redirect(get_media_url(exam.file, filename))
+        ext = os.path.splitext(obj.file.name)[1]
+        filename = f"{obj.course.name}-summary{obj.year}{ext}"
+        return redirect(get_media_url(obj.file, filename))
 
 
 @method_decorator(login_required, "dispatch")
diff --git a/website/utils/media/services.py b/website/utils/media/services.py
--- a/website/utils/media/services.py
+++ b/website/utils/media/services.py
@@ -32,7 +32,7 @@ def get_media_url(file, attachment=False):
     If the file is private a signature will be added.
     Do NOT use this with user input
     :param file: the file field
-    :param attachment: True if the file is a forced download
+    :param attachment: filename to use for the attachment or False to not download as attachment
     :return: the url of the media
     """
     storage = DefaultStorage()


Test patch:
Repository: svthalia/concrexit

Issue:
Liked photos API endpoint
To add an overview of liked photos to ThaliApp, we need a new endpoint for liked photos.

I think it would be best to have `api/v2/photos/photos/` with `liked` boolean GET filter. It will need to do some filtering to prevent photos that are not published in an album from being returned.


Discussion:

Resolving patch:
diff --git a/website/photos/api/v2/urls.py b/website/photos/api/v2/urls.py
--- a/website/photos/api/v2/urls.py
+++ b/website/photos/api/v2/urls.py
@@ -1,7 +1,12 @@
 """Photos app API v2 urls."""
 from django.urls import include, path
 
-from photos.api.v2.views import AlbumDetailView, AlbumListView, PhotoLikeView
+from photos.api.v2.views import (
+    AlbumDetailView,
+    AlbumListView,
+    LikedPhotosListView,
+    PhotoLikeView,
+)
 
 app_name = "photos"
 
@@ -19,6 +24,9 @@
                 path(
                     "photos/<int:pk>/like/", PhotoLikeView.as_view(), name="photo-like"
                 ),
+                path(
+                    "photos/liked/", LikedPhotosListView.as_view(), name="liked-photos"
+                ),
             ]
         ),
     ),
diff --git a/website/photos/api/v2/views.py b/website/photos/api/v2/views.py
--- a/website/photos/api/v2/views.py
+++ b/website/photos/api/v2/views.py
@@ -8,7 +8,11 @@
 from rest_framework.views import APIView
 
 from photos import services
-from photos.api.v2.serializers.album import AlbumListSerializer, AlbumSerializer
+from photos.api.v2.serializers.album import (
+    AlbumListSerializer,
+    AlbumSerializer,
+    PhotoListSerializer,
+)
 from photos.models import Album, Like, Photo
 
 
@@ -51,6 +55,35 @@ def get_queryset(self):
         )
 
 
+class LikedPhotosListView(ListAPIView):
+    """Returns the details the liked album."""
+
+    serializer_class = PhotoListSerializer
+    permission_classes = [
+        IsAuthenticatedOrTokenHasScope,
+    ]
+    required_scopes = ["photos:read"]
+
+    def get(self, request, *args, **kwargs):
+        if not self.request.member:
+            return Response(
+                data={
+                    "detail": "You need to be a member in order to view your liked photos."
+                },
+                status=status.HTTP_403_FORBIDDEN,
+            )
+        return self.list(request, *args, **kwargs)
+
+    def get_queryset(self):
+        return (
+            Photo.objects.filter(likes__member=self.request.member, album__hidden=False)
+            .annotate(
+                member_likes=Count("likes", filter=Q(likes__member=self.request.member))
+            )
+            .select_properties("num_likes")
+        )
+
+
 class PhotoLikeView(APIView):
     permission_classes = [IsAuthenticatedOrTokenHasScope]
     required_scopes = ["photos:read"]


Test patch:
Repository: svthalia/concrexit

Issue:
Events can be unpublished, change price, and republished, while registrations remain
### Describe the bug
Events can be unpublished, change price, and republished, while registrations remain. 
This is a problem because we do not allow changing the price after people signed up

### How to reproduce
1. Unpublish
2. Change pricee
3. Republish

### Expected behaviour
Changing price should not be possible if there are registrations.

### Screenshots

### Additional context
If we want to allow changing prices, this should be an explicit feature


Discussion:

Resolving patch:
diff --git a/website/events/models/event.py b/website/events/models/event.py
--- a/website/events/models/event.py
+++ b/website/events/models/event.py
@@ -335,7 +335,7 @@ def clean_changes(self, changed_data):
         can be retrieved from self.changed_data
         """
         errors = {}
-        if self.published:
+        if self.published or self.participant_count > 0:
             for field in ("price", "registration_start"):
                 if (
                     field in changed_data


Test patch:
Repository: svthalia/concrexit

Issue:
Invited from queue email missing some stuff
### Describe the bug
![image](https://user-images.githubusercontent.com/41264528/217180789-a1478ad4-56b2-43af-b6f0-52ee272e2bc6.jpeg)


### How to reproduce
Steps to reproduce the behaviour:
1. Be in queue
2. Get invited (by people deregistering)


Discussion:

Resolving patch:
diff --git a/website/events/emails.py b/website/events/emails.py
--- a/website/events/emails.py
+++ b/website/events/emails.py
@@ -25,12 +25,19 @@ def notify_first_waiting(event):
         subject = _("[THALIA] Notification about your registration for '{}'").format(
             event.title
         )
+
+        organiser_emails = [
+            organiser.contact_address
+            for organiser in event.organisers.all()
+            if organiser.contact_address is not None
+        ]
         text_message = text_template.render(
             {
                 "event": event,
                 "registration": first_waiting,
                 "name": first_waiting.name or first_waiting.member.first_name,
                 "base_url": settings.BASE_URL,
+                "organisers": organiser_emails,
             }
         )
 


Test patch:
Repository: svthalia/concrexit

Issue:
Admin site doesnt show organizers
### Describe the bug
Organizers are not shown in the site admin

### How to reproduce
Steps to reproduce the behaviour:
1. Go to any event
2. See that the organizers field is empty

### Expected behaviour
there should be at least one organizer

### Additional context
multiple organizers broke things again

Admin site doesnt show organizers
### Describe the bug
Organizers are not shown in the site admin

### How to reproduce
Steps to reproduce the behaviour:
1. Go to any event
2. See that the organizers field is empty

### Expected behaviour
there should be at least one organizer

### Additional context
multiple organizers broke things again



Discussion:

Resolving patch:
diff --git a/website/events/emails.py b/website/events/emails.py
--- a/website/events/emails.py
+++ b/website/events/emails.py
@@ -72,12 +72,21 @@ def notify_waiting(event, registration):
     subject = _("[THALIA] Notification about your registration for '{}'").format(
         event.title
     )
+
+    organiser_emails = [
+        organiser.contact_address
+        for organiser in event.organisers.all()
+        if organiser.contact_address is not None
+    ]
+
     text_message = text_template.render(
         {
             "event": event,
             "registration": registration,
             "name": registration.name or registration.member.first_name,
             "base_url": settings.BASE_URL,
+            "organisers": organiser_emails,
         }
     )
+
     EmailMessage(subject, text_message, to=[registration.email]).send()


Test patch:
Repository: svthalia/concrexit

Issue:
Make API redirects respect query params
<!--

Please add the appropriate label for what change should be made:
docs: changes to the documentation)
refactor: refactoring production code, eg. renaming a variable or rewriting a function
test: adding missing tests, refactoring tests; no production code change
chore: updating poetry etc; no production code change

-->

### Describe the change
As seen in #2828 and #2821, some old API endpoints are redirected to their new replacements. This redirection does not propagate any query parameters. It would be nice to pass query parameters along when redirecting to a replacement API endpoint to prevent breaking consumers.

### Motivation
#2828 #2821


Discussion:

Resolving patch:
diff --git a/website/partners/api/v2/urls.py b/website/partners/api/v2/urls.py
--- a/website/partners/api/v2/urls.py
+++ b/website/partners/api/v2/urls.py
@@ -16,14 +16,18 @@
     path(
         "partners/events/",
         RedirectView.as_view(
-            pattern_name="api:v2:events:external-events-list", permanent=False
+            pattern_name="api:v2:events:external-events-list",
+            permanent=False,
+            query_string=True,
         ),
         name="partner-events-list",
     ),
     path(
         "partners/events/<int:pk>/",
         RedirectView.as_view(
-            pattern_name="api:v2:events:external-event-detail", permanent=False
+            pattern_name="api:v2:events:external-event-detail",
+            permanent=False,
+            query_string=True,
         ),
         name="partner-events-detail",
     ),


Test patch:
Repository: svthalia/concrexit

Issue:
Become a member menu item is missing!
### Describe the bug
Become a member menu item is missing!

### How to reproduce
1. Check staging, the menu item is missing


### Expected behaviour
It should be there

### Screenshots
If applicable, add screenshots to help explain your problem.

### Additional context
Probably this was missed in #2728 


Discussion:

Resolving patch:
diff --git a/website/registrations/apps.py b/website/registrations/apps.py
--- a/website/registrations/apps.py
+++ b/website/registrations/apps.py
@@ -1,5 +1,6 @@
 """Configuration for the newsletters package."""
 from django.apps import AppConfig
+from django.urls import reverse
 from django.utils.translation import gettext_lazy as _
 
 
@@ -16,3 +17,16 @@ def ready(self):
         from .payables import register
 
         register()
+
+    def menu_items(self):
+        return {
+            "categories": [{"name": "association", "title": "Association", "key": 1}],
+            "items": [
+                {
+                    "category": "association",
+                    "title": "Become a member",
+                    "url": reverse("registrations:index"),
+                    "key": 6,
+                },
+            ],
+        }


Test patch:
Repository: svthalia/concrexit

Issue:
Registration birth date can be later than the current date
### Describe the bug
It is possible when registering to set your birthdate to after the current day. Still doing this does results in the user not being able to update any of their account information.

### How to reproduce
Simply putting in a date that hasn't passed in when registering

### Expected behaviour
Input should be rejected when trying to put in an impossible birthdate



Discussion:

Resolving patch:
diff --git a/website/registrations/models.py b/website/registrations/models.py
--- a/website/registrations/models.py
+++ b/website/registrations/models.py
@@ -415,6 +415,9 @@ def clean(self):
         if self.programme is None and self.membership_type != Membership.BENEFACTOR:
             errors.update({"programme": _("This field is required.")})
 
+        if self.birthday and self.birthday > timezone.now().date():
+            errors.update({"birthday": _("A birthday cannot be in the future.")})
+
         if self.direct_debit:
             if not self.iban:
                 errors.update(


Test patch:
diff --git a/website/registrations/tests/test_models.py b/website/registrations/tests/test_models.py
--- a/website/registrations/tests/test_models.py
+++ b/website/registrations/tests/test_models.py
@@ -207,6 +207,17 @@ def test_unique_email_user(self):
         with self.assertRaises(ValidationError):
             self.registration.clean()
 
+    def test_require_past_birthday(self):
+        registration = Registration.objects.create(
+            length=Entry.MEMBERSHIP_YEAR,
+            first_name="John",
+            last_name="Doe",
+            birthday=timezone.now().date() + timezone.timedelta(weeks=52),
+        )
+
+        with self.assertRaises(ValidationError):
+            registration.clean()
+
     def test_unique_student_number_user(self):
         self.registration.student_number = "s1234567"
         self.registration.clean()