internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: ephios-dev/ephios

Issue:
PWA does not respect orientation lock on Android



Discussion:
https://meta.discourse.org/t/manifest-orientation-property/94900/6

Resolving patch:
diff --git a/ephios/core/views/pwa.py b/ephios/core/views/pwa.py
--- a/ephios/core/views/pwa.py
+++ b/ephios/core/views/pwa.py
@@ -15,7 +15,6 @@ def manifest(request):
         "start_url": "/",
         "display": "standalone",
         "scope": "/",
-        "orientation": "any",
         "background_color": "#fff",
         "theme_color": "#000",
         "status_bar": "default",


Test patch:
Repository: ephios-dev/ephios

Issue:
Cannot delete events with PlaceholderParticipations
IntegrityError at /events/delete
(1451, 'Cannot delete or update a parent row: a foreign key constraint fails (`juhsp_ephios`.`core_placeholderparticipation`, CONSTRAINT `core_placeholderpart_abstractparticipatio_e728e150_fk_abstractp` FOREIGN KEY (`abstractparticipation_ptr_id`) REFERENCES `abstractpa)')


Discussion:

Resolving patch:
diff --git a/ephios/core/models/events.py b/ephios/core/models/events.py
--- a/ephios/core/models/events.py
+++ b/ephios/core/models/events.py
@@ -174,6 +174,11 @@ def get_datetime_display(self):
         return f"{self.get_date_display()}, {self.get_time_display()}"
 
 
+# https://github.com/django-polymorphic/django-polymorphic/issues/229#issuecomment-398434412
+def NON_POLYMORPHIC_CASCADE(collector, field, sub_objs, using):
+    return models.CASCADE(collector, field, sub_objs.non_polymorphic(), using)
+
+
 class AbstractParticipation(DatetimeDisplayMixin, PolymorphicModel):
     class States(models.IntegerChoices):
         REQUESTED = 0, _("requested")
@@ -187,7 +192,10 @@ def labels_dict(cls):
             return dict(zip(cls.values, cls.labels))
 
     shift = ForeignKey(
-        "Shift", on_delete=models.CASCADE, verbose_name=_("shift"), related_name="participations"
+        "Shift",
+        on_delete=NON_POLYMORPHIC_CASCADE,
+        verbose_name=_("shift"),
+        related_name="participations",
     )
     state = IntegerField(_("state"), choices=States.choices, default=States.GETTING_DISPATCHED)
     data = models.JSONField(default=dict, verbose_name=_("Signup data"))
diff --git a/ephios/core/signup/disposition.py b/ephios/core/signup/disposition.py
--- a/ephios/core/signup/disposition.py
+++ b/ephios/core/signup/disposition.py
@@ -227,6 +227,7 @@ def post(self, request, *args, **kwargs):
         formset.save()
         self._send_participant_notifications(formset)
 
+        # non_polymorphic() needed because of https://github.com/django-polymorphic/django-polymorphic/issues/34
         self.object.participations.filter(
             state=AbstractParticipation.States.GETTING_DISPATCHED
         ).non_polymorphic().delete()


Test patch:
Repository: ephios-dev/ephios

Issue:
CoupledSignup does not report participant limits correctly
When having one shift with instant confirmation and a maximum number of participants and a second shift that is coupled to this shift, the event is reported to have free spots even if all spots are filled


Discussion:
think it should just return `SignupStats.ZERO`

Resolving patch:
diff --git a/ephios/core/signup/methods.py b/ephios/core/signup/methods.py
--- a/ephios/core/signup/methods.py
+++ b/ephios/core/signup/methods.py
@@ -755,7 +755,7 @@ def render_configuration_form(self, *args, form=None, **kwargs):
         return template
 
 
-@dataclasses.dataclass()
+@dataclasses.dataclass(frozen=True)
 class SignupStats:
     requested_count: int
     confirmed_count: int
