Skip to content

Commit

Permalink
Merge branch 'master' into 1480-mandatory-occupied-time-field
Browse files Browse the repository at this point in the history
  • Loading branch information
amaliejvik authored Nov 7, 2024
2 parents 4344fbf + 709f6d4 commit d11f95e
Show file tree
Hide file tree
Showing 73 changed files with 1,252 additions and 186 deletions.
53 changes: 4 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,10 @@

<img src="./docs/splash.png"/>

## Documentation

- **[Technical Documentation](./docs/technical/README.md)**
- [Work Methodology](./docs/work-methodology.md)
- [Useful Commands](./docs/useful-commands.md)
- [Technologies used on Samf4 🤖](./docs/technical/Samf4Tech.md)
- [Project Specific Commands](./docs/docker-project-specific-commands.md)
- [Useful Docker aliases](./docs/docker-project-specific-commands.md)
- [🌐 API documentation](./docs/api-docs.md)

## Installation

We have a script that handles all installation for you. To run the script, a Github Personal Access Token (PAT) is required.
You can make one here https://github.com/settings/tokens/new. Tick scopes `repo`, `read:org` and `admin:public_key`),
then store the token somewhere safe (Github will never show it again).

Copy these commands (press button on the right-hand side of the block)
and run from the directory you would clone the project.

```sh
# Interactive
read -s -p "Github PAT token: " TOKEN ; X_INTERACTIVE=y /bin/bash -c "$(curl -fsSL https://$TOKEN@raw.githubusercontent.com/Samfundet/Samfundet4/master/{bash_utils.sh,install.sh})" && . ~/.bash_profile && cd Samfundet4; unset TOKEN; unset X_INTERACTIVE;
```
## Introduction

<details>
<summary>Non-interactive (show/hide)</summary>
Samfundet4 is the latest and greatest iteration of samfundet.no. It's built using Django and React.

```sh
# Non-interactive
read -s -p "Github PAT token: " TOKEN ; X_INTERACTIVE=n /bin/bash -c "$(curl -fsSL https://$TOKEN@raw.githubusercontent.com/Samfundet/Samfundet4/master/{bash_utils.sh,install.sh})" && . ~/.bash_profile && cd Samfundet4; unset TOKEN; unset X_INTERACTIVE;
```

<!--
cd ~/my-projects/test; rm -rf Samfundet4; read -s -p "Github PAT token: " TOKEN ; X_INTERACTIVE=y /bin/bash -c "$(curl -fsSL https://[email protected]/Samfundet/Samfundet4/master/{bash_utils.sh,install.sh})" && . ~/.bash_profile && cd Samfundet4; unset TOKEN; unset X_INTERACTIVE;
-->
</details>

<details>
<summary>Flags explained (show/hide)</summary>

> - X_INTERACTIVE (y/n): determines how many prompts you receive before performing an action.
> curl:
> - -f: fail fast
> - -s: silent, no progress-meter
> - -S: show error on fail
> - -L: follow redirect
</details>
## Documentation

<br>
<br>
<br>
Looking for install guides and technical documentation? Go to the [Documentation Overview](./docs/README.md)!
1 change: 1 addition & 0 deletions backend/root/utils/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@
samfundet__merch_detail = 'samfundet:merch-detail'
samfundet__role_list = 'samfundet:role-list'
samfundet__role_detail = 'samfundet:role-detail'
samfundet__role_users = 'samfundet:role-users'
samfundet__recruitment_list = 'samfundet:recruitment-list'
samfundet__recruitment_detail = 'samfundet:recruitment-detail'
samfundet__recruitment_gangs = 'samfundet:recruitment-gangs'
Expand Down
18 changes: 18 additions & 0 deletions backend/samfundet/migrations/0010_recruitment_promo_media.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 5.1.1 on 2024-10-31 19:56

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('samfundet', '0009_recruitmentpositionsharedinterviewgroup_name_en_and_more'),
]

operations = [
migrations.AddField(
model_name='recruitment',
name='promo_media',
field=models.CharField(blank=True, default=None, help_text='Youtube video id', max_length=11, null=True),
),
]
1 change: 1 addition & 0 deletions backend/samfundet/models/recruitment.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Recruitment(CustomBaseModel):
organization = models.ForeignKey(null=False, blank=False, to=Organization, on_delete=models.CASCADE, help_text='The organization that is recruiting')

max_applications = models.PositiveIntegerField(null=True, blank=True, verbose_name='Max applications per applicant')
promo_media = models.CharField(max_length=11, help_text='Youtube video id', null=True, default=None, blank=True)

def resolve_org(self, *, return_id: bool = False) -> Organization | int:
if return_id:
Expand Down
69 changes: 68 additions & 1 deletion backend/samfundet/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import re
import itertools
from typing import TYPE_CHECKING
from collections import defaultdict
Expand All @@ -18,7 +19,7 @@
from root.constants import PHONE_NUMBER_REGEX
from root.utils.mixins import CustomBaseSerializer

