Merge pull request #19 from Faraphel/extra-pages
Added styles and improved some configurations
This commit is contained in:
commit
5fec4cb691
35 changed files with 1075 additions and 240 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,5 +1,12 @@
|
||||||
|
# IDE
|
||||||
.idea
|
.idea
|
||||||
|
|
||||||
|
# Virtual Environment
|
||||||
venv
|
venv
|
||||||
|
.venv
|
||||||
|
|
||||||
|
# Django
|
||||||
media
|
media
|
||||||
static-collected
|
static-collected
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
|
.env
|
||||||
|
|
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
|
||||||
|
|
|
@ -24,38 +24,45 @@ class AdminUser(admin.ModelAdmin):
|
||||||
class AdminDepartment(admin.ModelAdmin):
|
class AdminDepartment(admin.ModelAdmin):
|
||||||
list_display = ("id", "name", "email")
|
list_display = ("id", "name", "email")
|
||||||
search_fields = ("id", "name", "email")
|
search_fields = ("id", "name", "email")
|
||||||
|
readonly_fields = ("id",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.StudentGroup)
|
@admin.register(models.StudentGroup)
|
||||||
class AdminStudentGroup(admin.ModelAdmin):
|
class AdminStudentGroup(admin.ModelAdmin):
|
||||||
list_display = ("id", "name", "owner", "department")
|
list_display = ("id", "name", "owner", "department")
|
||||||
search_fields = ("id", "name", "owner", "department")
|
search_fields = ("id", "name", "owner", "department")
|
||||||
|
list_filter = ("department",)
|
||||||
|
readonly_fields = ("id",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.TeachingUnit)
|
@admin.register(models.TeachingUnit)
|
||||||
class AdminTeachingUnit(admin.ModelAdmin):
|
class AdminTeachingUnit(admin.ModelAdmin):
|
||||||
list_display = ("id", "name", "email")
|
list_display = ("id", "name", "email")
|
||||||
search_fields = ("id", "name", "email")
|
search_fields = ("id", "name", "email")
|
||||||
|
readonly_fields = ("id",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.StudentCard)
|
@admin.register(models.StudentCard)
|
||||||
class AdminStudentCard(admin.ModelAdmin):
|
class AdminStudentCard(admin.ModelAdmin):
|
||||||
list_display = ("id", "uid", "owner")
|
list_display = ("id", "uid", "department", "owner")
|
||||||
search_fields = ("id", "uid", "owner")
|
search_fields = ("id", "uid", "department", "owner")
|
||||||
readonly_fields = ("uid",)
|
readonly_fields = ("id", "uid",)
|
||||||
|
list_filter = ("department",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.TeachingSession)
|
@admin.register(models.TeachingSession)
|
||||||
class AdminTeachingSession(admin.ModelAdmin):
|
class AdminTeachingSession(admin.ModelAdmin):
|
||||||
list_display = ("id", "start", "end", "duration", "teacher")
|
list_display = ("id", "start", "end", "unit", "duration", "teacher")
|
||||||
search_fields = ("id", "start", "end", "duration", "teacher")
|
search_fields = ("id", "start", "end", "unit", "duration", "teacher")
|
||||||
list_filter = ("start", "duration")
|
readonly_fields = ("id",)
|
||||||
|
list_filter = ("unit",)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.Attendance)
|
@admin.register(models.Attendance)
|
||||||
class AdminAttendance(admin.ModelAdmin):
|
class AdminAttendance(admin.ModelAdmin):
|
||||||
list_display = ("id", "date", "student")
|
list_display = ("id", "date", "student")
|
||||||
search_fields = ("id", "date", "student")
|
search_fields = ("id", "date", "student")
|
||||||
|
readonly_fields = ("id",)
|
||||||
list_filter = ("date",)
|
list_filter = ("date",)
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,6 +70,7 @@ class AdminAttendance(admin.ModelAdmin):
|
||||||
class AdminAbsence(admin.ModelAdmin):
|
class AdminAbsence(admin.ModelAdmin):
|
||||||
list_display = ("id", "message", "student", "start", "end")
|
list_display = ("id", "message", "student", "start", "end")
|
||||||
search_fields = ("id", "message", "student", "start", "end")
|
search_fields = ("id", "message", "student", "start", "end")
|
||||||
|
readonly_fields = ("id",)
|
||||||
list_filter = ("start", "end")
|
list_filter = ("start", "end")
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,3 +78,4 @@ class AdminAbsence(admin.ModelAdmin):
|
||||||
class AdminAbsenceAttachment(admin.ModelAdmin):
|
class AdminAbsenceAttachment(admin.ModelAdmin):
|
||||||
list_display = ("id", "content", "absence")
|
list_display = ("id", "content", "absence")
|
||||||
search_fields = ("id", "content", "absence")
|
search_fields = ("id", "content", "absence")
|
||||||
|
readonly_fields = ("id",)
|
||||||
|
|
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),
|
||||||
|
),
|
||||||
|
]
|
|
@ -39,17 +39,31 @@ class ModelPermissionHelper:
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def all_editable_by_user(cls, user: "User") -> QuerySet:
|
def all_editable_by_user(cls, user: "User") -> QuerySet:
|
||||||
|
"""
|
||||||
|
Return the list of object that the user can edit
|
||||||
|
"""
|
||||||
|
|
||||||
|
def is_editable_by_user(self, user: "User") -> bool:
|
||||||
"""
|
"""
|
||||||
Return True if the user can edit this object
|
Return True if the user can edit this object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
return self in self.all_editable_by_user(user)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def all_visible_by_user(cls, user: "User") -> QuerySet:
|
def all_visible_by_user(cls, user: "User") -> QuerySet:
|
||||||
|
"""
|
||||||
|
Return the list of object that the user can see
|
||||||
|
"""
|
||||||
|
|
||||||
|
def is_visible_by_user(self, user: "User") -> bool:
|
||||||
"""
|
"""
|
||||||
Return True if the user can see this object
|
Return True if the user can see this object
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
return self in self.all_visible_by_user(user)
|
||||||
|
|
||||||
|
|
||||||
class User(AbstractUser, ModelPermissionHelper):
|
class User(AbstractUser, ModelPermissionHelper):
|
||||||
"""
|
"""
|
||||||
|
@ -522,6 +536,17 @@ class TeachingSession(models.Model, ModelPermissionHelper):
|
||||||
def end(self) -> datetime:
|
def end(self) -> datetime:
|
||||||
return self.start + self.duration
|
return self.start + self.duration
|
||||||
|
|
||||||
|
@property
|
||||||
|
def related_absences(self) -> QuerySet["Absence"]:
|
||||||
|
"""
|
||||||
|
Return the sessions that match the user absence
|
||||||
|
"""
|
||||||
|
|
||||||
|
return Absence.objects.filter(
|
||||||
|
student__in=self.group.students.all(),
|
||||||
|
start__lte=self.start, end__gte=self.end
|
||||||
|
).distinct()
|
||||||
|
|
||||||
# validations
|
# validations
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
|
|
47
Palto/Palto/static/Palto/css/base.css
Normal file
47
Palto/Palto/static/Palto/css/base.css
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/* font */
|
||||||
|
* {
|
||||||
|
font-family: "Century Gothic", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* color-scheme */
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
:root {
|
||||||
|
--primary: #00345F;
|
||||||
|
--secondary: #EF800A;
|
||||||
|
|
||||||
|
--foreground: #1B1B1B;
|
||||||
|
--background: #FFFFFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:root {
|
||||||
|
--primary: #00549F;
|
||||||
|
--secondary: #EF800A;
|
||||||
|
|
||||||
|
--foreground: #FFFFFF;
|
||||||
|
--background: #1B1B1B;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* links */
|
||||||
|
*:link, *:visited {
|
||||||
|
color: var(--primary);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
*:link:hover, *:visited:hover {
|
||||||
|
color: var(--secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* table */
|
||||||
|
table, tr, th, td {
|
||||||
|
border-color: var(--foreground) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tweaks */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--foreground);
|
||||||
|
background-color: var(--background);
|
||||||
|
}
|
9
Palto/Palto/static/Palto/css/homepage.css
Normal file
9
Palto/Palto/static/Palto/css/homepage.css
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.title {
|
||||||
|
font-size: 500%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
width: 60%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
11
Palto/Palto/static/Palto/css/login.css
Normal file
11
Palto/Palto/static/Palto/css/login.css
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#login-form {
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#login-form input {
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
}
|
28
Palto/Palto/static/Palto/css/navigation.css
Normal file
28
Palto/Palto/static/Palto/css/navigation.css
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin: 4px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation-header {
|
||||||
|
display: flex;
|
||||||
|
margin-right: auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation-app-name {
|
||||||
|
font-size: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation-title {
|
||||||
|
font-size: 300%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#navigation-buttons {
|
||||||
|
display: flex;
|
||||||
|
margin-left: auto;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
32
Palto/Palto/static/Palto/css/profile.css
Normal file
32
Palto/Palto/static/Palto/css/profile.css
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#user-informations {
|
||||||
|
text-align: center;
|
||||||
|
margin: 5% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-name {
|
||||||
|
font-size: 200%;
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-relations {
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#user-relations > tbody > tr > td, #user-relations > tbody > tr > th {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-relation {
|
||||||
|
border-style: hidden;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-relation > tbody > tr > td, .user-relation > tbody > tr > th {
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
34
Palto/Palto/static/Palto/css/table_view.css
Normal file
34
Palto/Palto/static/Palto/css/table_view.css
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#table-informations {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 2px solid black;
|
||||||
|
margin: 5% auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#table-informations th {
|
||||||
|
text-align: right;
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#table-informations td {
|
||||||
|
text-align: left;
|
||||||
|
border: 1px solid black;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#table-relations {
|
||||||
|
display: flex;
|
||||||
|
width: 60%;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-relation {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border: 2px solid black;
|
||||||
|
margin: 0 auto auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-relation th, .table-relation td {
|
||||||
|
border: 2px solid black;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
BIN
Palto/Palto/static/Palto/font/CenturyGothic.ttf
Normal file
BIN
Palto/Palto/static/Palto/font/CenturyGothic.ttf
Normal file
Binary file not shown.
52
Palto/Palto/templates/Palto/absence_list.html
Normal file
52
Palto/Palto/templates/Palto/absence_list.html
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Absences
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Absences
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
{# table of all the absences #}
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Identifiant</th>
|
||||||
|
<th>Département</th>
|
||||||
|
<th>Étudiant</th>
|
||||||
|
<th>Période</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{# show the information for every session #}
|
||||||
|
{% for absence in absences %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{% url "Palto:absence_view" absence.id %}">{{ absence.short_id }}</a></td>
|
||||||
|
<td><a href="{% url "Palto:department_view" absence.department.id %}">{{ absence.department }}</a></td>
|
||||||
|
<td><a href="{% url "Palto:profile" absence.student.id %}">{{ absence.student }}</a></td>
|
||||||
|
<td>{{ absence.start }}<br>{{ absence.end }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{# page navigator #}
|
||||||
|
{# TODO(Faraphel): page navigator as template ? #}
|
||||||
|
{# TODO(Faraphel): new absence button #}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{% if sessions.has_previous %}
|
||||||
|
<a href="?page={{ sessions.previous_page_number }}">Previous</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<a>{{ sessions.number }}</a>
|
||||||
|
|
||||||
|
{% if sessions.has_next %}
|
||||||
|
<a href="?page={{ sessions.next_page_number }}">Next</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -1,6 +1,16 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Nouvelle Absence
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Nouvelle Absence
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
<form method="POST" enctype="multipart/form-data">
|
<form method="POST" enctype="multipart/form-data">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<table>
|
<table>
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Absence
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Absence
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
{# absence's information #}
|
{# absence's information #}
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -9,7 +19,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Département</th>
|
<th>Département</th>
|
||||||
<td><a href="{# TODO(Faraphel): departement #}">{{ absence.department }}</a></td>
|
<td><a href="{% url "Palto:department_view" absence.department.id %}">{{ absence.department }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Étudiant</th>
|
<th>Étudiant</th>
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<link rel="shortcut icon" type="image/png" href="{% static 'Palto/favicon.svg' %}"/>
|
|
||||||
|
|
||||||
<title>{% block title %}Palto{% endblock %}</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
{# navigation #}
|
|
||||||
{% include "Palto/navigation.html" %}
|
|
||||||
|
|
||||||
{# body #}
|
|
||||||
{% block body %}
|
|
||||||
{% endblock %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
31
Palto/Palto/templates/Palto/base/base-features.html
Normal file
31
Palto/Palto/templates/Palto/base/base-features.html
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{% extends "Palto/base/base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/navigation.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<div id="navigation-header">
|
||||||
|
<img id="navigation-icon" src="{% static "Palto/favicon.svg" %}" alt="Palto's icon" />
|
||||||
|
<a id="navigation-app-name">Palto</a>
|
||||||
|
</div>
|
||||||
|
<div id="navigation-title">
|
||||||
|
{% block navigation-title %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
<div id="navigation-buttons">
|
||||||
|
<a href="{% url "Palto:homepage" %}">Accueil</a>
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<a href="{% url "Palto:my_profile" %}">Profil</a>
|
||||||
|
<a href="{% url "Palto:logout" %}">Déconnexion</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url "Palto:login" %}">Connexion</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
{% endblock %}
|
30
Palto/Palto/templates/Palto/base/base.html
Normal file
30
Palto/Palto/templates/Palto/base/base.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
{# base meta #}
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="shortcut icon" type="image/png" href="{% static 'Palto/favicon.svg' %}"/>
|
||||||
|
<title>{% block page-title %}Palto{% endblock %}</title>
|
||||||
|
|
||||||
|
{# base style #}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
<style>
|
||||||
|
{# font #}
|
||||||
|
@font-face {
|
||||||
|
font-family: "Century Gothic";
|
||||||
|
src: url("{% static 'Palto/font/CenturyGothic.ttf' %}");
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/base.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{# body #}
|
||||||
|
{% block body %}
|
||||||
|
{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
78
Palto/Palto/templates/Palto/department_view.html
Normal file
78
Palto/Palto/templates/Palto/department_view.html
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
{% load dict_tags %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Départment {{ department.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Départment {{ department.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/table_view.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
{# department's information #}
|
||||||
|
<table id="table-informations">
|
||||||
|
<tr>
|
||||||
|
<th>Identifiant</th>
|
||||||
|
<td>{{ department.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Nom</th>
|
||||||
|
<td>{{ department.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Mail</th>
|
||||||
|
<td><a href="mailto:{{ department.email }}">{{ department.email }}</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Enseignants</th>
|
||||||
|
<td>{{ department.teachers.count }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Étudiants</th>
|
||||||
|
<td>{{ department.students.count }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="table-relations">
|
||||||
|
{# department's managers #}
|
||||||
|
<table class="table-relation">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Responsables</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for manager in department.managers.all %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{% url "Palto:profile" manager.id %}">{{ manager }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{# department's teachers #}
|
||||||
|
<table class="table-relation">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Enseignants</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for teacher in department.teachers.all %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{% url "Palto:profile" teacher.id %}">{{ teacher }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -1,5 +1,25 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Accueil
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Accueil
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/homepage.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
Hello there.
|
{{ block.super }}
|
||||||
|
|
||||||
|
<h1 class="title">Palto</h1>
|
||||||
|
|
||||||
|
<p class="text">
|
||||||
|
Palto est un outil de gestion des présences d'élèves dans vos établissements scolaires.
|
||||||
|
</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,10 +1,26 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Connexion
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Connexion
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/login.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
<form method="POST">
|
{{ block.super }}
|
||||||
|
|
||||||
|
<form id="login-form" method="POST">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<table>
|
<table>
|
||||||
{{ form_login.as_table }}
|
{{ form_login.as_p }}
|
||||||
</table>
|
</table>
|
||||||
<input type="submit" value="Se connecter">
|
<input type="submit" value="Se connecter">
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
{% load static %}
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<img src="{% static "Palto/favicon.svg" %}" alt="Palto's icon" width="5%">
|
|
||||||
<a>Palto</a>
|
|
||||||
|
|
||||||
<a href="{% url "Palto:homepage" %}">Home</a>
|
|
||||||
<a href="{% url "Palto:my_profile" %}">Profile</a>
|
|
||||||
<a href="{% url "Palto:login" %}">Login</a>
|
|
||||||
<a href="{% url "Palto:logout" %}">Logout</a>
|
|
||||||
</nav>
|
|
|
@ -1,20 +1,44 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
{% load static %}
|
||||||
{% load dict_tags %}
|
{% load dict_tags %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Profil
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Profil de {{ profile.first_name|title }} {{ profile.last_name|upper }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/profile.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
{{ profile.username }}
|
{{ block.super }}
|
||||||
{{ profile.email }}
|
|
||||||
{% if profile.is_superuser %}Administrator{% endif %}
|
{# user informations #}
|
||||||
|
<table id="user-informations">
|
||||||
|
<tbody>
|
||||||
|
<tr><td id="user-name">{{ profile.first_name|title }} {{ profile.last_name|upper }}</td></tr>
|
||||||
|
<tr><td id="user-username">{{ profile.username }}</td></tr>
|
||||||
|
<tr><td id="user-mail"><a href="mailto:{{ profile.email }}">{{ profile.email }}</a></td></tr>
|
||||||
|
<tr><td id="user-role">{% if profile.is_superuser %}Administrator{% endif %}</td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
{# user related departments table #}
|
{# user related departments table #}
|
||||||
<table>
|
<table id="user-relations">
|
||||||
|
<tbody>
|
||||||
{% for department, profile_department_data in profile_departments_data.items %}
|
{% for department, profile_department_data in profile_departments_data.items %}
|
||||||
<tr>
|
<tr>
|
||||||
{# department name #}
|
{# department name #}
|
||||||
<th>{{ department.name }}</th>
|
<th><a href="{% url "Palto:department_view" department.id %}">{{ department.name }}</a></th>
|
||||||
{# relation information #}
|
{# relation information #}
|
||||||
<td>
|
<td>
|
||||||
<table>
|
<table class="user-relation">
|
||||||
|
<tbody>
|
||||||
{# user managing the department #}
|
{# user managing the department #}
|
||||||
{% if profile_department_data|dict_get:"is_manager" %}
|
{% if profile_department_data|dict_get:"is_manager" %}
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -61,16 +85,18 @@
|
||||||
<td>Groupe Étudiant</td>
|
<td>Groupe Étudiant</td>
|
||||||
<td>
|
<td>
|
||||||
{% for student_group in student_groups %}
|
{% for student_group in student_groups %}
|
||||||
<a href="{# TODO(Faraphel): redirect to group #}">{{ student_group.name }}</a>
|
<a href="{% url "Palto:student_group_view" student_group.id %}">{{ student_group.name }}</a>
|
||||||
{% if not forloop.last %}<br/>{% endif %}
|
{% if not forloop.last %}<br/>{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
57
Palto/Palto/templates/Palto/student_group.html
Normal file
57
Palto/Palto/templates/Palto/student_group.html
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Groupe Étudiant {{ group.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Groupe Étudiant {{ group.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/table_view.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
{# group's information #}
|
||||||
|
<table id="table-informations">
|
||||||
|
<tr>
|
||||||
|
<th>Identifiant</th>
|
||||||
|
<td>{{ group.id }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Nom</th>
|
||||||
|
<td>{{ group.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Département</th>
|
||||||
|
<td><a href="{% url "Palto:department_view" group.department.id %}">{{ group.department }}</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Propriétaire</th>
|
||||||
|
<td><a href="{% url "Palto:profile" group.owner.id %}">{{ group.owner }}</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div id="table-relations">
|
||||||
|
{# group's students information #}
|
||||||
|
<table class="table-relation">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Étudiants</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for student in group.students.all %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{% url "Palto:profile" student.id %}">{{ student }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -1,6 +1,15 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Sessions
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Sessions
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
{# table of all the sessions #}
|
{# table of all the sessions #}
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
{% load dict_tags %}
|
{% load dict_tags %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Session de {{ session.unit.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Session de {{ session.unit.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/table_view.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
{# session's information #}
|
{# session's information #}
|
||||||
<table>
|
<table id="table-informations">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Identifiant</th>
|
<th>Identifiant</th>
|
||||||
<td>{{ session.id }}</td>
|
<td>{{ session.id }}</td>
|
||||||
|
@ -26,12 +42,13 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Groupe</th>
|
<th>Groupe</th>
|
||||||
<td>{{ session.group }}</td>
|
<td><a href="{% url "Palto:student_group_view" session.group.id %}">{{ session.group }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div id="table-relations">
|
||||||
{# session's students information #}
|
{# session's students information #}
|
||||||
<table>
|
<table class="table-relation">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Elève</th>
|
<th>Elève</th>
|
||||||
|
@ -61,6 +78,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
{# TODO(Raphaël): export boutton #}
|
{# TODO(Raphaël): export boutton #}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,9 +1,25 @@
|
||||||
{% extends "Palto/base.html" %}
|
{% extends "Palto/base/base-features.html" %}
|
||||||
{% load dict_tags %}
|
{% load dict_tags %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block page-title %}
|
||||||
|
Unité d'Enseignement de {{ unit.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block navigation-title %}
|
||||||
|
Unité d'Enseignement de {{ unit.name }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block style %}
|
||||||
|
{{ block.super }}
|
||||||
|
<link rel="stylesheet" href="{% static "Palto/css/table_view.css" %}" />
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
{# unit's information #}
|
{# unit's information #}
|
||||||
<table>
|
<table id="table-informations">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Identifiant</th>
|
<th>Identifiant</th>
|
||||||
<td>{{ unit.id }}</td>
|
<td>{{ unit.id }}</td>
|
||||||
|
@ -14,7 +30,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Département</th>
|
<th>Département</th>
|
||||||
<td href="{# TODO(Faraphel): department url #}">{{ unit.department.name }}</td>
|
<td><a href="{% url "Palto:department_view" unit.department.id %}">{{ unit.department.name }}</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Mail</th>
|
<th>Mail</th>
|
||||||
|
@ -26,8 +42,9 @@
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
<div id="table-relations">
|
||||||
{# unit's managers #}
|
{# unit's managers #}
|
||||||
<table>
|
<table class="table-relation">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Responsables</th>
|
<th>Responsables</th>
|
||||||
|
@ -43,7 +60,7 @@
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{# unit's teachers #}
|
{# unit's teachers #}
|
||||||
<table>
|
<table class="table-relation">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Enseignants</th>
|
<th>Enseignants</th>
|
||||||
|
@ -57,4 +74,5 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -19,6 +19,12 @@ urlpatterns = [
|
||||||
path("profile/", views.profile_view, name="my_profile"),
|
path("profile/", views.profile_view, name="my_profile"),
|
||||||
path("profile/<uuid:profile_id>/", views.profile_view, name="profile"),
|
path("profile/<uuid:profile_id>/", views.profile_view, name="profile"),
|
||||||
|
|
||||||
|
# Student groups
|
||||||
|
path("student_groups/view/<uuid:group_id>/", views.student_group_view, name="student_group_view"),
|
||||||
|
|
||||||
|
# Departments
|
||||||
|
path("departments/view/<uuid:department_id>/", views.department_view, name="department_view"),
|
||||||
|
|
||||||
# Units
|
# Units
|
||||||
path("teaching_units/view/<uuid:unit_id>/", views.teaching_unit_view, name="teaching_unit_view"),
|
path("teaching_units/view/<uuid:unit_id>/", views.teaching_unit_view, name="teaching_unit_view"),
|
||||||
|
|
||||||
|
@ -27,6 +33,7 @@ urlpatterns = [
|
||||||
path("teaching_sessions/view/<uuid:session_id>/", views.teaching_session_view, name="teaching_session_view"),
|
path("teaching_sessions/view/<uuid:session_id>/", views.teaching_session_view, name="teaching_session_view"),
|
||||||
|
|
||||||
# Absences
|
# Absences
|
||||||
|
path("absences/", views.absence_list_view, name="absence_list"),
|
||||||
path("absences/view/<uuid:absence_id>/", views.absence_view, name="absence_view"),
|
path("absences/view/<uuid:absence_id>/", views.absence_view, name="absence_view"),
|
||||||
path("absences/new/", views.new_absence_view, name="absence_new"),
|
path("absences/new/", views.new_absence_view, name="absence_new"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.db.models import Model, Manager
|
from django.db.models import Model, QuerySet
|
||||||
|
|
||||||
|
|
||||||
def get_object_or_none(manager: Manager, *args, **kwargs) -> Optional[Model]:
|
def get_object_or_none(manager: QuerySet, *args, **kwargs) -> Optional[Model]:
|
||||||
"""
|
"""
|
||||||
Similar to the Manager.get method, but return None instead of raising an error.
|
Similar to the Manager.get method, but return None instead of raising an error.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,7 +8,6 @@ from django.contrib.auth import login, authenticate, logout
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.core.handlers.wsgi import WSGIRequest
|
from django.core.handlers.wsgi import WSGIRequest
|
||||||
from django.core.paginator import Paginator
|
from django.core.paginator import Paginator
|
||||||
from django.db import IntegrityError
|
|
||||||
from django.http import HttpResponseForbidden
|
from django.http import HttpResponseForbidden
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
|
|
||||||
|
@ -25,6 +24,8 @@ def homepage_view(request: WSGIRequest):
|
||||||
|
|
||||||
def login_view(request: WSGIRequest):
|
def login_view(request: WSGIRequest):
|
||||||
# create a login form
|
# create a login form
|
||||||
|
|
||||||
|
if request.POST:
|
||||||
form_login = forms.LoginForm(request.POST)
|
form_login = forms.LoginForm(request.POST)
|
||||||
|
|
||||||
if form_login.is_valid():
|
if form_login.is_valid():
|
||||||
|
@ -43,6 +44,8 @@ def login_view(request: WSGIRequest):
|
||||||
else:
|
else:
|
||||||
# otherwise the credentials were invalid.
|
# otherwise the credentials were invalid.
|
||||||
form_login.add_error(field=None, error="Invalid credentials.")
|
form_login.add_error(field=None, error="Invalid credentials.")
|
||||||
|
else:
|
||||||
|
form_login = forms.LoginForm()
|
||||||
|
|
||||||
# return the page
|
# return the page
|
||||||
return render(
|
return render(
|
||||||
|
@ -72,7 +75,7 @@ def profile_view(request: WSGIRequest, profile_id: uuid.UUID = None):
|
||||||
profile = get_object_or_404(models.User, id=profile_id)
|
profile = get_object_or_404(models.User, id=profile_id)
|
||||||
|
|
||||||
# check if the user is allowed to see this specific object
|
# check if the user is allowed to see this specific object
|
||||||
if profile not in models.User.all_visible_by_user(request.user):
|
if not profile.is_visible_by_user(request.user):
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
# prepare the data and the "complex" query for the template
|
# prepare the data and the "complex" query for the template
|
||||||
|
@ -124,8 +127,7 @@ def teaching_unit_view(request: WSGIRequest, unit_id: uuid.UUID):
|
||||||
unit = get_object_or_404(models.TeachingUnit, id=unit_id)
|
unit = get_object_or_404(models.TeachingUnit, id=unit_id)
|
||||||
|
|
||||||
# check if the user is allowed to see this specific object
|
# check if the user is allowed to see this specific object
|
||||||
if unit not in models.TeachingUnit.all_visible_by_user(request.user):
|
if not unit.is_visible_by_user(request.user):
|
||||||
# TODO(Faraphel): syntaxic sugar session.visible_by_user(request.user)
|
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
# render the page
|
# render the page
|
||||||
|
@ -143,23 +145,14 @@ def teaching_session_view(request: WSGIRequest, session_id: uuid.UUID):
|
||||||
session = get_object_or_404(models.TeachingSession, id=session_id)
|
session = get_object_or_404(models.TeachingSession, id=session_id)
|
||||||
|
|
||||||
# check if the user is allowed to see this specific object
|
# check if the user is allowed to see this specific object
|
||||||
if session not in models.TeachingSession.all_visible_by_user(request.user):
|
if not session.is_visible_by_user(request.user):
|
||||||
# TODO(Faraphel): syntaxic sugar session.visible_by_user(request.user)
|
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
# prepare the data and the "complex" query for the template
|
# prepare the data and the "complex" query for the template
|
||||||
session_students_data = {
|
session_students_data = {
|
||||||
student: {
|
student: {
|
||||||
"attendance": get_object_or_none(
|
"attendance": get_object_or_none(models.Attendance.objects, session=session, student=student),
|
||||||
models.Attendance.objects,
|
"absence": get_object_or_none(session.related_absences, student=student)
|
||||||
session=session,
|
|
||||||
student=student
|
|
||||||
),
|
|
||||||
"absence": get_object_or_none(
|
|
||||||
models.Absence.objects,
|
|
||||||
student=student,
|
|
||||||
start__lte=session.start, end__gte=session.end
|
|
||||||
), # TODO(Faraphel): property ?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for student in session.group.students.all()
|
for student in session.group.students.all()
|
||||||
|
@ -181,8 +174,7 @@ def absence_view(request: WSGIRequest, absence_id: uuid.UUID):
|
||||||
absence = get_object_or_404(models.Absence, id=absence_id)
|
absence = get_object_or_404(models.Absence, id=absence_id)
|
||||||
|
|
||||||
# check if the user is allowed to see this specific object
|
# check if the user is allowed to see this specific object
|
||||||
if absence not in models.Absence.all_visible_by_user(request.user):
|
if not absence.is_visible_by_user(request.user):
|
||||||
# TODO(Faraphel): syntaxic sugar session.visible_by_user(request.user)
|
|
||||||
return HttpResponseForbidden()
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
# render the page
|
# render the page
|
||||||
|
@ -226,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(
|
||||||
|
@ -236,3 +228,59 @@ def new_absence_view(request: WSGIRequest):
|
||||||
form_new_absence=form_new_absence,
|
form_new_absence=form_new_absence,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def absence_list_view(request):
|
||||||
|
# get all the absences that the user can see, sorted by starting date
|
||||||
|
raw_absences = models.Absence.all_visible_by_user(request.user).order_by("start")
|
||||||
|
# paginate them to avoid having too many elements at the same time
|
||||||
|
paginator = Paginator(raw_absences, ELEMENT_PER_PAGE)
|
||||||
|
|
||||||
|
# get only the session for the requested page
|
||||||
|
page = request.GET.get("page", 0)
|
||||||
|
absences = paginator.get_page(page)
|
||||||
|
|
||||||
|
# render the page
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"Palto/absence_list.html",
|
||||||
|
context=dict(
|
||||||
|
absences=absences
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def department_view(request: WSGIRequest, department_id: uuid.UUID):
|
||||||
|
department = get_object_or_404(models.Department, id=department_id)
|
||||||
|
|
||||||
|
# check if the user is allowed to see this specific object
|
||||||
|
if not department.is_visible_by_user(request.user):
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
# render the page
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"Palto/department_view.html",
|
||||||
|
context=dict(
|
||||||
|
department=department,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def student_group_view(request: WSGIRequest, group_id: uuid.UUID):
|
||||||
|
group = get_object_or_404(models.StudentGroup, id=group_id)
|
||||||
|
|
||||||
|
# check if the user is allowed to see this specific object
|
||||||
|
if not group.is_visible_by_user(request.user):
|
||||||
|
return HttpResponseForbidden()
|
||||||
|
|
||||||
|
# render the page
|
||||||
|
return render(
|
||||||
|
request,
|
||||||
|
"Palto/student_group.html",
|
||||||
|
context=dict(
|
||||||
|
group=group,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
|
@ -7,10 +7,11 @@ For more information on this file, see
|
||||||
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
|
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import dotenv
|
||||||
|
|
||||||
from django.core.asgi import get_asgi_application
|
from django.core.asgi import get_asgi_application
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Palto.settings')
|
from utils import env
|
||||||
|
|
||||||
|
|
||||||
|
dotenv.load_dotenv(env.create_dotenv())
|
||||||
application = get_asgi_application()
|
application = get_asgi_application()
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
@ -93,13 +90,31 @@ WSGI_APPLICATION = 'Palto.wsgi.application'
|
||||||
# Database
|
# Database
|
||||||
# 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
|
||||||
|
|
|
@ -7,10 +7,8 @@ For more information on this file, see
|
||||||
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
|
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Palto.settings')
|
|
||||||
|
|
||||||
application = get_wsgi_application()
|
application = get_wsgi_application()
|
||||||
|
|
46
README.md
46
README.md
|
@ -1 +1,45 @@
|
||||||
# M1-Projet-Serveur
|
# Palto-Server
|
||||||
|
|
||||||
|
(This is a project realised for our University, it will not be maintained after and it should not be used outside of testing)
|
||||||
|
|
||||||
|
Palto is a project to check students attendances at their school classes.
|
||||||
|
It allows teachers to create sessions containing students that should be present.
|
||||||
|
They can then scan their student card with the NFC technology and they will be automatically marked as present to
|
||||||
|
this session.
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
## Classic
|
||||||
|
|
||||||
|
1. Install `python >= 3.11`
|
||||||
|
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`.
|
||||||
|
4. Modify the `Palto/settings.py` file to setup your database and other settings.
|
||||||
|
5. Make the migrations with `python ./manage.py makemigrations`.
|
||||||
|
6. Apply the migrations to the database with `python ./manage.py migrate`.
|
||||||
|
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`.
|
|
@ -6,7 +6,9 @@ import sys
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Run administrative tasks."""
|
"""Run administrative tasks."""
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Palto.settings')
|
|
||||||
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Palto.settings")
|
||||||
|
|
||||||
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:
|
||||||
|
|
Loading…
Reference in a new issue