diff --git a/ephios/plugins/basesignup/signup/coupled_signup.py b/ephios/plugins/basesignup/signup/coupled_signup.py
--- a/ephios/plugins/basesignup/signup/coupled_signup.py
+++ b/ephios/plugins/basesignup/signup/coupled_signup.py
@@ -1,3 +1,5 @@
+from dataclasses import replace
+
 from django import forms
 from django.db.models import Q
 from django.db.models.signals import post_save, pre_save
@@ -7,7 +9,7 @@
 from django.utils.translation import gettext_lazy as _
 
 from ephios.core.models import AbstractParticipation, Shift
-from ephios.core.signup.methods import ActionDisallowedError, BaseSignupMethod
+from ephios.core.signup.methods import ActionDisallowedError, BaseSignupMethod, SignupStats
 from ephios.plugins.basesignup.signup.common import RenderParticipationPillsShiftStateMixin
 
 
@@ -74,6 +76,14 @@ def _decline_checkers(self):
             self.signup_is_disabled,
         ]
 
+    def get_signup_stats(self) -> "SignupStats":
+        raw_signupstats = super().get_signup_stats()
+        return replace(
+            SignupStats.ZERO,
+            requested_count=raw_signupstats.requested_count,
+            confirmed_count=raw_signupstats.confirmed_count,
+        )
+
 
 @receiver(pre_save)
 def update_coupled_shifts(sender, instance, **kwargs):


