Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mid October Update #903

Merged
merged 69 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
6aeef42
Feat(kontres)/add image to bookable item (#785)
eriskjel Mar 23, 2024
c9b5975
Feat(kontres)/add approved by (#786)
eriskjel Apr 6, 2024
28067fa
Create minutes for Codex (#787)
MadsNyl Apr 8, 2024
9e4ff76
Feat(minute)/viewset (#788)
MadsNyl Apr 8, 2024
0544b2f
Feat(kontres)/add notification (#790)
eriskjel Apr 10, 2024
ae483dd
Memberships with fines activated (#791)
MadsNyl Apr 12, 2024
95ef58c
fixed merge
MadsNyl Apr 13, 2024
bfa2299
Feat(user)/user bio (#758)
haruixu Apr 16, 2024
3f56496
Update CHANGELOG.md
MadsNyl Apr 16, 2024
064da8a
added filter for allowed photos for user (#794)
MadsNyl Apr 17, 2024
81a3c5e
Upped payment time when coming from waiting list (#796)
MadsNyl Apr 17, 2024
a583c45
fixed paymenttime saved to db (#798)
MadsNyl Apr 17, 2024
0f24085
fixed bug (#800)
MadsNyl Apr 17, 2024
8a3cfd4
fixed mergeconflict
MadsNyl Apr 17, 2024
e597268
Disallow users to unregister when payment is done (#802)
MadsNyl May 1, 2024
3b84765
update changelog
MadsNyl May 1, 2024
f21e0ab
Added serializer for category in event (#804)
MadsNyl May 2, 2024
e30f102
fixed merge
MadsNyl May 2, 2024
64d717c
Permission middelware (#806)
MadsNyl Jun 9, 2024
ed57afc
Permission refactor of QR Codes (#807)
MadsNyl Jun 9, 2024
ab3cf15
Permissions for payment orders (#808)
MadsNyl Jun 10, 2024
062193d
chore(iac): updated docs and force https (#810)
martcl Jul 26, 2024
23b310a
feat(iac): add terraform guardrails so index don't nuke our infra (#811)
martcl Jul 26, 2024
fa31096
Automatic registration for new users with Feide (#809)
MadsNyl Jul 30, 2024
bef294d
changelog update
MadsNyl Jul 30, 2024
4db63b5
Merge branch 'dev' of https://github.com/TIHLDE/Lepton into dev
MadsNyl Jul 30, 2024
fcce5e8
Feide env variables Terraform (#814)
MadsNyl Jul 31, 2024
514a26b
added delete endpoint for file (#815)
MadsNyl Aug 4, 2024
d3e8e9a
Update CHANGELOG.md
MadsNyl Aug 4, 2024
a02af01
merge conflict
MadsNyl Aug 4, 2024
c9bf357
format
MadsNyl Aug 4, 2024
1a7dff4
format
MadsNyl Aug 4, 2024
f086ac2
fixed permission for committee leaders for group forms
MadsNyl Aug 18, 2024
ec03558
updated csv for forms (#818)
MadsNyl Aug 18, 2024
0526f02
Permission for group forms and news (#820)
MadsNyl Aug 21, 2024
39f7cd0
merge and changelog
MadsNyl Aug 21, 2024
f40fba0
Update reservation_seralizer.py (#822)
MindChirp Aug 27, 2024
1f03c30
Group ownership of Minutes (#847)
MadsNyl Sep 14, 2024
d7e9b91
Changed endpoint response (#846)
MindChirp Sep 14, 2024
b574fdc
updated changelog.md
MadsNyl Sep 14, 2024
8b83718
Merge branch 'dev' of https://github.com/TIHLDE/Lepton into dev
MadsNyl Sep 14, 2024
bf42e7a
merge
MadsNyl Sep 14, 2024
99ba049
finished events now appear in the correct order (newest first) (#849)
EmilJohns1 Sep 17, 2024
a17c46d
Implement Swagger (#858)
MadsNyl Sep 20, 2024
496d536
Swagger GitHub Action (#860)
MadsNyl Sep 21, 2024
0c1de71
Add new app (#862)
MadsNyl Sep 23, 2024
9a19163
Upgrade all dependencies to latest (#857)
Tmpecho Sep 23, 2024
f991cec
Allow HS members to create a new group (#864)
Tmpecho Sep 23, 2024
1166728
App Script Fix (#875)
MadsNyl Sep 24, 2024
0771f2d
Event registration payment orders (#876)
MadsNyl Sep 24, 2024
5b06e4e
chore(deps): update python-dotenv requirement from ~=0.21.1 to ~=1.0.…
dependabot[bot] Sep 24, 2024
01fdcc3
Chore(deps): Bump sentry-sdk from 1.14.0 to 2.8.0 (#866)
dependabot[bot] Sep 24, 2024
192e7ee
Codex Course (#852)
MadsNyl Sep 24, 2024
26fb8d1
merge conflict and update CHANGELOG.md
MadsNyl Sep 25, 2024
7d5c0f1
fixed filtering of groups and made tests
MadsNyl Sep 25, 2024
20aabb4
merge conflict
MadsNyl Sep 25, 2024
edb007f
fixed list endpoint for cheatsheets
MadsNyl Sep 25, 2024
9236863
trigger
MadsNyl Sep 25, 2024
3b9004e
format
MadsNyl Sep 25, 2024
527d0c2
Endpoint for sending email (#883)
EmilJohns1 Sep 26, 2024
d0a0c07
chore(deps): bump black from 24.3.0 to 24.8.0 (#869)
dependabot[bot] Sep 27, 2024
81f7c7e
chore(deps): Bump azure-storage-blob from 12.13.1 to 12.23.1 (#885)
dependabot[bot] Sep 30, 2024
019d273
Added admin.py to root in new app, and added app dir to tests (#892)
MadsNyl Oct 1, 2024
14edd06
Description to forms (#894)
MadsNyl Oct 1, 2024
1a49b08
Bug report system (#865)
josefinearntsen Oct 11, 2024
1393885
Renaming of index app to feedback (#901)
MadsNyl Oct 11, 2024
c44e910
chore(deps): bump django from 4.2.16 to 5.1.1 (#889)
dependabot[bot] Oct 11, 2024
17b2483
Event registration race condition (#902)
MadsNyl Oct 11, 2024
119bbdb
upadted CHANGELOG.md
MadsNyl Oct 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@

## Neste versjon

## Versjon 2024.09.25
## Versjon 2024.10.11
- ✨ **Tilbakemelding-funksjon**. Man kan nå opprette tilbakemeldinger for bugs og idé.
- 🦟 **Påmelding**. Det vil nå ikke være mulig med flere påmeldinger på et arrangement enn maksgrensen.

## Versjon 2024.09.25
- ✨**Codex arrangementer**. Det kan nå opprettes arrangementer på Codex, som medlemmer av Codex kan melde seg på.
- ⚡**Betalingsordre**. Man kan nå se historikk over betalingsordre for en påmelding til et arrangement.
- ✨**Gruppe**. HS kan nå opprette en ny gruppe.
Expand Down
15 changes: 15 additions & 0 deletions app/common/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ def get_user_class_number(user_class: NativeUserClass) -> int:
return int(_class.split(".")[0])


def get_user_class_name(user_class: int):
if user_class == 1:
return NativeUserClass.FIRST
elif user_class == 2:
return NativeUserClass.SECOND
elif user_class == 3:
return NativeUserClass.THIRD
elif user_class == 4:
return NativeUserClass.FOURTH
elif user_class == 5:
return NativeUserClass.FIFTH
else:
return NativeUserClass.ALUMNI


# This can't be removed because it is used in the migrations. It is not used in the code
class UserStudy(ChoiceEnum):
DATAING = "Dataingeniør"
Expand Down
6 changes: 6 additions & 0 deletions app/communication/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ class UserNotificationSettingType(models.TextChoices):
RESERVATION_NEW = "RESERVATION NEW", "Ny reservasjon"
RESERVATION_APPROVED = "RESERVATION APPROVED", "Godkjent reservasjon"
RESERVATION_CANCELLED = "RESERVATION CANCELLED", "Avslått reservasjon"
KONTRES = "KONTRES", "Kontres"
BLITZED = "BLITZED", "Blitzed"

@classmethod
def get_kontres_and_blitzed(cls):
return [cls.KONTRES, cls.BLITZED]
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Generated by Django 4.2.5 on 2024-09-23 18:02

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("communication", "0010_alter_usernotificationsetting_notification_type"),
]

operations = [
migrations.AlterField(
model_name="usernotificationsetting",
name="notification_type",
field=models.CharField(
choices=[
("REGISTRATION", "Påmeldingsoppdateringer"),
("UNREGISTRATION", "Avmeldingsoppdateringer"),
("STRIKE", "Prikkoppdateringer"),
("EVENT_SIGN_UP_START", "Arrangementer - påmeldingsstart"),
("EVENT_SIGN_OFF_DEADLINE", "Arrangementer - avmeldingsfrist"),
("EVENT_EVALUATION", "Arrangementer - evaluering"),
("EVENT_INFO", "Arrangementer - info fra arrangør"),
("FINE", "Grupper - bot"),
("GROUP_MEMBERSHIP", "Grupper - medlemsskap"),
("OTHER", "Andre"),
("RESERVATION NEW", "Ny reservasjon"),
("RESERVATION APPROVED", "Godkjent reservasjon"),
("RESERVATION CANCELLED", "Avslått reservasjon"),
("KONTRES", "Kontres"),
("BLITZED", "Blitzed"),
],
max_length=30,
),
),
]
216 changes: 216 additions & 0 deletions app/communication/tests/test_send_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import os
from unittest.mock import patch

from rest_framework import status

import pytest

from app.communication.enums import UserNotificationSettingType
from app.communication.notifier import Notify
from app.content.factories import UserFactory
from app.util.test_utils import get_api_client

EMAIL_URL = "/send-email/"
EMAIL_API_KEY = os.environ.get("EMAIL_API_KEY")


def _get_email_url():
return f"{EMAIL_URL}"


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
def test_send_email_success(mock_send):
"""
Test that the send_email endpoint sends an email successfully.
"""
test_user = UserFactory()

data = {
"user_id_list": [test_user.user_id],
"notification_type": "KONTRES",
"title": "Test Notification",
"paragraphs": ["This is a test paragraph.", "This is another paragraph."],
}

client = get_api_client(user=test_user)
url = _get_email_url()
headers = {"api_key": EMAIL_API_KEY}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_201_CREATED
mock_send.assert_called_once()


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
def test_send_email_fails_when_field_missing(mock_send):
"""
Test that the send_email endpoint returns 400 when one of the fields is missing.
"""
test_user = UserFactory()

data = {
"user_id_list": [test_user.user_id],
"title": "Test Notification",
"paragraphs": ["This is a test paragraph.", "This is another paragraph."],
}

client = get_api_client(user=test_user)
url = _get_email_url()
headers = {"api_key": EMAIL_API_KEY}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_400_BAD_REQUEST
mock_send.assert_not_called()


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
def test_send_email_fails_when_wrong_api_key(mock_send):
"""
Test that the send_email endpoint returns 403 when the API key is invalid.
"""
test_user = UserFactory()

data = {
"user_id_list": [test_user.user_id],
"notification_type": "KONTRES",
"title": "Test Notification",
"paragraphs": ["This is a test paragraph.", "This is another paragraph."],
}

client = get_api_client(user=test_user)
url = _get_email_url()
headers = {"api_key": "wrong_key"}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_403_FORBIDDEN
mock_send.assert_not_called()


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
def test_send_email_fails_when_user_id_invalid(mock_send):
"""
Test that the send_email endpoint returns 404 when the user id is invalid.
"""
test_user = UserFactory()

data = {
"user_id_list": [999],
"notification_type": "KONTRES",
"title": "Test Notification",
"paragraphs": ["This is a test paragraph.", "This is another paragraph."],
}

client = get_api_client(user=test_user)
url = _get_email_url()
headers = {"api_key": EMAIL_API_KEY}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_404_NOT_FOUND
mock_send.assert_not_called()


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
@pytest.mark.parametrize(
"notification_type", UserNotificationSettingType.get_kontres_and_blitzed()
)
def test_email_success_with_kontres_and_blitzed(mock_send, notification_type):
"""
Tests that the send_email endpoint works with both KONTRES and BLITZED notification types.
"""
test_user = UserFactory()

data = {
"user_id_list": [test_user.user_id],
"notification_type": notification_type,
"title": "Test Notification",
"paragraphs": ["This is a test paragraph.", "This is another paragraph."],
}

client = get_api_client(user=test_user)
url = _get_email_url()
headers = {"api_key": EMAIL_API_KEY}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_201_CREATED
mock_send.assert_called_once()


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
def test_send_email_success_with_user_id_list(mock_send):
"""
Test that the send_email endpoint sends an email successfully to a list of users.
"""
test_user1 = UserFactory()
test_user2 = UserFactory()
test_user3 = UserFactory()

data = {
"user_id_list": [
test_user.user_id for test_user in [test_user1, test_user2, test_user3]
],
"notification_type": "KONTRES",
"title": "Test Notification",
"paragraphs": ["This is a test paragraph.", "This is another paragraph."],
}

client = get_api_client(user=test_user1)
url = _get_email_url()
headers = {"api_key": EMAIL_API_KEY}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_201_CREATED
mock_send.assert_called_once()


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
def test_send_email_fails_when_user_id_list_is_empty(mock_send):
"""
Test that the send_email endpoint returns 400 when the user id list is empty.
"""
test_user = UserFactory()

data = {
"user_id_list": [],
"notification_type": "KONTRES",
"title": "Test Notification",
"paragraphs": ["This is a test paragraph.", "This is another paragraph."],
}

client = get_api_client(user=test_user)
url = _get_email_url()
headers = {"api_key": EMAIL_API_KEY}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_400_BAD_REQUEST
mock_send.assert_not_called()


@pytest.mark.django_db
@patch.object(Notify, "send", return_value=None)
def test_send_email_fails_when_user_paragraph_list_is_empty(mock_send):
"""
Test that the send_email endpoint returns 400 when the paragraph list is empty.
"""
test_user = UserFactory()

data = {
"user_id_list": [test_user.user_id],
"notification_type": "KONTRES",
"title": "Test Notification",
"paragraphs": [],
}

client = get_api_client(user=test_user)
url = _get_email_url()
headers = {"api_key": EMAIL_API_KEY}
response = client.post(url, data, format="json", **headers)

assert response.status_code == status.HTTP_400_BAD_REQUEST
mock_send.assert_not_called()
1 change: 1 addition & 0 deletions app/content/serializers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ class Meta:
"user_id",
"first_name",
"last_name",
"image",
)


Expand Down
2 changes: 2 additions & 0 deletions app/content/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
accept_form,
delete,
register_with_feide,
send_email,
upload,
)

Expand Down Expand Up @@ -53,6 +54,7 @@
re_path(r"", include(router.urls)),
path("accept-form/", accept_form),
path("upload/", upload),
path("send-email/", send_email),
path("delete-file/<str:container_name>/<str:blob_name>/", delete),
path("feide/", register_with_feide),
re_path(r"users/(?P<user_id>[^/.]+)/events.ics", UserCalendarEvents()),
Expand Down
1 change: 1 addition & 0 deletions app/content/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
from app.content.views.logentry import LogEntryViewSet
from app.content.views.minute import MinuteViewSet
from app.content.views.feide import register_with_feide
from app.content.views.send_email import send_email
6 changes: 4 additions & 2 deletions app/content/views/cheatsheet.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from sentry_sdk import capture_exception

from app.common.enums import UserClass, UserStudy
from app.common.enums import NativeUserStudy as UserStudy
from app.common.enums import get_user_class_name
from app.common.pagination import BasePagination
from app.common.permissions import BasicViewPermission, is_admin_user
from app.common.viewsets import BaseViewSet
Expand All @@ -24,8 +25,9 @@ class CheatsheetViewSet(BaseViewSet):

def get_object(self):
if "pk" not in self.kwargs:
grade = get_user_class_name(int(self.kwargs["grade"]))
return self.filter_queryset(self.queryset).filter(
grade=UserClass(int(self.kwargs["grade"])),
grade=grade,
study=UserStudy[self.kwargs["study"]],
)

Expand Down
4 changes: 3 additions & 1 deletion app/content/views/registration.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import uuid

from django.db.transaction import atomic
from django.shortcuts import get_object_or_404
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework import filters, status
Expand Down Expand Up @@ -48,6 +49,7 @@ def _is_own_registration(self):
def _is_not_own_registration(self):
return not self._is_own_registration()

@atomic
def create(self, request, *args, **kwargs):
"""Register the current user for the given event."""

Expand All @@ -68,7 +70,7 @@ def create(self, request, *args, **kwargs):
serializer.is_valid(raise_exception=True)

event_id = self.kwargs.get("event_id", None)
event = Event.objects.get(pk=event_id)
event = Event.objects.select_for_update().get(pk=event_id)

registration = super().perform_create(
serializer, event=event, user=request.user
Expand Down
Loading
Loading