Skip to content

Commit

Permalink
March update (#781)
Browse files Browse the repository at this point in the history
* created endpoint for listing and destroying orders

* Infrastructure as Code using terraform (#684)

* feat: initial terraform

* chore: add basic documentation

* chore: more documentation

* Added IaC setup for Lepton

* added checov github action

* chekov allow softfail

* chore: database name

* hack to allow azure container apps managed certificates

* small changes

* tweeking values

* chore: revert changes to makefile

* chore: make format

* chore: bump build actions (#755)

* Feat(payment)/orders (#757)

* added filtersearch

* added filter

* added filter and listing

* Add status field to the ordering filter and fix retrieve method in OrderViewSet

* Refactor order filters and views

* Add is_index_user function to check if user is in Index

* Refactor order factory and serializers, add update endpoint for orders

* Add admin group user permission to order views and tests

* added permission checks for order model and removed from order viewset (#760)

* added permission checks for order model and removed from order viewset

* format

* fixed string representation for orders (#764)

* removed bug that deleted paid event if event is updated. added more i… (#765)

* removed bug that deleted paid event if event is updated. added more info to paid_event in adminpanel

* format

* Update CHANGELOG.md (#766)

* Feat(kontres)/initial setup (#720)

* Initial setup

* Update settings.py

* created draft for reservation class and state enum

* config for kontres

* created endpoint for creating new reservation

* created serializer for create_reservation

* added create_reservation to url path

* added admin.py to implement admin panel logic

* added __Str__ for admin panel

* added seperate class for a bookable item

* made "Kontoret" default value of a reservation for now and added migration

* removed unnecessary code

* added endpoint to edit a reservation, lacking request validation

* added endpoint to fetch all reservations

* updated urls.py with the newest endpoints

* code cleanup

* created model test for reservation class

* added bookable item object to reservation serializer

* created test for creating reservations

* added clean and self to reservation class

* added bookable item serializer

* added som error handling to fetch_all_reservations.py

* created endpoint to fetch reseervation by id

* modifies urls.py to accomodate changes in endpoints regarding queries and arguments

* removed the default value for bookable item in reservation

* every new reservation will now automatically be pending

* fetch_reservation.py now uses url argument instead of query parameter

* fixed bug in reservation_seralizer.py regarding bookableitem id

* fixed and added more tests in test_create_reservation.py

* created pytests for editing a reservation

* created pytests for fetching all reservations

* created pytests for fetching a reservation by 1

* made toString method in reservation model cleaner

* combined files to make one common reservation endpoint

* adjusted tests to accommodate to endpoint url

* transitioned to uuid for reservation and bookable item class

* fixed tests to accommodate uuid

* created endpoint to fetch all bookable items

* modified urls.py to accommodate uuid and new endpoint

* removed old and seperate endpoint files

* added uuid to admin panel

* added error handling in reservation view

* initial commit

* all new reservations will be pending

* added test for bookable items

* deleted old endpoint and url model

* fixed code for pr

* fixed kontres conventions

* formatting

* added queryset to reservation model

* fixed imports

* rename

* re-migrated

* formatting

* moved tests to correct place, and refactored to use factories and conftest.py

* added reservation and bookable item factories to conftest.py

* created factories

* fixed migration issues

* fixed packaging location

* removed comments

* added read and write access specifications to reservation and bookable item model

* added endpoint guards

* fixed tests

* formatting

* fixed tests

* refactored permission system

* refactored reservation model with correct permission system

* fixed viewset to accomodate new permission system

* removed unnecessary test

* fixed old tests to accomodate new permission system, as well as added new ones

* added extensive validation logic to prevent overlapping reservations etc

* formatting for pr

* removed relative import

* linting

* removed necessary code

* rewrote reservation queryset

* made reservation factory use enums for state

* removed necessary state validation

* fixed tests

* formatting

* linting

* Added new field to reservation model

* Trigger Build

* Trigger Build

* format and closed INSTALLED_APPS list

* closed urls list

* fixed description model bug as result of git issues

* added class methods to bookable_item model

* reformated permission logic

* removed uneccessary code

* translated error messages to norwegian

* reformated tests to fit new viewset and permission logic

* linting

* changed from write to update permission and added status code on reponse on update view

* creating a reservation will now use userId from request, and ignore any other attempt

* users are now unable to modify reservation after it has been confirmed. also fixed permission logic in update method

* added tests to make sure users cannot change their reservation after is has been confirmed

* linting

---------

Co-authored-by: Frikk Balder <[email protected]>
Co-authored-by: ConradOsvik <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>

* LogEntry viewset and fix (#768)

added viewset and serializer for logentry. Also remove date_hierachy in admin register for LogEntry

* Feat(kontres)/add group to create reservation (#769)

* fixed delete object permission

* added group field to reservation model

* added context to serializer

* added group validation logic to serializer

* created tests for group logic on reservation model

* fixed seralizer complexity by splitting into methods

* fixed time issue on tests

* linting

* fixed bug where bookable item was not properly inserted as payload in 2 tests

* removed unnecessary assertion against database

* fixed destroy logic in viewset and model

* Feat(kontres)/return full objects in response (#770)

* reservation response will now include the full objects for author, bookable_item and group

* reservation response will now include the full objects for author, bookable_item and group

* git aids

* fixed some more git aids

* fixed logic in serializer method for validating time (#771)

* fixed logic in serializer method for validating time

* skip unfinished test - waiting for updated viewset logic

* Update pull_request_template.md (#774)

* Fix(event)/fix priority waiting number (#775)

* fix(kontres)/added basic viewset to bookable item (#773)

* Removed SQL logging settings (#776)

removed logging settings

* fixed bug of payment countdown for registrations from waitlist to queue (#778)

* Update CHANGELOG.md (#779)

* Feat(kontres)/add endpoint for my reservations (#777)

* added endpoint to fetch /me/reservations

* added tests for /me/reservations endpoint

* removed logging statement

* admin can now fetch all reservations by user

* created tests for admin fetching reservations by user

---------

Co-authored-by: Martin Clementz <[email protected]>
Co-authored-by: Erik Skjellevik <[email protected]>
Co-authored-by: Frikk Balder <[email protected]>
Co-authored-by: ConradOsvik <[email protected]>
  • Loading branch information
5 people authored Mar 11, 2024
1 parent ec473c2 commit 8347ca8
Show file tree
Hide file tree
Showing 44 changed files with 2,136 additions and 27 deletions.
7 changes: 3 additions & 4 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
## Proposed changes
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue.
Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. Remove this part with the description.

Issue number: closes #
Issue number: closes # (remove if not an issue)


## Pull request checklist

Please check if your PR fulfills the following requirements:
- [ ] CHANGELOG.md has been updated. [Guide](https://tihlde.slab.com/posts/changelog-z8hybjom)
- [ ] Tests for the changes have been added (for bug fixes / features)
- [ ] Docs have been reviewed and added / updated if needed (for bug fixes / features)
- [ ] API docs on [Codex](https://codex.tihlde.org/contributing) have been reviewed and added / updated if needed (for bug fixes / features)
- [ ] The fixtures have been updated if needed (for migrations)

## Further comments
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@

## Neste versjon

## Versjon 2023.03.11
- 🦟 **Vipps** Brukere som kommer fra venteliste vil nå få en payment countdown startet, slik at de blir kastet ut hvis de ikke betaler.
-**Venteliste** Brukere vil nå se sin reelle ventelisteplass som tar hensyn til prioriteringer.
- 🎨 **Logging** SQL Debug for pytest er skrudd av.
-**Kontres** Endepunkter for reservasjoner av utstyr og kontor.

## Versjon 2023.02.07
- 🦟 **Vipps** Brukere kan nå oppdatere betalt arrangement, uten at det betalte arrangementet blir slettet.

Expand Down
3 changes: 2 additions & 1 deletion app/content/admin/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ def has_delete_permission(self, request, obj=None):
class LogEntryAdmin(admin.ModelAdmin):
actions = None

date_hierarchy = "action_time"
# This breaks the admin panel becaause of the new DB is not configured
# date_hierarchy = "action_time"

list_filter = ["user", "content_type", "action_flag"]

Expand Down
1 change: 1 addition & 0 deletions app/content/factories/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
from app.content.factories.toddel_factory import ToddelFactory
from app.content.factories.priority_pool_factory import PriorityPoolFactory
from app.content.factories.qr_code_factory import QRCodeFactory
from app.content.factories.logentry_factory import LogEntryFactory
19 changes: 19 additions & 0 deletions app/content/factories/logentry_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.contrib.admin.models import LogEntry
from django.utils import timezone

import factory
from factory.django import DjangoModelFactory

from app.content.factories.user_factory import UserFactory


class LogEntryFactory(DjangoModelFactory):
class Meta:
model = LogEntry

action_time = timezone.now()
user = factory.SubFactory(UserFactory)
content_type = None
object_id = 1
object_repr = "Test"
action_flag = 1
50 changes: 34 additions & 16 deletions app/content/models/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,7 @@ def delete(self, *args, **kwargs):
if moved_registration:
moved_registration.save()

if (
moved_registration.event.is_paid_event
and not moved_registration.is_on_wait
):
if moved_registration.event.is_paid_event:
try:
start_payment_countdown(
moved_registration.event, moved_registration
Expand Down Expand Up @@ -294,20 +291,41 @@ def is_prioritized(self):

@property
def wait_queue_number(self):
"""
Returns the number of people in front of the user in the waiting list.
"""
waiting_list_count = (
self.event.get_waiting_list()
.order_by("-created_at")
.filter(created_at__lte=self.created_at)
.count()
)

if waiting_list_count == 0 or not self.is_on_wait:
# Return None if the user is not on the waitlist to indicate they are not waiting for a spot.
if not self.is_on_wait:
return None

return waiting_list_count
# Retrieve all registrations for the event that are on the waitlist and order them by creation time.
waiting_list_registrations = self.event.registrations.filter(
is_on_wait=True
).order_by("created_at")

# Separate the waiting list registrations into prioritized and non-prioritized groups.
prioritized_registrations = [
reg for reg in waiting_list_registrations if reg.is_prioritized
]
non_prioritized_registrations = [
reg for reg in waiting_list_registrations if not reg.is_prioritized
]

# If the registration is prioritized, calculate its queue position among other prioritized registrations.
if self.is_prioritized:
if self in prioritized_registrations:
queue_position = prioritized_registrations.index(self) + 1
else:
return None
else:
# For non-prioritized registrations, calculate queue position considering all prioritized registrations first.
if self in non_prioritized_registrations:
queue_position = (
len(prioritized_registrations)
+ non_prioritized_registrations.index(self)
+ 1
)
else:
return None

return queue_position

def swap_users(self):
"""Swaps a user with a spot with a prioritized user, if such user exists"""
Expand Down
15 changes: 15 additions & 0 deletions app/content/serializers/content_type.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from django.contrib.contenttypes.models import ContentType
from rest_framework import serializers

from app.common.serializers import BaseModelSerializer


class ContentTypeSerializer(BaseModelSerializer):
app_label_name = serializers.SerializerMethodField()

class Meta:
model = ContentType
fields = ("app_label_name",)

def get_app_label_name(self, obj):
return obj.app_labeled_name
31 changes: 31 additions & 0 deletions app/content/serializers/logentry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django.contrib.admin.models import LogEntry
from rest_framework import serializers

from app.common.serializers import BaseModelSerializer
from app.content.serializers.content_type import ContentTypeSerializer
from app.content.serializers.user import SimpleUserSerializer


class LogEntryListSerializer(BaseModelSerializer):
user = SimpleUserSerializer(many=False)
content_type = ContentTypeSerializer(many=False)
action_flag = serializers.SerializerMethodField()

class Meta:
model = LogEntry
fields = (
"action_time",
"user",
"content_type",
"object_id",
"object_repr",
"action_flag",
)

def get_action_flag(self, obj):
if obj.is_addition():
return "ADDITION"
if obj.is_change():
return "CHANGE"
if obj.is_deletion():
return "DELETION"
2 changes: 2 additions & 0 deletions app/content/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
CategoryViewSet,
CheatsheetViewSet,
EventViewSet,
LogEntryViewSet,
NewsViewSet,
PageViewSet,
QRCodeViewSet,
Expand Down Expand Up @@ -40,6 +41,7 @@
)
router.register("pages", PageViewSet)
router.register("strikes", StrikeViewSet, basename="strikes")
router.register("log-entries", LogEntryViewSet, basename="log-entries")

urlpatterns = [
re_path(r"", include(router.urls)),
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 @@ -12,3 +12,4 @@
from app.content.views.strike import StrikeViewSet
from app.content.views.toddel import ToddelViewSet
from app.content.views.qr_code import QRCodeViewSet
from app.content.views.logentry import LogEntryViewSet
39 changes: 39 additions & 0 deletions app/content/views/logentry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from django.contrib.admin.models import LogEntry
from rest_framework.response import Response

from app.common.mixins import ActionMixin
from app.common.pagination import BasePagination
from app.common.permissions import AdminGroup, check_has_access
from app.common.viewsets import BaseViewSet
from app.content.serializers.logentry import LogEntryListSerializer


class LogEntryViewSet(BaseViewSet, ActionMixin):
serializer_class = LogEntryListSerializer
pagination_class = BasePagination
queryset = LogEntry.objects.all()

def list(self, request, *args, **kwargs):
if check_has_access(AdminGroup.admin(), request):
return super().list(request, *args, **kwargs)

return Response({"detail": "Du har ikke tilgang til å se loggen."}, status=403)

def retrieve(self, request, *args, **kwargs):
if check_has_access(AdminGroup.admin(), request):
return super().retrieve(request, *args, **kwargs)

return Response({"detail": "Du har ikke tilgang til å se loggen."}, status=403)

def create(self, request, *args, **kwargs):
return Response({"detail": "Du har ikke tilgang til å logge."}, status=403)

def update(self, request, *args, **kwargs):
return Response(
{"detail": "Du har ikke tilgang til å oppdatere loggen."}, status=403
)

def destroy(self, request, *args, **kwargs):
return Response(
{"detail": "Du har ikke tilgang til å slette loggen."}, status=403
)
11 changes: 11 additions & 0 deletions app/content/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
MembershipHistorySerializer,
MembershipSerializer,
)
from app.kontres.models.reservation import Reservation
from app.kontres.serializer.reservation_seralizer import ReservationSerializer
from app.util.export_user_data import export_user_data
from app.util.utils import CaseInsensitiveBooleanQueryParam

Expand Down Expand Up @@ -378,3 +380,12 @@ def export_user_data(self, request, *args, **kwargs):
{"detail": "Noe gikk galt, prøv igjen senere eller kontakt Index"},
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
)

@action(detail=False, methods=["get"], url_path="me/reservations")
def get_user_reservations(self, request, *args, **kwargs):
user = request.user
reservations = Reservation.objects.filter(author=user).order_by("start_time")
serializer = ReservationSerializer(
reservations, many=True, context={"request": request}
)
return Response(serializer.data, status=status.HTTP_200_OK)
Empty file added app/kontres/__init__.py
Empty file.
16 changes: 16 additions & 0 deletions app/kontres/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from django.contrib import admin

from app.kontres.models.bookable_item import BookableItem
from app.kontres.models.reservation import Reservation


class ReservationAdmin(admin.ModelAdmin):
readonly_fields = ("id",)


class BookableItemAdmin(admin.ModelAdmin):
readonly_fields = ("id",)


admin.site.register(Reservation, ReservationAdmin)
admin.site.register(BookableItem, BookableItemAdmin)
5 changes: 5 additions & 0 deletions app/kontres/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class KontResConfig(AppConfig):
name = "app.kontres"
7 changes: 7 additions & 0 deletions app/kontres/enums.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.db import models


class ReservationStateEnum(models.TextChoices):
PENDING = "PENDING"
CONFIRMED = "CONFIRMED"
CANCELLED = "CANCELLED"
2 changes: 2 additions & 0 deletions app/kontres/factories/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from app.kontres.factories.bookable_item_factory import BookableItemFactory
from app.kontres.factories.reservation_factory import ReservationFactory
12 changes: 12 additions & 0 deletions app/kontres/factories/bookable_item_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from factory import Faker, Sequence
from factory.django import DjangoModelFactory

from app.kontres.models.bookable_item import BookableItem


class BookableItemFactory(DjangoModelFactory):
class Meta:
model = BookableItem

name = Sequence(lambda n: f"Item_{n}")
description = Faker("text")
21 changes: 21 additions & 0 deletions app/kontres/factories/reservation_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django.utils import timezone

from factory import Faker, SubFactory
from factory.django import DjangoModelFactory

from app.content.factories import UserFactory
from app.kontres.enums import ReservationStateEnum
from app.kontres.factories.bookable_item_factory import BookableItemFactory
from app.kontres.models.reservation import Reservation


class ReservationFactory(DjangoModelFactory):
class Meta:
model = Reservation

author = SubFactory(UserFactory)
bookable_item = SubFactory(BookableItemFactory)
start_time = timezone.now() + timezone.timedelta(hours=1)
end_time = timezone.now() + timezone.timedelta(hours=2)
state = ReservationStateEnum.PENDING
description = Faker("text")
48 changes: 48 additions & 0 deletions app/kontres/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Generated by Django 4.0.8 on 2023-10-25 13:10

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

initial = True

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]

operations = [
migrations.CreateModel(
name='BookableItem',
fields=[
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=20)),
('description', models.TextField(blank=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Reservation',
fields=[
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('start_time', models.DateTimeField()),
('end_time', models.DateTimeField()),
('state', models.CharField(choices=[('PENDING', 'Pending'), ('CONFIRMED', 'Confirmed'), ('CANCELLED', 'Cancelled')], default='PENDING', max_length=15)),
('description', models.TextField(blank=True)),
('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reservations', to=settings.AUTH_USER_MODEL)),
('bookable_item', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='reservations', to='kontres.bookableitem')),
],
options={
'abstract': False,
},
),
]
Loading

0 comments on commit 8347ca8

Please sign in to comment.