from .models.role import Role
from .models.role import Role, UserOrgRole, UserGangRole, UserGangSectionRole
from .models.event import Event, EventGroup, EventCustomTicket, PurchaseFeedbackModel, PurchaseFeedbackQuestion, PurchaseFeedbackAlternative
from .models.billig import BilligEvent, BilligPriceGroup, BilligTicketGroup
from .models.general import (
Expand All @@ -39,6 +40,7 @@
KeyValue,
MenuItem,
TextItem,
GangSection,
Reservation,
ClosedPeriod,
FoodCategory,
Expand Down Expand Up @@ -425,6 +427,12 @@ class Meta:
fields = '__all__'


class GangSectionSerializer(CustomBaseSerializer):
class Meta:
model = GangSection
fields = '__all__'


class RecruitmentGangSerializer(CustomBaseSerializer):
recruitment_positions = serializers.SerializerMethodField(method_name='get_positions_count', read_only=True)

Expand Down Expand Up @@ -498,6 +506,54 @@ class Meta:
fields = '__all__'


class UserOrgRoleSerializer(CustomBaseSerializer):
user = UserSerializer()
org_role = serializers.SerializerMethodField()

class Meta:
model = UserOrgRole
fields = ('user', 'org_role')

def get_org_role(self, obj: UserOrgRole) -> dict:
return {
'created_at': obj.created_at,
'created_by': UserSerializer(obj.created_by).data,
'organization': OrganizationSerializer(obj.obj).data,
}


class UserGangRoleSerializer(CustomBaseSerializer):
user = UserSerializer()
gang_role = serializers.SerializerMethodField()

class Meta:
model = UserGangRole
fields = ('user', 'gang_role')

def get_gang_role(self, obj: UserGangRole) -> dict:
return {
'created_at': obj.created_at,
'created_by': UserSerializer(obj.created_by).data,
'gang': GangSerializer(obj.obj).data,
}


class UserGangSectionRoleSerializer(CustomBaseSerializer):
user = UserSerializer()
section_role = serializers.SerializerMethodField()

class Meta:
model = UserGangSectionRole
fields = ('user', 'section_role')

def get_section_role(self, obj: UserGangSectionRole) -> dict:
return {
'created_at': obj.created_at,
'created_by': UserSerializer(obj.created_by).data,
'section': GangSectionSerializer(obj.obj).data,
}


class SaksdokumentSerializer(CustomBaseSerializer):
# Read only url file path used in frontend
url = serializers.SerializerMethodField(method_name='get_url', read_only=True)
Expand Down Expand Up @@ -739,11 +795,22 @@ class Meta:

class RecruitmentSerializer(CustomBaseSerializer):
separate_positions = RecruitmentSeparatePositionSerializer(many=True, read_only=True)
promo_media = serializers.CharField(max_length=100, allow_blank=True, allow_null=True)

class Meta:
model = Recruitment
fields = '__all__'

def validate_promo_media(self, value: str | None) -> str | None:
if value is None or value == '':
return None
match = re.search(r'(youtu.*be.*)\/(watch\?v=|embed\/|v|shorts|)(.*?((?=[&#?])|$))', value)
if match:
return match.group(3)
if len(value) == 11:
return value
raise ValidationError('Invalid youtube url')

def to_representation(self, instance: Recruitment) -> dict:
data = super().to_representation(instance)
data['organization'] = OrganizationSerializer(instance.organization).data
Expand Down
22 changes: 21 additions & 1 deletion backend/samfundet/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import hmac
import hashlib
from typing import Any
from itertools import chain

from guardian.shortcuts import get_objects_for_user

Expand Down Expand Up @@ -40,7 +41,7 @@

from .utils import event_query, generate_timeslots, get_occupied_timeslots_from_request
from .homepage import homepage
from .models.role import Role
from .models.role import Role, UserOrgRole, UserGangRole, UserGangSectionRole
from .serializers import (
TagSerializer,
GangSerializer,
Expand All @@ -67,11 +68,13 @@
EventGroupSerializer,
PermissionSerializer,
RecruitmentSerializer,
UserOrgRoleSerializer,
ClosedPeriodSerializer,
FoodCategorySerializer,
OrganizationSerializer,
SaksdokumentSerializer,
UserFeedbackSerializer,
UserGangRoleSerializer,
InterviewRoomSerializer,
FoodPreferenceSerializer,
UserPreferenceSerializer,
Expand All @@ -82,6 +85,7 @@
ReservationCheckSerializer,
UserForRecruitmentSerializer,
RecruitmentPositionSerializer,
UserGangSectionRoleSerializer,
RecruitmentStatisticsSerializer,
RecruitmentForRecruiterSerializer,
RecruitmentSeparatePositionSerializer,
Expand Down Expand Up @@ -324,6 +328,22 @@ class RoleView(ModelViewSet):
serializer_class = RoleSerializer
queryset = Role.objects.all()

@action(detail=True, methods=['get'])
def users(self, request: Request, pk: int) -> Response:
role = get_object_or_404(Role, id=pk)

org_roles = UserOrgRole.objects.filter(role=role).select_related('user', 'obj')
gang_roles = UserGangRole.objects.filter(role=role).select_related('user', 'obj')
section_roles = UserGangSectionRole.objects.filter(role=role).select_related('user', 'obj')

org_data = UserOrgRoleSerializer(org_roles, many=True).data
gang_data = UserGangRoleSerializer(gang_roles, many=True).data
section_data = UserGangSectionRoleSerializer(section_roles, many=True).data

combined = list(chain(org_data, gang_data, section_data))

return Response(combined)


# =============================== #
# Sulten #
Expand Down
52 changes: 52 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[**&larr; Back: Samfundet4**](../)

# Documentation Overview

> [!TIP]
> If you're new, start by going through the [Introduction to Samfundet4](./introduction.md) guide.
## Frontend

- [Creating react components (conventions)](./technical/frontend/components.md)
- [Forms and schemas](./technical/frontend/forms.md)
- [*Deprecated: SamfForm*](./technical/frontend/samfform.md)
- [Cypress Setup Documentation](./technical/frontend/cypress.md)
- [Data fetching and State management](./technical/frontend/data-fetching.md)

## Backend

- [🌐 API documentation](./api-docs.md)
- [Billig (payment system)](./technical/backend/billig.md)
- [Seed scripts](./technical/backend/seed.md)
- [Role system](./technical/backend/rolesystem.md)

## Other

- [Automatic Interview Scheduling](./intervew-scheduling.md)

## Workflow

- [Work Methodology](./work-methodology.md)
- How to contribute to the project
- [Useful Commands](./useful-commands.md)
- [Useful Docker aliases](./docker-project-specific-commands.md)
- [Common error messages](./common-errors.md)

## Pipelines & Deployment

- [Pipeline (mypy, Biome, tsc, ...)](./technical/pipeline.md)

## Install

- Linux: [Docker](./install/linux-docker.md)[Native](./install/linux-native.md)
- MacOS: [Docker](./install/mac-docker.md)[Native](./install/mac-native.md)
- Windows: [Docker](./install/windows-docker.md)[WSL](./install/windows-wsl.md)
- [Install script](./install/install-script.md)
- [Post-install instructions](./install/post-install.md)

## Editor configuration

* [JetBrains (WebStorm, PyCharm, etc...)](./editors/jetbrains.md)
* [VS Code](./editors/vscode.md)
* [Vim/Neovim](./editors/vim.md)
* [Emacs](./editors/emacs.md)
12 changes: 8 additions & 4 deletions docs/api-docs.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[**&larr; Back: Documentation Overview**](./README.md)

# API docs

API docs are generated by [drf-spectacular](https://drf-spectacular.readthedocs.io/en/latest/readme.html).
Expand All @@ -6,14 +8,16 @@ API documentation is available as two different interfaces:

[Swagger-UI](http://localhost:8000/schema/swagger-ui/#/) or [Redoc](http://localhost:8000/schema/redoc/)



🐋 _When backend server is running_

## API schema file

If you want a schema file for the API you can go to [http://localhost:8000/schema/](http://localhost:8000/schema/).

A schema file will be downloaded which can be used for multiple purposes, like sharing API documentation, or to generate code for recreating or testing the API.
A schema file will be downloaded which can be used for multiple purposes, like sharing API documentation, or to generate
code for recreating or testing the API.

> 💡 Note: You might encounter some error messages during this process. These errors are typically related to drf-spectacular not being able to parse certain views in views.py. However, the tool will still attempt to generate the documentation, though the results might not be fully comprehensive.
> [!NOTE]
> You might encounter some error messages during this process. These errors are typically related to drf-spectacular not
> being able to parse certain views in views.py. However, the tool will still attempt to generate the documentation,
> though the results might not be fully comprehensive.
4 changes: 3 additions & 1 deletion docs/common-errors.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[**&larr; Back: Documentation Overview**](./README.md)

# Common error messages

## Rule of thumb
Expand All @@ -20,4 +22,4 @@ exec /app/entrypoint.sh: no such file or directory
Cannot connect to the Docker daemon at ../../.../default/docker.sock. Is the docker daemon running?
```
### Fix:
Make sure docker desktop is running (Windows) or run `colima start`on Mac.
Make sure docker desktop is running (Windows) or run `colima start` (or start Docker Desktop) on Mac.
3 changes: 0 additions & 3 deletions docs/dependencies.md

This file was deleted.

15 changes: 10 additions & 5 deletions docs/docker-project-specific-commands.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Useful project spesific Docker actions
### For frontend actions
All commands has to be run inside a shell in a container.
[**&larr; Back: Documentation Overview**](./README.md)

# Useful project specific Docker actions

## Frontend
All commands have to be run inside a shell in a container.
```bash
docker compose exec frontend bash
#Command to open the frontend container in a shell
Expand All @@ -25,9 +28,11 @@ yarn run tsc:check
#runs TypeScript Compiler check, like in GitHub Actions pipeline, but in Docker
```

## For backend actions:
---

## Backend

All commands has to be run inside a shell in a container.
All commands have to be run inside a shell in a container.
```bash
docker compose exec backend bash
#Command to open container in a shell
Expand Down
Binary file added docs/editors/assets/biome_config.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/editors/assets/pycharm_add_interpreter.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/editors/assets/pycharm_interpreter_bar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit d11f95e

Please sign in to comment.