1 Commits

Author SHA1 Message Date
Simeon Pursiainen a27d06b0d0 Revert "testing"
This reverts commit 7554c1e7e8
2025-03-03 09:50:06 +00:00
20 changed files with 1999 additions and 310 deletions
-1
View File
@@ -12,4 +12,3 @@ node_modules/
*.code-workspace *.code-workspace
venv/ venv/
.venv/ .venv/
poetry.lock
+6 -8
View File
@@ -27,8 +27,7 @@ audit:
- pushes - pushes
needs: [] needs: []
before_script: before_script:
- pip install pip==25.3 - pip install poetry==2.0.1
- pip install poetry==2.1.1
- poetry config virtualenvs.create false - poetry config virtualenvs.create false
- poetry install --no-interaction --no-ansi - poetry install --no-interaction --no-ansi
script: script:
@@ -49,8 +48,7 @@ test:
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB" DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB"
DB_HOST: postgres DB_HOST: postgres
before_script: before_script:
- pip install pip==25.3 - pip install poetry==2.0.1
- pip install poetry==2.1.1
- poetry config virtualenvs.create false - poetry config virtualenvs.create false
- poetry install --no-interaction --no-ansi - poetry install --no-interaction --no-ansi
script: script:
@@ -93,8 +91,8 @@ publish:
services: services:
- docker:25-dind - docker:25-dind
only: only:
- main - develop
- production - master
script: script:
- docker info - docker info
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
@@ -105,7 +103,7 @@ deploy:dev:
image: docker:25-cli image: docker:25-cli
stage: deploy stage: deploy
only: only:
- main - develop
environment: environment:
name: dev name: dev
url: http://api.dev.sahkoinsinoorikilta.fi url: http://api.dev.sahkoinsinoorikilta.fi
@@ -127,7 +125,7 @@ deploy:production:
stage: deploy stage: deploy
image: docker:25-cli image: docker:25-cli
only: only:
- production - master
environment: environment:
name: production name: production
url: https://api.sahkoinsinoorikilta.fi url: https://api.sahkoinsinoorikilta.fi
-2
View File
@@ -1,2 +0,0 @@
python 3.12.9
poetry 2.1.1
+4 -4
View File
@@ -1,11 +1,12 @@
FROM python:3.12.9-slim-bullseye AS builder FROM python:3.12.9-slim-bullseye AS builder
ENV PYTHONUNBUFFERED 1 ENV PYTHONUNBUFFERED 1
COPY . ./ COPY . ./
ENV POETRY_VERSION=2.1.1
RUN pip install pip==25.3 ENV POETRY_VERSION=2.0.1
RUN pip install "poetry==$POETRY_VERSION" RUN pip install "poetry==$POETRY_VERSION"
RUN poetry self add poetry-plugin-export RUN poetry self add poetry-plugin-export
RUN poetry export --without-hashes --format=requirements.txt --output requirements.txt RUN poetry export --without-hashes > requirements.txt
FROM python:3.12.9-slim-bullseye AS server FROM python:3.12.9-slim-bullseye AS server
@@ -22,7 +23,6 @@ ENV PYTHONUNBUFFERED=1 \
PIP_DEFAULT_TIMEOUT=100 PIP_DEFAULT_TIMEOUT=100
RUN apt-get update && apt-get install --no-install-recommends -y build-essential RUN apt-get update && apt-get install --no-install-recommends -y build-essential
RUN pip install pip==25.3
RUN pip install --no-deps -r requirements.txt RUN pip install --no-deps -r requirements.txt
RUN python manage.py collectstatic --noinput RUN python manage.py collectstatic --noinput
+17 -36
View File
@@ -15,6 +15,7 @@ Set up your SSH key authentication in GitLab Profile Settings. Then clone the re
```bash ```bash
git clone git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git git clone git@gitlab.com:sahkoinsinoorikilta/vtmk/web2.0-backend.git
cd web2.0-backend cd web2.0-backend
git checkout develop
``` ```
Copy env file for local use: Copy env file for local use:
@@ -30,34 +31,24 @@ For depedencies and virtual environment, we use [poetry](https://python-poetry.o
First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry: First install [python](https://wiki.python.org/moin/BeginnersGuide/Download). Then install poetry:
```bash ```bash
python -m pip install poetry==2.1.1 python -m pip install poetry==2.0.1
``` ```
Install dependencies with The easiest integration with VSCode is to have poetry install virtual environment in project folder, configured with CMD
```bash ```bash
poetry install python -m poetry config virtualenvs.in-project true
```
Poetry is configured to install dependencies in a virtual environment, so you should see `.venv` folder in repo root.
Activate virtual environment in shell
```bash
eval $(poetry env activate)
``` ```
### Node ### Node
We use Node.js for few development tasks, like linting. We use Node.js for few development tasks, like linting. Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm). After installing install dependencies:
Easiest way to install Node is [nvm](https://github.com/nvm-sh/nvm).
After installing install dependencies:
```bash ```bash
npm install npm install
``` ```
See [Linting](#linting) for more info TODO: List scripts
### Database ### Database
@@ -71,28 +62,18 @@ docker run --name sik.web.db -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postg
## Development ## Development
Install dependencies with Activate virtual environment in shell
```bash
eval $(python -m poetry env activate)
```
Install dependencies
```bash ```bash
poetry install poetry install
``` ```
and make sure you are using Python from your virutal environment.
Virtual environment can be activated with
```bash
eval $(poetry env activate)
```
and you verify correct Python executable with
```bash
which python
# should return path similar to {your-system path}/web2.0-backend/.venv/bin/python
```
### Initializing data ### Initializing data
Run the following `manage.py` commands to initialize a new database. Do not run these in production without thinking! Run the following `manage.py` commands to initialize a new database. Do not run these in production without thinking!
@@ -128,11 +109,11 @@ Example of creating a feature branch:
git checkout -b feature-branch-name git checkout -b feature-branch-name
``` ```
When your changes are ready and the code works without errors, submit a merge request to `main` in GitLab. Another developer reviews your changes and runs the merge. Feature branches should be closed on merge. When your changes are ready and the code works without errors, submit a merge request to `develop` in GitLab. Another developer reviews your changes and runs the merge. Feature branches should be closed on merge.
Bugfixes do not need their own feature branches and can be pushed straight to `main`, but if the fix needs a notable amount of work, it should be done in a `bugfix` branch instead. Bugfixes do not need their own feature branches and can be pushed straight to `develop`, but if the fix needs a notable amount of work, it should be done in a `bugfix` branch instead.
Merge requests to `main` should be reviewed by multiple developers. Only a moderator can accept merge requests to `production`. Merge requests to `master` should be reviewed by multiple developers. Only a moderator can accept merge requests to `master`.
### Linting ### Linting
@@ -172,4 +153,4 @@ For more information about deployment check **[infra](https://gitlab.com/sahkoin
## GitLab CI ## GitLab CI
All pushed changes go through the GitLab Continuous Integration, which consists of automated unit testing and linting. Make sure your changes pass both before merging to `main` or `production`. All pushed changes go through the GitLab Continuous Integration, which consists of automated unit testing and linting. Make sure your changes pass both before merging to `develop` or `master`.
+5 -15
View File
@@ -4,27 +4,17 @@ services:
volumes: volumes:
- dbdata:/var/lib/postgresql/data - dbdata:/var/lib/postgresql/data
ports: ports:
- 5432:5432 - "5432:5432"
environment: environment:
- POSTGRES_PASSWORD=postgres - POSTGRES_PASSWORD=postgres
web: web:
build: . build: .
environment: image: registry.gitlab.com/sahkoinsinoorikilta/vtmk/web2.0-backend
- DEPLOY_ENV=local env_file:
- HOST=localhost - .env
- DEBUG=True
- SECRET_KEY=7p$85^4ibb^p4-=vs44b7!y0e-zemugze18@a#30&71=a8)dp(
- DB_NAME=postgres
- DB_USER=postgres
- DB_PASSWD=postgres
- DB_HOST=db
- DB_PORT=5432
- EMAIL_API_KEY=
- GROUP_KEY=
- GOOGLE_CREDS='{}'
ports: ports:
- 8000:8000 - "8000:8000"
depends_on: depends_on:
- db - db
@@ -1,65 +0,0 @@
# Generated by Django 4.2.24 on 2025-10-13 14:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("kaehmy", "0011_delete_kaehmybaserole"),
]
operations = [
migrations.AlterField(
model_name="baserole",
name="category",
field=models.CharField(
choices=[
("board", "Board"),
("corporate", "Corporate affairs"),
("freshman", "Freshmen"),
("international", "International"),
("siwa", "SIK's free time"),
("media", "Media"),
("tech", "Technology"),
("wellbeing", "Wellbeing"),
("sikpaja", "Sik-paja"),
("ceremonies", "Ceremonies"),
("studies", "Studies"),
("sosso", "Sössö magazine"),
("pota", "PoTa"),
("alumni", "Alumni relations"),
("n", "N"),
("others", "Others"),
],
default="others",
max_length=255,
verbose_name="Category",
),
),
migrations.AlterField(
model_name="customrole",
name="baserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="kaehmy.baserole",
),
),
migrations.AlterField(
model_name="presetrole",
name="baserole_ptr",
field=models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="kaehmy.baserole",
),
),
]
+1 -6
View File
@@ -3,13 +3,8 @@
from django.contrib import admin from django.contrib import admin
from members.models import Member, Request, Payment from members.models import Member, Request, Payment
# Register your models here. # Register your models here.
class MemberAdmin(admin.ModelAdmin): admin.site.register(Member)
search_fields = ("first_name", "last_name", "email", "POR")
admin.site.register(Member, MemberAdmin)
admin.site.register(Request) admin.site.register(Request)
admin.site.register(Payment) admin.site.register(Payment)
Generated
+1925
View File
File diff suppressed because it is too large Load Diff
+36 -41
View File
@@ -5,49 +5,44 @@ authors = [
description = "Backend for sahkoinsinoorikilta.fi" description = "Backend for sahkoinsinoorikilta.fi"
name = "web2.0-backend" name = "web2.0-backend"
readme = "README.md" readme = "README.md"
requires-python = "~3.12" requires-python = ">=3.12"
version = "0.1.0" version = "0.1.0"
[virtualenvs] dependencies = [
create = true "decorator (>=4.4.2,<5.0.0)",
in-project = true "Django (>=4.2.19,<5.0.0)",
"django-app-namespace-template-loader (>=0.4.1,<1.0.0)",
[tool.poetry.dependencies] "django-auditlog (>=2.1.1,<3.0.0)",
decorator = "^4.4.2" "django-autocomplete-light (>=3.4.1,<4.0.0)",
Django = "^4.2.19" "django-bootstrap3 (>=21.2.0,<22.0.0)",
django-app-namespace-template-loader = "^0.4.1" "django-cors-headers (>=3.13.0,<4.0.0)",
django-auditlog = "^2.1.1" "django-filter (>=22.1.0,<23.0.0)",
django-autocomplete-light = "^3.4.1" "django-import-export (>=2.8.0,<3.0.0)",
django-bootstrap3 = "^21.2.0" "django-modeltranslation (>=0.18.4,<1.0.0)",
django-cors-headers = "^3.13.0" "django-phonenumber-field[phonenumbers] (>=6.4.0,<7.0.0)",
django-filter = "^22.1.0" "django-polymorphic (>=3.1.0,<4.0.0)",
django-import-export = "^2.8.0" "django-tables2 (>=2.4.1,<3.0.0)",
django-modeltranslation = "^0.18.4" "djangorestframework (>=3.12.4,<4.0.0)",
django-phonenumber-field = {version = "^6.4.0", extras = ["phonenumbers"]} "djangorestframework-simplejwt (>=5.2.0,<6.0.0)",
django-polymorphic = "^3.1.0" "google-auth (>=2.9.1,<3.0.0)",
django-tables2 = "^2.4.1" "google-api-python-client (>=2.54.0,<3.0.0)",
djangorestframework = "^3.12.4" "gunicorn (>=20.1.0,<21.0.0)",
djangorestframework-simplejwt = "^5.5.0" "jsonschema (>=4.9.0,<5.0.0)",
google-auth = "^2.9.1" "Markdown (>=3.2.2,<4.0.0)",
google-api-python-client = "^2.54.0" "openpyxl (>=2.6.4,<3.0.0)",
gunicorn = "^23.0.0" "Pillow (>=10.0.0,<11.0.0)",
jsonschema = "^4.9.0" "psycopg2-binary (>=2.9.3,<3.0.0)",
Markdown = "^3.2.2" "pyexcel (>=0.7.0,<1.0.0)",
openpyxl = "^2.6.4" "pyexcel-io (>=0.6.0,<1.0.0)",
Pillow = "^10.0.0" "pyexcel-xlsx (>=0.6.0,<1.0.0)",
psycopg2-binary = "^2.9.3" "python-dotenv (>=0.20.0,<1.0.0)",
pyexcel = "^0.7.0" "requests (>=2.28.1,<3.0.0)",
pyexcel-io = "^0.6.0" "sendgrid (>=6.7.0,<7.0.0)",
pyexcel-xlsx = "^0.6.0" "sentry-sdk (>=1.4.3,<2.0.0)",
python-dotenv = "^0.20.0" "six (>=1.12.0,<2.0.0)",
requests = "^2.28.1" "uWSGI (>=2.0.28,<3.0.0)",
sendgrid = "^6.7.0" "whitenoise (>=6.2.0,<7.0.0)",
sentry-sdk = "^2.24.1" ]
six = "^1.12.0"
uWSGI = "^2.0.28"
whitenoise = "^6.2.0"
pyjwt = "^2.9.0"
setuptools = "^80.9.0"
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
black = "^25.1.0" black = "^25.1.0"
+1 -7
View File
@@ -28,7 +28,6 @@
</p> </p>
<h5>{% trans "Päivämääriä & deadlineja" %}</h5> <h5>{% trans "Päivämääriä & deadlineja" %}</h5>
<ul> <ul>
<li><strong>11.10.</strong> {% blocktrans %}Toimikuntablää$t @Kiltis{% endblocktrans %}</li>
<li><strong>23.10.</strong> {% blocktrans %}Deadline hallitusvirkoihin hakemiselle.{% endblocktrans %}</li> <li><strong>23.10.</strong> {% blocktrans %}Deadline hallitusvirkoihin hakemiselle.{% endblocktrans %}</li>
<li><strong>24.10.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli{% endblocktrans %}</li> <li><strong>24.10.</strong> {% blocktrans %}Vaalikokous, osa 1 (puheenjohtajan valinta) ja hallitustyrkkypaneeli{% endblocktrans %}</li>
<li><strong>6.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen ja toimikuntien puheenjohtajien valinta){% endblocktrans %}</li> <li><strong>6.11.</strong> {% blocktrans %}Vaalikokous, osa 2 (hallituksen ja toimikuntien puheenjohtajien valinta){% endblocktrans %}</li>
@@ -77,15 +76,10 @@
<input type="checkbox" required name="gdpr" value="1"> <input type="checkbox" required name="gdpr" value="1">
<span>{% blocktrans %} <span>{% blocktrans %}
Hyväksyn <a href="https://static.sahkoinsinoorikilta.fi/GDPR/Suomeksi/Tietosuojaseloste%20%20Toimihenkilksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen. Hyväksyn <a href="https://static.sahkoinsinoorikilta.fi/GDPR/Tietosuojaseloste%20%E2%80%93%20Toimihenkil%C3%B6ksi%20hakemisen%20rekisteri.pdf" target="_blank">tietosuojaselosteen</a> ja tietojeni tallentamisen.
{% endblocktrans %} {% endblocktrans %}
</span> </span>
<br> <br>
<input type="checkbox" name="kaehmybot" value="1" checked>
<span>{% blocktrans %}
Kähmybot saa lähettää hakemuksestani ilmoituksen killan telegramiin (hallitusvirkoihin hakiessa valitse kyllä).
{% endblocktrans %}
</span>
{% buttons %} {% buttons %}
<button type="submit" class="btn btn-primary"> <button type="submit" class="btn btn-primary">
{% trans "Submit" %} {% trans "Submit" %}
+1 -1
View File
@@ -5,6 +5,6 @@
{{ challenge.message }} {{ challenge.message }}
{% trans "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 12.2" %}. {% trans "Muistattehan vahvistaa haasteen paikan päällä Smökissä torstaina 13.2" %}.
{% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }} {% trans "Käy kurkkaamassa muutkin haasteet osoitteessa" %} {{ url }}
@@ -1,32 +0,0 @@
# Generated by Django 4.2.24 on 2025-10-13 14:48
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("webapp", "0082_delete_baserole"),
]
operations = [
migrations.AddField(
model_name="signup",
name="submit_id",
field=models.UUIDField(default=uuid.uuid4, editable=False, null=True),
),
migrations.AlterField(
model_name="basewebhook",
name="polymorphic_ctype",
field=models.ForeignKey(
editable=False,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="polymorphic_%(app_label)s.%(class)s_set+",
to="contenttypes.contenttype",
),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.24 on 2025-10-13 15:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0083_signup_submit_id_alter_basewebhook_polymorphic_ctype"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.UUIDField(null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.24 on 2025-10-13 15:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0084_alter_signup_submit_id"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.CharField(null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.24 on 2025-10-13 15:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0085_alter_signup_submit_id"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.UUIDField(editable=False, null=True),
),
]
@@ -1,18 +0,0 @@
# Generated by Django 4.2.24 on 2025-10-13 15:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("webapp", "0086_alter_signup_submit_id"),
]
operations = [
migrations.AlterField(
model_name="signup",
name="submit_id",
field=models.UUIDField(editable=False, null=True, unique=True),
),
]
-2
View File
@@ -195,8 +195,6 @@ class Signup(models.Model):
email = models.EmailField(blank=True, null=True) email = models.EmailField(blank=True, null=True)
# Random unique identifier. Used for signup editing by the user. # Random unique identifier. Used for signup editing by the user.
uuid = models.UUIDField(default=uuid4, editable=False) uuid = models.UUIDField(default=uuid4, editable=False)
# Random unique identifier generated by browser upon opening signup form. Used to prevent duplicate signups.
submit_id = models.UUIDField(null=True, editable=False, unique=True)
deleted = models.BooleanField(default=False) deleted = models.BooleanField(default=False)
def __str__(self): def __str__(self):
+1 -2
View File
@@ -7,7 +7,6 @@ class SignupSerializer(serializers.ModelSerializer):
source="signupForm", queryset=SignupForm.objects.all() source="signupForm", queryset=SignupForm.objects.all()
) )
list_name = serializers.CharField(read_only=True) list_name = serializers.CharField(read_only=True)
submit_id = serializers.UUIDField(required=False)
def add_extra_fields(self, validated_data): def add_extra_fields(self, validated_data):
questions = validated_data["signupForm"].questions questions = validated_data["signupForm"].questions
@@ -35,7 +34,7 @@ class SignupSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Signup model = Signup
fields = ("id", "submit_id", "signupForm_id", "answer", "list_name") fields = ("id", "signupForm_id", "answer", "list_name")
extra_kwargs = { extra_kwargs = {
"url": { "url": {
"view_name": "signup-detail", "view_name": "signup-detail",
-14
View File
@@ -1,7 +1,6 @@
"""Webapp views.""" """Webapp views."""
import json import json
import time
from jwt import decode from jwt import decode
from jwt.exceptions import InvalidTokenError from jwt.exceptions import InvalidTokenError
from django.utils import timezone from django.utils import timezone
@@ -12,7 +11,6 @@ from django.views.decorators.http import require_http_methods
from django_filters import rest_framework as filters from django_filters import rest_framework as filters
from django.db.models import Prefetch from django.db.models import Prefetch
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db.utils import IntegrityError
from rest_framework import routers from rest_framework import routers
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
@@ -214,14 +212,6 @@ class SignupViewSet(ModelViewSet):
return self.partial_update(request, *args, **kwargs) return self.partial_update(request, *args, **kwargs)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
# Temporary manual duplicate check as submit_id uniqueness is not enforced in deployment database
if "submit_id" in request.data and Signup.objects.filter(
submit_id=request.data["submit_id"]
):
return JsonResponse(
status=200, data={"message": "The submission has already been received"}
)
id = request.data["signupForm_id"] id = request.data["signupForm_id"]
try: try:
answer = request.data["answer"] answer = request.data["answer"]
@@ -236,10 +226,6 @@ class SignupViewSet(ModelViewSet):
return JsonResponse( return JsonResponse(
status=404, data={"error": f"SignupForm {id} not found"} status=404, data={"error": f"SignupForm {id} not found"}
) )
except IntegrityError:
return JsonResponse(
status=200, data={"message": "The submission has already been received"}
)
else: else:
return JsonResponse( return JsonResponse(
status=404, data={"error": f"SignupForm {id} not found"} status=404, data={"error": f"SignupForm {id} not found"}