Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 0 additions & 12 deletions .coveragerc

This file was deleted.

30 changes: 30 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
latex/anschreiben/veranstalter.adr
# Ignore all files in db folder
db/*
# But keep the folder
!db/.gitkeep

*.pyc
htmlcov
pyenv
*.aux
*.log
.idea
*.pdf
*.adr
.project
.pydevproject
.settings/*
.coverage
settings_secret.py
static/*
venv/
.venv
.vscode/*
.python-version

node_modules/

*.mo

.env
13 changes: 13 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
DJANGO_SETTINGS_MODULE=settings.prod
ALLOWED_HOSTS=.fachschaft.informatik.tu-darmstadt.de,.d120.de
CSRF_TRUSTED_ORIGINS=
SECRET_KEY=
KEYCLOAK_CLIENT_ID=
KEYCLOAK_SECRET=
KEYCLOAK_SERVER_URL=
EMAIL_HOST=
EMAIL_PORT=
EMAIL_USE_TLS=True
EMAIL_HOST_USER=
EMAIL_HOST_PASSWORD=
GUNICORN_WORKERS=
2 changes: 1 addition & 1 deletion .github/workflows/django.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
run: |
python -m pip install --upgrade pip
pip install coveralls
pip install -r requirements.txt
pip install -e ".[dev]"
echo "PASSWORD_HASHERS = ['django.contrib.auth.hashers.MD5PasswordHasher',]" >> src/settings.py

- name: Static tests
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ pyenv
settings_secret.py
static/*
venv/
.venv/
.vscode/*
.python-version

node_modules/

*.mo

.env
66 changes: 66 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ==========================================
# Build Node.js Dependencies
# ==========================================
FROM node:22-slim AS node-builder

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

# ==========================================
# Build Python Dependencies
# ==========================================
FROM python:3.13-slim AS python-builder

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

WORKDIR /app

COPY pyproject.toml ./

COPY src ./src

RUN pip install --root-user-action ignore --upgrade pip && \
pip install --root-user-action ignore --no-cache-dir .

# ==========================================
# Final Production Image
# ==========================================
FROM python:3.13-slim AS production

ARG DEBIAN_FRONTEND=noninteractive
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

RUN apt-get update && apt-get install -y --no-install-recommends \
gettext \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

# non-root user
RUN useradd -m -r appuser

WORKDIR /app

COPY --from=python-builder /usr/local/lib/python3.13/site-packages/ /usr/local/lib/python3.13/site-packages/
COPY --from=python-builder /usr/local/bin/ /usr/local/bin/

COPY --from=node-builder /app/node_modules /app/node_modules

COPY --chown=appuser:appuser . .

COPY --chown=appuser:appuser entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# Build static assets & translations as non-root user
USER appuser

RUN django-admin compilemessages && \
python src/manage.py collectstatic --no-input --clear

EXPOSE 8000
WORKDIR /app/src

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
23 changes: 20 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,34 @@ To use pyfeedback the following tools have to be installed:

## Preparing development environment

* Create a virtualenv with `python -m venv venv`
* Activate the virtualenv with `source venv/bin/activate`
* Install all requirements with `pip install -r requirements.txt`
* Create a virtualenv with `python -m venv .venv`
* Activate the virtualenv with `source .venv/bin/activate`
* Install all requirements with `pip install -e ".[dev]"`
* Create the test database with `python src/manage.py migrate`
* Compile translations with `(cd src && django-admin compilemessages)`
* Install frontend dependencies with `npm i`
* Start the development server with `python src/manage.py runserver`

## Production
- Use `pip install .` to install dependencies.
- Docker: make sure to set `DJANGO_SETTINGS_MODULE=settings.prod` in .env
- Without docker: `gunicorn wsgi:application`

## Tests
pyfeedback is using a test driven development and tries to get to 100% coverage. Tests can be run with
```
python src/manage.py test feedback
```
Do not implement new functionality without providing a test for it.

## Settings

`src/manage.py` uses settings.dev, `src/wsgi.py` uses settings.prod

Use `python src/manage.py runserver --settings=settings.prod` to run production settings during development.


## Docker

- DJANGO_SETTINGS_MODULE: `settings.prod` or `settings.dev`. Docker uses wsgi.py, making `settings.prod` *default*.
- GUNICORN_WORKERS: *default* 3 workers
13 changes: 13 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
services:
pyfeedback:
build: .
container_name: pyfeedback-docker
ports:
- "8000:8000"
env_file:
- .env
volumes:
- pyfeedback-db:/app/db/

volumes:
pyfeedback-db:
16 changes: 16 additions & 0 deletions entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh

# Exit on error
set -e

echo "Applying database migrations..."
python manage.py migrate --no-input

if [ $# -eq 0 ]; then
echo "Starting Gunicorn with ${GUNICORN_WORKERS:-3} workers..."
exec gunicorn --bind 0.0.0.0:8000 --workers "${GUNICORN_WORKERS:-3}" wsgi:application
else
# (bash, python manage.py createsuperuser etc.)
echo "Running custom command: $@"
exec "$@"
fi
49 changes: 49 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
[build-system]
requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"

[project]
name = "pyfeedback"
version = "3.0.0"
description = "Web application to assist feedback for modules at TU Darmstadt"
readme = "README.md"
requires-python = ">=3.13"
authors = [
{ name = "d120" }
]
dependencies = [
"Django==6.0.7",
"django-formtools==2.7",
"django-allauth[socialaccount]==65.18.0",
"gunicorn==26.0.0",
"whitenoise[brotli]==6.12.0",
]

[project.optional-dependencies]
dev = [
"django-debug-toolbar==7.0.0",
"coverage==7.15.1",
"freezegun==1.5.5",
"python-dotenv>=1.0.0",
]

[tool.setuptools.packages.find]
where = ["src"]

[tool.coverage.run]
source = ["src"]
omit = [
"*/migrations/*",
"*/tests/*",
"*/tests.py",
"*/tests_*.py",
"src/manage.py",
"src/wsgi.py",
"src/asgi.py",
"*/locale/*",
]

[tool.coverage.report]
precision = 2
show_missing = true
fail_under = 85
6 changes: 0 additions & 6 deletions requirements.txt

This file was deleted.

26 changes: 0 additions & 26 deletions script/bootstrap

This file was deleted.

Binary file removed src/locale/de/LC_MESSAGES/django.mo
Binary file not shown.
Binary file removed src/locale/en/LC_MESSAGES/django.mo
Binary file not shown.
2 changes: 1 addition & 1 deletion src/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings.dev")

from django.core.management import execute_from_command_line

Expand Down
Empty file added src/settings/__init__.py
Empty file.
Loading
Loading