Test patch:
diff --git a/tests/plugins/basesignup/test_coupled_signup.py b/tests/plugins/basesignup/test_coupled_signup.py
--- a/tests/plugins/basesignup/test_coupled_signup.py
+++ b/tests/plugins/basesignup/test_coupled_signup.py
@@ -65,3 +65,12 @@ def test_event_detail_with_missing_leader_shift(django_app, volunteer, event, cr
     create_coupled_shift()
     response = django_app.get(event.get_absolute_url(), user=volunteer)
     assert "leading shift is missing" in response
+
+
+def test_coupled_signupstats(django_app, volunteer, event, create_coupled_shift):
+    shift = event.shifts.first()
+    shift.signup_configuration["maximum_number_of_participants"] = 1
+    shift.save()
+    coupled_shift = create_coupled_shift()
+    signup_stats = event.get_signup_stats()
+    assert signup_stats.free == 1
Repository: ephios-dev/ephios

Issue:
Make minors identifiable on event detail page
As an Einsatzleiter, I want to quickly grasp which participants are younger than 18 years. For that purpose, I want to have the participation boxes on the event detail page/shift box to display a small warning/indication, e.g. a red corner or similar.


Discussion:

Resolving patch:
diff --git a/ephios/core/signup/participants.py b/ephios/core/signup/participants.py
--- a/ephios/core/signup/participants.py
+++ b/ephios/core/signup/participants.py
@@ -27,6 +27,12 @@ def get_age(self, today: date = None):
         today, born = today or date.today(), self.date_of_birth
         return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
 
+    @property
+    def is_minor(self):
+        if age := self.get_age():
+            return age < 18
+        return False
+
     def __str__(self):
         return f"{self.first_name} {self.last_name}"
 


Test patch:
Repository: ephios-dev/ephios

Issue:
StaticI18N catalog not loaded on some views
The shift edit views tries to load `/de.js` while other views like the instance settings are loading `/static/jsi18n/de/djangojs.js` correctly, despite originating from the same `<script type="text/javascript" src="{% statici18n LANGUAGE_CODE %}"></script>` in base.html


Discussion:
I couldn't reproduce this

Resolving patch:
diff --git a/ephios/plugins/simpleresource/forms.py b/ephios/plugins/simpleresource/forms.py
--- a/ephios/plugins/simpleresource/forms.py
+++ b/ephios/plugins/simpleresource/forms.py
@@ -1,3 +1,4 @@
+from crispy_forms.helper import FormHelper
 from django.forms import BaseModelFormSet, BooleanField, ModelForm, modelformset_factory
 from django.forms.formsets import DELETION_FIELD_NAME
 from django.utils.translation import gettext as _
@@ -17,6 +18,8 @@ class Meta:
 
     def __init__(self, *args, shift, **kwargs):
         self.shift = shift
+        self.helper = FormHelper()
+        self.helper.include_media = False
         try:
             kwargs.setdefault("instance", ResourceAllocation.objects.get(shift=shift))
         except ResourceAllocation.DoesNotExist:
diff --git a/ephios/plugins/simpleresource/models.py b/ephios/plugins/simpleresource/models.py
--- a/ephios/plugins/simpleresource/models.py
+++ b/ephios/plugins/simpleresource/models.py
@@ -1,10 +1,11 @@
 from django.db import models
+from django.utils.translation import gettext_lazy as _
 
 from ephios.core.models import Shift
 
 
 class ResourceCategory(models.Model):
-    name = models.CharField(max_length=50)
+    name = models.CharField(max_length=50, verbose_name=_("Name"))
 
     def __str__(self):
         # pylint: disable=invalid-str-returned
@@ -12,8 +13,10 @@ def __str__(self):
 
 
 class Resource(models.Model):
-    title = models.CharField(max_length=100)
-    category = models.ForeignKey(ResourceCategory, on_delete=models.CASCADE)
+    title = models.CharField(max_length=100, verbose_name=_("Title"))
+    category = models.ForeignKey(
+        ResourceCategory, on_delete=models.CASCADE, verbose_name=_("Category")
+    )
 
     def __str__(self):
         # pylint: disable=invalid-str-returned
@@ -22,7 +25,7 @@ def __str__(self):
 
 class ResourceAllocation(models.Model):
     shift = models.ForeignKey(Shift, on_delete=models.CASCADE)
-    resources = models.ManyToManyField(Resource, blank=True)
+    resources = models.ManyToManyField(Resource, blank=True, verbose_name=_("Resources"))
 
     def __str__(self):
         return f"Resource allocation for {self.shift}"


Test patch:
Repository: ephios-dev/ephios

Issue:
Change event type in event form
As a planner I want to change the event's type after creation so I can correct a mistake.


Discussion:

Resolving patch:
diff --git a/ephios/core/forms/events.py b/ephios/core/forms/events.py
--- a/ephios/core/forms/events.py
+++ b/ephios/core/forms/events.py
@@ -58,7 +58,7 @@ class EventForm(forms.ModelForm):
 
     class Meta:
         model = Event
-        fields = ["title", "description", "location"]
+        fields = ["title", "type", "description", "location"]
 
     def __init__(self, **kwargs):
         user = kwargs.pop("user")
@@ -94,6 +94,8 @@ def __init__(self, **kwargs):
 
         super().__init__(**kwargs)
 
+        if event is None:
+            self.fields.pop("type")
         self.fields["visible_for"].queryset = can_publish_for_groups
         self.fields["visible_for"].disabled = not can_publish_for_groups
         if self.locked_visible_for_groups:
@@ -105,7 +107,8 @@ def __init__(self, **kwargs):
             ).format(groups=", ".join(group.name for group in self.locked_visible_for_groups))
 
     def save(self, commit=True):
-        self.instance.type = self.eventtype
+        if not self.instance.pk:
+            self.instance.type = self.eventtype
         event: Event = super().save(commit=commit)
 
         add_log_recorder(event, PermissionLogRecorder("view_event", _("Visible for")))


