finalising the project configuration
This commit is contained in:
parent
9c4cb77d35
commit
96905d9f78
10 changed files with 235 additions and 53 deletions
11
Dockerfile
11
Dockerfile
|
@ -3,6 +3,9 @@ FROM python:3.12
|
||||||
|
|
||||||
# Set environment variables for Django
|
# Set environment variables for Django
|
||||||
ENV DJANGO_SETTINGS_MODULE=Palto.settings
|
ENV DJANGO_SETTINGS_MODULE=Palto.settings
|
||||||
|
ENV DJANGO_SECRET_KEY=""
|
||||||
|
ENV DATABASE_ENGINE="sqlite"
|
||||||
|
ENV DEBUG=false
|
||||||
|
|
||||||
# Set the working directory in the container
|
# Set the working directory in the container
|
||||||
WORKDIR /App
|
WORKDIR /App
|
||||||
|
@ -10,8 +13,12 @@ WORKDIR /App
|
||||||
# Copy the current directory contents into the container
|
# Copy the current directory contents into the container
|
||||||
COPY . /App
|
COPY . /App
|
||||||
|
|
||||||
# Install any needed packages specified in requirements.txt
|
# Install requirements
|
||||||
RUN pip install -r requirements.txt
|
RUN python -m pip install -r requirements.txt
|
||||||
|
|
||||||
|
# Prepare the server
|
||||||
|
RUN python manage.py collectstatic --no-input
|
||||||
|
RUN python manage.py migrate
|
||||||
|
|
||||||
# Expose the port on which your Django application will run
|
# Expose the port on which your Django application will run
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
167
Palto/Palto/migrations/0001_initial.py
Normal file
167
Palto/Palto/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
# Generated by Django 5.0.1 on 2024-01-15 17:37
|
||||||
|
|
||||||
|
import Palto.Palto.models
|
||||||
|
import django.contrib.auth.models
|
||||||
|
import django.contrib.auth.validators
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('auth', '0012_alter_user_first_name_max_length'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Absence',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('message', models.TextField()),
|
||||||
|
('start', models.DateTimeField()),
|
||||||
|
('end', models.DateTimeField()),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Department',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=64, unique=True)),
|
||||||
|
('email', models.EmailField(max_length=254)),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AbsenceAttachment',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('content', models.FileField(upload_to='absence/attachment/')),
|
||||||
|
('absence', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='Palto.absence')),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='absence',
|
||||||
|
name='department',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='absences', to='Palto.department'),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='StudentGroup',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=128)),
|
||||||
|
('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_groups', to='Palto.department')),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='User',
|
||||||
|
fields=[
|
||||||
|
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||||
|
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||||
|
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||||
|
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||||
|
('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
|
||||||
|
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||||
|
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||||
|
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||||
|
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
|
||||||
|
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'user',
|
||||||
|
'verbose_name_plural': 'users',
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
managers=[
|
||||||
|
('objects', django.contrib.auth.models.UserManager()),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TeachingUnit',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('name', models.CharField(max_length=64)),
|
||||||
|
('email', models.EmailField(blank=True, max_length=254, null=True)),
|
||||||
|
('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teaching_units', to='Palto.department')),
|
||||||
|
('student_groups', models.ManyToManyField(blank=True, related_name='studying_units', to='Palto.studentgroup')),
|
||||||
|
('managers', models.ManyToManyField(blank=True, related_name='managing_units', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('teachers', models.ManyToManyField(blank=True, related_name='teaching_units', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TeachingSession',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('start', models.DateTimeField()),
|
||||||
|
('duration', models.DurationField()),
|
||||||
|
('note', models.TextField(blank=True)),
|
||||||
|
('group', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teaching_sessions', to='Palto.studentgroup')),
|
||||||
|
('unit', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sessions', to='Palto.teachingunit')),
|
||||||
|
('teacher', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teaching_sessions', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='studentgroup',
|
||||||
|
name='owner',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owning_groups', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='studentgroup',
|
||||||
|
name='students',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='student_groups', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='StudentCard',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('uid', models.BinaryField(max_length=7)),
|
||||||
|
('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_cards', to='Palto.department')),
|
||||||
|
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='student_cards', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='department',
|
||||||
|
name='managers',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='managing_departments', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='department',
|
||||||
|
name='students',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='studying_departments', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='department',
|
||||||
|
name='teachers',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='teaching_departments', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Attendance',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('date', models.DateTimeField()),
|
||||||
|
('session', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attendances', to='Palto.teachingsession')),
|
||||||
|
('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attended_sessions', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
bases=(models.Model, Palto.Palto.models.ModelPermissionHelper),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='absence',
|
||||||
|
name='student',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='absences', to=settings.AUTH_USER_MODEL),
|
||||||
|
),
|
||||||
|
]
|
|
@ -218,7 +218,7 @@ def new_absence_view(request: WSGIRequest):
|
||||||
content=file
|
content=file
|
||||||
)
|
)
|
||||||
|
|
||||||
return redirect("Palto:homepage") # TODO(Faraphel): redirect to absence list
|
return redirect("Palto:absence_list")
|
||||||
|
|
||||||
# render the page
|
# render the page
|
||||||
return render(
|
return render(
|
||||||
|
|
|
@ -10,9 +10,12 @@ For the full list of settings and their values, see
|
||||||
https://docs.djangoproject.com/en/4.2/ref/settings/
|
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import warnings
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from django.core.management.utils import get_random_secret_key
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
|
@ -21,22 +24,16 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
|
||||||
|
|
||||||
# SECURITY WARNING: keep the secret key used in production secret!
|
# SECURITY WARNING: keep the secret key used in production secret!
|
||||||
SECRET_KEY = os.environ["DJANGO_SECRET"]
|
SECRET_KEY = os.getenv("DJANGO_SECRET_KEY")
|
||||||
|
if not SECRET_KEY:
|
||||||
|
SECRET_KEY = get_random_secret_key()
|
||||||
|
warnings.warn('The Django secret key should be defined in the "DJANGO_SECRET_KEY" variable environment.')
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = os.getenv("DEBUG", "false").lower() in ["1", "true"]
|
||||||
|
|
||||||
ALLOWED_HOSTS = [
|
ALLOWED_HOSTS = os.getenv("", "localhost 0.0.0.0").split(" ")
|
||||||
"127.0.0.1",
|
INTERNAL_IPS = ["localhost"]
|
||||||
"localhost",
|
|
||||||
"0.0.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
INTERNAL_IPS = [
|
|
||||||
"127.0.0.1",
|
|
||||||
"localhost",
|
|
||||||
"0.0.0.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
|
@ -94,12 +91,30 @@ WSGI_APPLICATION = 'Palto.wsgi.application'
|
||||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"sqlite": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': BASE_DIR / 'db.sqlite3',
|
'NAME': BASE_DIR / os.getenv("DATABASE_SQLITE_FILENAME", "db.sqlite3"),
|
||||||
}
|
},
|
||||||
|
"mysql": {
|
||||||
|
'ENGINE': 'django.db.backends.mysql',
|
||||||
|
"NAME": os.getenv("MYSQL_POSTGRES_NAME"),
|
||||||
|
"USER": os.getenv("MYSQL_POSTGRES_USER"),
|
||||||
|
"PASSWORD": os.getenv("MYSQL_POSTGRES_PASSWORD"),
|
||||||
|
"HOST": os.getenv("MYSQL_POSTGRES_HOST", "localhost"),
|
||||||
|
"PORT": os.getenv("MYSQL_POSTGRES_PORT", "5432"),
|
||||||
|
},
|
||||||
|
"postgres": {
|
||||||
|
"ENGINE": "django.db.backends.postgresql",
|
||||||
|
"NAME": os.getenv("DATABASE_POSTGRES_NAME"),
|
||||||
|
"USER": os.getenv("DATABASE_POSTGRES_USER"),
|
||||||
|
"PASSWORD": os.getenv("DATABASE_POSTGRES_PASSWORD"),
|
||||||
|
"HOST": os.getenv("DATABASE_POSTGRES_HOST", "localhost"),
|
||||||
|
"PORT": os.getenv("DATABASE_POSTGRES_PORT", "5432"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DATABASES["default"] = DATABASES[os.getenv("DATABASE_ENGINE", "sqlite")]
|
||||||
|
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
|
||||||
|
|
|
@ -8,11 +8,7 @@ https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
import dotenv
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
from utils import env
|
|
||||||
|
|
||||||
|
|
||||||
dotenv.load_dotenv(env.create_dotenv())
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
27
README.md
27
README.md
|
@ -9,6 +9,8 @@ this session.
|
||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
|
## Classic
|
||||||
|
|
||||||
1. Install `python >= 3.11`
|
1. Install `python >= 3.11`
|
||||||
2. Create a virtual environment with `python -m venv ./.venv/`. The next steps will be inside it.
|
2. Create a virtual environment with `python -m venv ./.venv/`. The next steps will be inside it.
|
||||||
3. Install the dependencies with `python -m pip install -r ./requirements.txt`.
|
3. Install the dependencies with `python -m pip install -r ./requirements.txt`.
|
||||||
|
@ -16,3 +18,28 @@ this session.
|
||||||
5. Make the migrations with `python ./manage.py makemigrations`.
|
5. Make the migrations with `python ./manage.py makemigrations`.
|
||||||
6. Apply the migrations to the database with `python ./manage.py migrate`.
|
6. Apply the migrations to the database with `python ./manage.py migrate`.
|
||||||
7. Run the program by with `python ./manage.py runserver`.
|
7. Run the program by with `python ./manage.py runserver`.
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
1. Start a terminal in the directory of the project.
|
||||||
|
2. Run `docker build`.
|
||||||
|
3. Change the environment variables to match your configuration.
|
||||||
|
|
||||||
|
# Advanced Settings
|
||||||
|
|
||||||
|
## Debug Mode
|
||||||
|
|
||||||
|
By default, the server is launch in production mode.
|
||||||
|
This disables the automatic static files serving since they are considered as being already served by nginx or apache.
|
||||||
|
You can start with the environment variable `DEBUG=true` to start it in development mode.
|
||||||
|
|
||||||
|
## Secret Key
|
||||||
|
|
||||||
|
You should set a django secret key manually in the `DJANGO_SECRET_KEY` environment variable. You can get one by
|
||||||
|
opening a python interpreter with django and calling the function `django.core.management.utils.get_random_secret_key()`.
|
||||||
|
|
||||||
|
## Database
|
||||||
|
|
||||||
|
The database used by default is `sqlite`. This is not recommended to keep it since it won't be saved by docker after
|
||||||
|
a restart if no volume are set, and it is considered a slow database engine. Using a `postgres` database is recommended.
|
||||||
|
You can find more details about the database in the configuration `settings.py`.
|
|
@ -3,15 +3,9 @@
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import dotenv
|
|
||||||
|
|
||||||
from utils import env
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Run administrative tasks."""
|
"""Run administrative tasks."""
|
||||||
dotenv.load_dotenv(env.create_dotenv())
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
|
|
|
@ -13,4 +13,3 @@ factory_boy
|
||||||
|
|
||||||
# Other librairies
|
# Other librairies
|
||||||
markdown
|
markdown
|
||||||
python-dotenv
|
|
||||||
|
|
23
utils/env.py
23
utils/env.py
|
@ -1,23 +0,0 @@
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from django.core.management.utils import get_random_secret_key
|
|
||||||
|
|
||||||
|
|
||||||
path_dotenv = Path("./.env")
|
|
||||||
|
|
||||||
|
|
||||||
def create_dotenv(force: bool = False) -> Path:
|
|
||||||
# if not forced and the file already exist, ignore
|
|
||||||
if not force and path_dotenv.exists():
|
|
||||||
return path_dotenv
|
|
||||||
|
|
||||||
# otherwise create the file
|
|
||||||
path_dotenv.write_text(
|
|
||||||
(
|
|
||||||
f"DJANGO_SETTINGS_MODULE='Palto.settings'\n"
|
|
||||||
f"DJANGO_SECRET={get_random_secret_key()!r}\n"
|
|
||||||
),
|
|
||||||
encoding="utf-8"
|
|
||||||
)
|
|
||||||
|
|
||||||
return path_dotenv
|
|
Loading…
Reference in a new issue