Skip to content

Commit

Permalink
Merge branch 'master' into feature/registration
Browse files Browse the repository at this point in the history
  • Loading branch information
magsyg authored Dec 19, 2023
2 parents 83b0355 + b8803f0 commit 1484dc3
Show file tree
Hide file tree
Showing 32 changed files with 680 additions and 39 deletions.
16 changes: 15 additions & 1 deletion backend/root/utils/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
DO NOT WRITE IN THIS FILE, AS IT WILL BE OVERWRITTEN ON NEXT UPDATE.
THIS FILE WAS GENERATED BY: root.management.commands.generate_routes
LAST UPDATE: 2023-09-24 15:12:38.294245+00:00
LAST UPDATE: 2023-11-02 18:37:45.368800+00:00
"""

############################################################
Expand Down Expand Up @@ -353,6 +353,15 @@
admin__samfundet_interviewroom_delete = 'admin:samfundet_interviewroom_delete'
admin__samfundet_interviewroom_change = 'admin:samfundet_interviewroom_change'
adminsamfundetinterviewroom__objectId = ''
admin__samfundet_interview_permissions = 'admin:samfundet_interview_permissions'
admin__samfundet_interview_permissions_manage_user = 'admin:samfundet_interview_permissions_manage_user'
admin__samfundet_interview_permissions_manage_group = 'admin:samfundet_interview_permissions_manage_group'
admin__samfundet_interview_changelist = 'admin:samfundet_interview_changelist'
admin__samfundet_interview_add = 'admin:samfundet_interview_add'
admin__samfundet_interview_history = 'admin:samfundet_interview_history'
admin__samfundet_interview_delete = 'admin:samfundet_interview_delete'
admin__samfundet_interview_change = 'admin:samfundet_interview_change'
adminsamfundetinterview__objectId = ''
admin__samfundet_notification_changelist = 'admin:samfundet_notification_changelist'
admin__samfundet_notification_add = 'admin:samfundet_notification_add'
admin__samfundet_notification_history = 'admin:samfundet_notification_history'
Expand Down Expand Up @@ -427,8 +436,12 @@
samfundet__recruitment_position_detail = 'samfundet:recruitment_position-detail'
samfundet__recruitment_admissions_for_applicant_list = 'samfundet:recruitment_admissions_for_applicant-list'
samfundet__recruitment_admissions_for_applicant_detail = 'samfundet:recruitment_admissions_for_applicant-detail'
samfundet__recruitment_admissions_for_group_list = 'samfundet:recruitment_admissions_for_group-list'
samfundet__recruitment_admissions_for_group_detail = 'samfundet:recruitment_admissions_for_group-detail'
samfundet__recruitment_admissions_for_gang_list = 'samfundet:recruitment_admissions_for_gang-list'
samfundet__recruitment_admissions_for_gang_detail = 'samfundet:recruitment_admissions_for_gang-detail'
samfundet__interview_list = 'samfundet:interview-list'
samfundet__interview_detail = 'samfundet:interview-detail'
samfundet__api_root = 'samfundet:api-root'
samfundet__csrf = 'samfundet:csrf'
samfundet__login = 'samfundet:login'
Expand All @@ -444,6 +457,7 @@
samfundet__home = 'samfundet:home'
samfundet__assign_group = 'samfundet:assign_group'
samfundet__recruitment_positions = 'samfundet:recruitment_positions'
samfundet__recruitment_positions_gang = 'samfundet:recruitment_positions_gang'
samfundet__active_recruitment_positions = 'samfundet:active_recruitment_positions'
samfundet__applicants_without_interviews = 'samfundet:applicants_without_interviews/'
static__path = ''
Expand Down
3 changes: 3 additions & 0 deletions backend/samfundet/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
RecruitmentAdmission,
InterviewRoom,
Interview,
Occupiedtimeslot,
)
from .models.general import (
Tag,
Expand Down Expand Up @@ -62,6 +63,8 @@

# Unregister User and Group to set new Admins.
admin.site.unregister(Group)
# Just for testing TODO remove when done
admin.site.register(Occupiedtimeslot)


@admin.register(User)
Expand Down
53 changes: 53 additions & 0 deletions backend/samfundet/migrations/0042_occupiedtimeslot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generated by Django 5.0 on 2023-12-17 21:25

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


class Migration(migrations.Migration):
dependencies = [
("samfundet", "0041_recruitmentposition_norwegian_applicants_only_and_more"),
]

operations = [
migrations.CreateModel(
name="Occupiedtimeslot",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"start_dt",
models.DateTimeField(help_text="The time of the interview"),
),
("end_dt", models.DateTimeField(help_text="The time of the interview")),
(
"recruitment",
models.ForeignKey(
help_text="Occupied timeslots for the users for this recruitment",
on_delete=django.db.models.deletion.CASCADE,
to="samfundet.recruitment",
),
),
(
"user",
models.ForeignKey(
help_text="Occupied timeslots for user",
on_delete=django.db.models.deletion.CASCADE,
related_name="occupied_timeslots",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"abstract": False,
},
),
]
18 changes: 18 additions & 0 deletions backend/samfundet/models/recruitment.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,3 +201,21 @@ def save(self, *args: tuple, **kwargs: dict) -> None:
self.interview = Interview.objects.create()

super().save(*args, **kwargs)


class Occupiedtimeslot(FullCleanSaveMixin):

user = models.ForeignKey(
User,
on_delete=models.CASCADE,
null=False,
blank=False,
help_text='Occupied timeslots for user',
related_name='occupied_timeslots',
)
# Mostly only used for deletion, and anonymization.
recruitment = models.ForeignKey(Recruitment, on_delete=models.CASCADE, help_text='Occupied timeslots for the users for this recruitment')

# Start and end time of availability
start_dt = models.DateTimeField(help_text='The time of the interview', null=False, blank=False)
end_dt = models.DateTimeField(help_text='The time of the interview', null=False, blank=False)
12 changes: 11 additions & 1 deletion backend/samfundet/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
RecruitmentAdmission,
InterviewRoom,
Interview,
Occupiedtimeslot,
)
from .models.event import (Event, EventGroup, EventCustomTicket)
from .models.general import (
Expand Down Expand Up @@ -530,6 +531,7 @@ def get_recruitment_admission_ids(self, obj: User) -> list[int]:


class RecruitmentPositionSerializer(serializers.ModelSerializer):
gang = GangSerializer(read_only=True)

class Meta:
model = RecruitmentPosition
Expand Down Expand Up @@ -562,11 +564,19 @@ def create(self, validated_data: dict) -> RecruitmentAdmission:
return recruitment_admission


class OccupiedtimeslotSerializer(serializers.ModelSerializer):

class Meta:
model = Occupiedtimeslot
fields = '__all__'


class ApplicantInfoSerializer(serializers.ModelSerializer):
occupied_timeslots = OccupiedtimeslotSerializer(many=True)

class Meta:
model = User
fields = ['id', 'first_name', 'last_name', 'email']
fields = ['id', 'first_name', 'last_name', 'email', 'occupied_timeslots']


class InterviewRoomSerializer(serializers.ModelSerializer):
Expand Down
3 changes: 3 additions & 0 deletions backend/samfundet/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
router.register('recruitment', views.RecruitmentView, 'recruitment')
router.register('recruitment-position', views.RecruitmentPositionView, 'recruitment_position')
router.register('recruitment-admisisons-for-applicant', views.RecruitmentAdmissionForApplicantView, 'recruitment_admissions_for_applicant')
router.register('recruitment-admisisons-for-group', views.RecruitmentAdmissionForGangView, 'recruitment_admissions_for_group')
router.register('recruitment-admisisons-for-gang', views.RecruitmentAdmissionForGangView, 'recruitment_admissions_for_gang')
router.register('interview', views.InterviewView, 'interview')

Expand All @@ -60,6 +61,8 @@

########## Recruitment ##########
path('recruitment-positions/', views.RecruitmentPositionsPerRecruitmentView.as_view(), name='recruitment_positions'),
path('recruitment-positions-gang/', views.RecruitmentPositionsPerGangView.as_view(), name='recruitment_positions_gang'),
path('active-recruitment-positions/', views.ActiveRecruitmentPositionsView.as_view(), name='active_recruitment_positions'),
path('applicants-without-interviews/', views.ApplicantsWithoutInterviewsView.as_view(), name='applicants_without_interviews/'),
path('occupiedtimeslot/', views.OccupiedtimeslotView.as_view(), name='occupied_timeslots')
]
43 changes: 42 additions & 1 deletion backend/samfundet/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.generics import ListAPIView
from rest_framework.generics import ListAPIView, ListCreateAPIView
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from rest_framework.exceptions import PermissionDenied
from rest_framework.permissions import AllowAny, IsAuthenticated, BasePermission, DjangoModelPermissionsOrAnonReadOnly
Expand All @@ -37,6 +37,7 @@
Interview,
Recruitment,
InterviewRoom,
Occupiedtimeslot,
RecruitmentPosition,
RecruitmentAdmission,
)
Expand Down Expand Up @@ -95,6 +96,7 @@
FoodPreferenceSerializer,
UserPreferenceSerializer,
InformationPageSerializer,
OccupiedtimeslotSerializer,
UserForRecruitmentSerializer,
RecruitmentPositionSerializer,
RecruitmentAdmissionForGangSerializer,
Expand Down Expand Up @@ -559,6 +561,24 @@ def get_queryset(self) -> Response:
return None


@method_decorator(ensure_csrf_cookie, 'dispatch')
class RecruitmentPositionsPerGangView(ListAPIView):
permission_classes = [AllowAny]
serializer_class = RecruitmentPositionSerializer

def get_queryset(self) -> Response:
"""
Optionally restricts the returned positions to a given recruitment,
by filtering against a `recruitment` query parameter in the URL.
"""
recruitment = self.request.query_params.get('recruitment', None)
gang = self.request.query_params.get('gang', None)
if recruitment is not None and gang is not None:
return RecruitmentPosition.objects.filter(gang=gang, recruitment=recruitment)
else:
return None


class ApplicantsWithoutInterviewsView(ListAPIView):
permission_classes = [AllowAny]
serializer_class = UserForRecruitmentSerializer
Expand Down Expand Up @@ -681,3 +701,24 @@ class InterviewView(ModelViewSet):
permission_classes = [AllowAny]
serializer_class = InterviewSerializer
queryset = Interview.objects.all()


class OccupiedtimeslotView(ListCreateAPIView):
model = Occupiedtimeslot
serializer_class = OccupiedtimeslotSerializer

def get_queryset(self) -> QuerySet[Occupiedtimeslot]:
recruitment = self.request.query_params.get('recruitment', Recruitment.objects.order_by('-actual_application_deadline').first())
return Occupiedtimeslot.objects.filter(recruitment=recruitment, user=self.request.user.id)

def create(self, request: Request) -> Response:
for p in request.data:
p['user'] = request.user.id
# TODO Could maybe need a check for saving own, not allowing to save others to themselves
serializer = self.get_serializer(data=request.data, many=True)
if serializer.is_valid():
serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
2 changes: 2 additions & 0 deletions frontend/src/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
LycheAboutPage,
LycheContactPage,
LycheHomePage,
LycheMenuPage,
NotFoundPage,
RecruitmentAdmissionFormPage,
RecruitmentPage,
Expand Down Expand Up @@ -232,6 +233,7 @@ export function AppRoutes() {
<Route element={<SultenOutlet />}>
<Route path={ROUTES.frontend.sulten} element={<LycheHomePage />} />
<Route path={ROUTES.frontend.sulten_about} element={<LycheAboutPage />} />
<Route path={ROUTES.frontend.sulten_menu} element={<LycheMenuPage />} />
<Route path={ROUTES.frontend.sulten_contact} element={<LycheContactPage />} />
</Route>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,87 @@
@import 'src/constants';

@import 'src/mixins';


.container {
width: 100%;
border: 1px solid $grey-3;
border: 2px solid $grey-3;
overflow: hidden;

@include theme-dark{
border: 1px solid $grey-0;
}
}

.parent {
border-radius: 0.7em;
border-radius: 10px;
border-color: $grey-4;
background-color: $grey_4;

@include theme-dark{
background-color: #1d0809;
border-color: transparent;
}
}

.child {
border-radius: 0;
border: 0;
border: 1px solid transparent;
border-radius: 10px;
margin: 10px;
width: auto;

@include theme-dark{
border-color: transparent;
}
}

.extendable_header_wrapper {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
font-size: 1rem;
background: $red-samf;
padding: 0.5em 1.5em 0.5em 0.5em;
color: $white;
font-size: 20px;
font-weight: 400;
background: $grey_4;
padding: 0.5em 1.5em 0.5em 1em;
color: $black;
cursor: pointer;
border: 0;
outline: none;
text-align: left;

@include theme-dark{
background-color: #1d0809;
color: white;
}
}

.extendable_header_wrapper:hover{
background-color: #dbdbdb;

@include theme-dark{
background-color: #1d0809;
}
}

.extendable_header_title {
display: block;
flex-basis: 75%;
flex-basis: 95%;
padding: 0;
margin: 0;
}

.expandable_header_arrow {
flex-basis: 25%;
flex-basis: 5%;
height: 35px;
width: 35px;
pointer-events: none;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
color: $white;
font-size: medium;
font-weight: 400;
}

.expandable_header_arrow.open {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ export function ExpandableHeader({
return (
<div className={containerClassNames}>
<div className={classNames} onClick={() => setShowChildren(!showChildren)}>
<p className={styles.extendable_header_title}>{label}</p>
<div className={classnames(styles.expandable_header_arrow, showChildren ? styles.open : styles.closed)}>
&#9660;
</div>
<p className={styles.extendable_header_title}>{label}</p>
</div>
{showChildren && children}
</div>
Expand Down
Loading

0 comments on commit 1484dc3

Please sign in to comment.