Test patch:
diff --git a/tests/core/test_event_form.py b/tests/core/test_event_form.py
--- a/tests/core/test_event_form.py
+++ b/tests/core/test_event_form.py
@@ -28,6 +28,7 @@ def test_create_event(django_app, planner, superuser, service_event_type, groups
     shift_form["end_time"] = time(16, 0)
     event = shift_form.submit().follow().context["event"]
     assert event.title == "Seeed Concert"
+    assert event.type == service_event_type
     assert set(get_groups_with_perms(event, only_with_perms_in=["view_event"])) == {
         volunteers,
         planners,
@@ -121,3 +122,15 @@ def test_unsaved_event_warning(django_app, planner, groups, service_event_type):
     response = response.click("View")
     assert "This event has not been saved"
     assert not response.context["event"].active
+
+
+def test_change_event_type(django_app, planner, event, service_event_type, training_event_type):
+    assert event.type == service_event_type
+    event_form = django_app.get(
+        reverse("core:event_edit", kwargs=dict(pk=event.pk)),
+        user=planner,
+    ).form
+    event_form["type"] = training_event_type.pk
+    event_form.submit()
+    event.refresh_from_db()
+    assert event.type == training_event_type
Repository: ephios-dev/ephios

Issue:
Read env-file from environment variable location
currently, this is a great way to get a dev setup running quickly:

https://github.com/ephios-dev/ephios/blob/9f37e9efb0e3395d0b6c599a0230449430e6cbdd/ephios/settings.py#L22

on production it would be great if you could specify where the env file is located.


Discussion:

Resolving patch:
diff --git a/ephios/settings.py b/ephios/settings.py
--- a/ephios/settings.py
+++ b/ephios/settings.py
@@ -2,6 +2,7 @@
 import os
 from datetime import timedelta
 from email.utils import getaddresses
+from pathlib import Path
 
 import environ
 from cryptography.hazmat.primitives import serialization
@@ -14,12 +15,14 @@
 except ImportError:
     import importlib.metadata as importlib_metadata
 
-
 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
 env = environ.Env()
 # for syntax see https://django-environ.readthedocs.io/en/latest/
-environ.Env.read_env(env_file=os.path.join(BASE_DIR, ".env"))
+# read env file from ENV_PATH or fall back to a .env file in the project root
+env_path = env.str("ENV_PATH", default=os.path.join(BASE_DIR, ".env"))
+print(f"Loading ephios environment from {Path(env_path).absolute()}")
+environ.Env.read_env(env_file=env_path)
 
 DATA_DIR = env.str("DATA_DIR", default=os.path.join(BASE_DIR, "data"))
 if not os.path.exists(DATA_DIR):
diff --git a/ephios/urls.py b/ephios/urls.py
--- a/ephios/urls.py
+++ b/ephios/urls.py
@@ -7,7 +7,7 @@
     1. Add an import:  from my_app import views
     2. Add a URL to urlpatterns:  path('', views.home, name='home')
 Class-based views
-    1. Add an import:  from other_app.views import Home
+    1. Add an import: from other_app.views import Home
     2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
 Including another URLconf
     1. Import the include() function: from django.urls import include, path


Test patch:
Repository: ephios-dev/ephios

Issue:
Improve explanation for page slugs
Users did not understand what the page slug means. We should provide a help text, either directly or in the docs.


Discussion:

Resolving patch:
diff --git a/ephios/plugins/pages/models.py b/ephios/plugins/pages/models.py
--- a/ephios/plugins/pages/models.py
+++ b/ephios/plugins/pages/models.py
@@ -5,7 +5,12 @@
 class Page(models.Model):
     title = models.CharField(verbose_name=_("Title"), max_length=250)
     content = models.TextField(_("Content"), blank=True)
-    slug = models.SlugField(_("Slug"), max_length=250, unique=True)
+    slug = models.SlugField(
+        _("URL slug"),
+        help_text=_("The slug is used to generate the page's URL."),
+        max_length=250,
+        unique=True,
+    )
     show_in_footer = models.BooleanField(_("Show in footer"), default=False)
     publicly_visible = models.BooleanField(_("Publicly visible"), default=False)
 
diff --git a/ephios/plugins/pages/views.py b/ephios/plugins/pages/views.py
--- a/ephios/plugins/pages/views.py
+++ b/ephios/plugins/pages/views.py
@@ -29,7 +29,7 @@ def dispatch(self, request, *args, **kwargs):
 class PageCreateView(CustomPermissionRequiredMixin, CreateView):
     model = Page
     permission_required = "pages.add_page"
-    fields = ["title", "content", "slug", "show_in_footer", "publicly_visible"]
+    fields = ["title", "slug", "content", "show_in_footer", "publicly_visible"]
 
     def get_success_url(self):
         messages.success(self.request, _("Page saved successfully."))
@@ -39,7 +39,7 @@ def get_success_url(self):
 class PageUpdateView(CustomPermissionRequiredMixin, UpdateView):
     model = Page
     permission_required = "pages.change_page"
-    fields = ["title", "content", "slug", "show_in_footer", "publicly_visible"]
+    fields = ["title", "slug", "content", "show_in_footer", "publicly_visible"]
 
     def get_success_url(self):
         messages.success(self.request, _("Page saved successfully."))


Test patch:
Repository: ephios-dev/ephios

Issue:
Custom participant notification is sent twice
When a user is both participating and resposible for an event, they will receive custom notification texts twice, once for each role. Instead, they should only receive one notification.


Discussion:

Resolving patch:
diff --git a/ephios/core/services/notifications/types.py b/ephios/core/services/notifications/types.py
--- a/ephios/core/services/notifications/types.py
+++ b/ephios/core/services/notifications/types.py
@@ -452,11 +452,16 @@ class CustomEventParticipantNotification(AbstractNotificationHandler):
     @classmethod
     def send(cls, event: Event, content: str):
         participants = set()
+        responsible_users = get_users_with_perms(
+            event, with_superusers=False, only_with_perms_in=["change_event"]
+        )
         for shift in event.shifts.all():
             participants.update(shift.get_participants())
         notifications = []
         for participant in participants:
             user = participant.user if isinstance(participant, LocalUserParticipant) else None
+            if user in responsible_users:
+                continue
             notifications.append(
                 Notification(
                     slug=cls.slug,
@@ -469,9 +474,7 @@ def send(cls, event: Event, content: str):
                     },
                 )
             )
-        for responsible in get_users_with_perms(
-            event, with_superusers=False, only_with_perms_in=["change_event"]
-        ):
+        for responsible in responsible_users:
             notifications.append(
                 Notification(
                     slug=cls.slug,


Test patch:
diff --git a/tests/core/test_event_notifications.py b/tests/core/test_event_notifications.py
--- a/tests/core/test_event_notifications.py
+++ b/tests/core/test_event_notifications.py
@@ -59,3 +59,22 @@ def test_mail_participants_content_required(
         response = form.submit()
         assert response.status_code == 200
         assert Notification.objects.count() == 0
+
+    def test_participants_mail_is_not_duplicated_for_participating_responsible(
+        self, django_app, event, planner, groups
+    ):
+        LocalParticipation.objects.create(
+            shift=event.shifts.first(),
+            user=planner,
+            state=AbstractParticipation.States.CONFIRMED,
+        )
+        form = django_app.get(
+            reverse("core:event_notifications", kwargs=dict(pk=event.id)), user=planner
+        ).form
+        form["action"] = EventNotificationForm.PARTICIPANTS
+        form["mail_content"] = "hey Franz was here"
+        form.submit()
+        number_of_responsible_users = len(
+            get_users_with_perms(event, only_with_perms_in=["change_event"])
+        )
+        assert Notification.objects.count() == number_of_responsible_users
Repository: ephios-dev/ephios

Issue:
select2 bootstrap5 theme not working
The select2 selects don't seem to look like native bootstrap selects. The theming seems broken.
Find some select2s at Disposition, LogFilter, etc.

correct with native select:
![image](https://user-images.githubusercontent.com/5970076/211670960-4d604f00-11bb-429b-b8e4-45a994b29660.png)

select2:
![image](https://user-images.githubusercontent.com/5970076/211671022-730e7294-f907-48ba-a06b-0d8278c83e58.png)



Discussion:

Resolving patch:
diff --git a/ephios/settings.py b/ephios/settings.py
--- a/ephios/settings.py
+++ b/ephios/settings.py
@@ -151,7 +151,12 @@
 DATABASES = {"default": env.db_url()}
 
 # Caches
-CACHES = {"default": env.cache_url(default="locmemcache://")}
+CACHES = {
+    "default": {
+        "TIMEOUT": 60 * 60 * 12,  # 12 hours
+        **env.cache_url(default="locmemcache://"),
+    }
+}
 SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
 DJANGO_REDIS_IGNORE_EXCEPTIONS = True
 DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = True
@@ -304,6 +309,8 @@
 SELECT2_JS = ""
 SELECT2_CSS = ""
 SELECT2_I18N_PATH = ""
+SELECT2_CACHE_BACKEND = "default"
+SELECT2_THEME = "bootstrap-5"
 
 # django-debug-toolbar
 if DEBUG and env.bool("DEBUG_TOOLBAR", True):


Test patch:
Repository: ephios-dev/ephios

Issue:
Strict Transport Security (HSTS) duration ist too short
**Describe the bug**
Using tools like Mozilla Observatory to check the security of an Ephios instance it comes the error that HSTS is implemented incorrectly, because the specified duration is too short (max-age parameter). This is defined in the settings.py with 360 secounds. Normally, a time indication of half to one year is recommended. 

**To Reproduce**
Steps to reproduce the behavior:
- Got to  https://observatory.mozilla.org/ or https://www.ssllabs.com/ssltest/
- Enter the UrL of the Ephios instance and start the test. 
- Lock at HTTP Strict Transport Security 

**Expected behavior**
The variable SECURE_HSTS_SECONDS in settings.py must be set to a higher value, at least 15552000 seconds (better 31536000), by default. 

**Environment**
I'm using Ubuntu 22.04 server edition with Apache2 as reverse Proxy.


Discussion:

Resolving patch:
diff --git a/ephios/settings.py b/ephios/settings.py
--- a/ephios/settings.py
+++ b/ephios/settings.py
@@ -46,11 +46,13 @@
     X_FRAME_OPTIONS = "DENY"
     SECURE_CONTENT_TYPE_NOSNIFF = True
     SECURE_SSL_REDIRECT = True
-    SECURE_HSTS_SECONDS = 3600
-    SECURE_HSTS_INCLUDE_SUBDOMAINS = True
     SECURE_REFERRER_POLICY = "same-origin"
+    # 1 day by default, change to 1 year in production (see deployment docs)
+    SECURE_HSTS_SECONDS = env.int("SECURE_HSTS_SECONDS", default=3600 * 24)
+    SECURE_HSTS_INCLUDE_SUBDOMAINS = env.bool("SECURE_HSTS_INCLUDE_SUBDOMAINS", default=False)
+    SECURE_HSTS_PRELOAD = env.bool("SECURE_HSTS_PRELOAD", default=False)
 
-# Application definition
+    CONN_MAX_AGE = env.int("CONN_MAX_AGE", default=0)
 
 INSTALLED_APPS = [
     # we need to import our own modules before everything else to allow template


Test patch:
Repository: ephios-dev/ephios

Issue:
Notification Mails detected as SPAM
**Describe the bug**
This is likely specific to the exact email setup users have but in my case, the notification mails in my case are caught by the SpamAssassin rule `TO_NO_BRKTS_HTML_IMG` which is discussed [here](https://stackoverflow.com/questions/40082594/meaning-of-to-no-brkts-html-img-in-spamassassin). As a fix for this, it would be useful if ephios used the configured first and last name to properly address the users in the `To:` field of the email.   

**To Reproduce**
Make ephios send a notification email.

**Expected behavior**
Emails should arrive in my inbox without being detected as SPAM. This could be done by modifying the `To:` field. 

**Screenshots**
\-

**Environment**
An email SPAM filter using SpamAssassin

**Additional context**
\-



Discussion:
You mentioned more factors in other channels like only having one embedded image. Can you explain that here too?
Basically, all my knowledge comes from the StackOverflow thread linked in the initial issue, which also goes into a single image and an HTML MIME part being in the mail. 

Resolving patch:
diff --git a/ephios/core/services/notifications/backends.py b/ephios/core/services/notifications/backends.py
--- a/ephios/core/services/notifications/backends.py
+++ b/ephios/core/services/notifications/backends.py
@@ -91,7 +91,9 @@ def can_send(cls, notification):
 
     @classmethod
     def _get_mailaddress(cls, notification):
-        return notification.user.email if notification.user else notification.data.get("email")
+        if notification.user:
+            return f"{notification.user.get_full_name()} <{notification.user.email}>"
+        return notification.data.get("email")
 
     @classmethod
     def send(cls, notification):


Test patch: