This commit is contained in:
Dmitriy
2025-06-23 01:24:34 +03:00
commit 60b4e0e839
303 changed files with 35737 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

15
schedule/admin.py Normal file
View File

@ -0,0 +1,15 @@
from django.contrib import admin
from .models import Lesson, LessonEnrollment
@admin.register(Lesson)
class LessonAdmin(admin.ModelAdmin):
list_display = ('course', 'instructor', 'date', 'start_time', 'end_time', 'location', 'max_students', 'created_at', 'updated_at')
list_filter = ('date', 'created_at', 'updated_at')
search_fields = ('course__title', 'instructor__profile__user__username', 'location')
date_hierarchy = 'date'
@admin.register(LessonEnrollment)
class LessonEnrollmentAdmin(admin.ModelAdmin):
list_display = ('lesson', 'student', 'created_at')
list_filter = ('created_at',)
search_fields = ('lesson__course__title', 'student__profile__user__username')

29
schedule/forms.py Normal file
View File

@ -0,0 +1,29 @@
from django import forms
from .models import Lesson, LessonEnrollment
class LessonForm(forms.ModelForm):
class Meta:
model = Lesson
fields = ['course', 'date', 'start_time', 'end_time', 'location', 'max_students']
widgets = {
'date': forms.DateInput(attrs={'type': 'date'}),
'start_time': forms.TimeInput(attrs={'type': 'time'}),
'end_time': forms.TimeInput(attrs={'type': 'time'}),
}
def clean(self):
cleaned_data = super().clean()
start_time = cleaned_data.get('start_time')
end_time = cleaned_data.get('end_time')
if start_time and end_time:
# Проверяем, что время окончания больше времени начала
if start_time.time() >= end_time:
raise forms.ValidationError('Время окончания должно быть позже времени начала')
return cleaned_data
class EnrollmentForm(forms.ModelForm):
class Meta:
model = LessonEnrollment
fields = ['lesson', 'student']

View File

@ -0,0 +1,60 @@
# Generated by Django 5.0.2 on 2025-06-10 19:27
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('course', '0001_initial'),
('instructor', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Course',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.TextField()),
('duration', models.IntegerField(help_text='Длительность в часах')),
('price', models.DecimalField(decimal_places=2, max_digits=10)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
),
migrations.CreateModel(
name='Lesson',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lesson_type', models.CharField(choices=[('theory', 'Теоретическое'), ('practice', 'Практическое'), ('exam', 'Экзамен')], max_length=20)),
('start_time', models.DateTimeField()),
('end_time', models.TimeField()),
('max_students', models.PositiveIntegerField(default=10)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to='course.course')),
('instructor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to='instructor.instructor')),
],
options={
'verbose_name': 'Занятие',
'verbose_name_plural': 'Занятия',
'ordering': ['start_time'],
},
),
migrations.CreateModel(
name='LessonEnrollment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
('lesson', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='enrollments', to='schedule.lesson')),
],
options={
'verbose_name': 'Запись на занятие',
'verbose_name_plural': 'Записи на занятия',
},
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 5.0.2 on 2025-06-10 19:27
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('schedule', '0001_initial'),
('student', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='lessonenrollment',
name='student',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='enrollments', to='student.student'),
),
migrations.AlterUniqueTogether(
name='lessonenrollment',
unique_together={('lesson', 'student')},
),
]

View File

@ -0,0 +1,55 @@
# Generated by Django 5.0.2 on 2025-06-10 20:05
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('schedule', '0002_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterModelOptions(
name='lesson',
options={'ordering': ['date', 'start_time'], 'verbose_name': 'Занятие', 'verbose_name_plural': 'Занятия'},
),
migrations.RemoveField(
model_name='lesson',
name='lesson_type',
),
migrations.AddField(
model_name='lesson',
name='date',
field=models.DateField(default=django.utils.timezone.now),
),
migrations.AddField(
model_name='lesson',
name='enrolled_students',
field=models.ManyToManyField(blank=True, related_name='enrolled_lessons', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='lesson',
name='location',
field=models.CharField(default='Учебный класс', max_length=200),
),
migrations.AlterField(
model_name='lesson',
name='course',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to='schedule.course'),
),
migrations.AlterField(
model_name='lesson',
name='instructor',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='lesson',
name='start_time',
field=models.TimeField(),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 5.0.2 on 2025-06-10 20:23
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('instructor', '0001_initial'),
('schedule', '0003_alter_lesson_options_remove_lesson_lesson_type_and_more'),
]
operations = [
migrations.RemoveField(
model_name='lesson',
name='enrolled_students',
),
migrations.AlterField(
model_name='lesson',
name='instructor',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to='instructor.instructor'),
),
]

View File

@ -0,0 +1,24 @@
# Generated by Django 5.0.2 on 2025-06-10 20:24
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('analytics', '0002_alter_courseanalytics_course'),
('course', '0001_initial'),
('schedule', '0004_remove_lesson_enrolled_students_and_more'),
]
operations = [
migrations.AlterField(
model_name='lesson',
name='course',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='lessons', to='course.course'),
),
migrations.DeleteModel(
name='Course',
),
]

View File

39
schedule/models.py Normal file
View File

@ -0,0 +1,39 @@
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from course.models import Course
from instructor.models import Instructor
from student.models import Student
from django.utils import timezone
class Lesson(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='lessons')
instructor = models.ForeignKey(Instructor, on_delete=models.CASCADE, related_name='lessons')
date = models.DateField(default=timezone.now)
start_time = models.TimeField()
end_time = models.TimeField()
location = models.CharField(max_length=200, default='Учебный класс')
max_students = models.PositiveIntegerField(default=10)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['date', 'start_time']
verbose_name = 'Занятие'
verbose_name_plural = 'Занятия'
def __str__(self):
return f"{self.course.title} - {self.date} {self.start_time}"
class LessonEnrollment(models.Model):
lesson = models.ForeignKey(Lesson, on_delete=models.CASCADE, related_name='enrollments')
student = models.ForeignKey(Student, on_delete=models.CASCADE, related_name='enrollments')
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ['lesson', 'student']
verbose_name = 'Запись на занятие'
verbose_name_plural = 'Записи на занятия'
def __str__(self):
return f"{self.student} - {self.lesson}"

13
schedule/urls.py Normal file
View File

@ -0,0 +1,13 @@
from django.urls import path
from . import views
app_name = 'schedule'
urlpatterns = [
path('', views.schedule_list, name='list'),
path('create/', views.create_lesson, name='create'),
path('<int:pk>/', views.lesson_detail, name='detail'),
path('<int:pk>/update/', views.update_lesson, name='update'),
path('<int:pk>/delete/', views.delete_lesson, name='delete'),
path('<int:pk>/enroll/', views.enroll_lesson, name='enroll'),
]

113
schedule/views.py Normal file
View File

@ -0,0 +1,113 @@
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from .models import Course, Lesson, LessonEnrollment
from .forms import LessonForm, EnrollmentForm
def schedule_list(request):
lessons = Lesson.objects.all().order_by('date', 'start_time')
return render(request, 'schedule/schedule_list.html', {'lessons': lessons})
@login_required
def course_detail(request, pk):
course = get_object_or_404(Course, pk=pk)
lessons = Lesson.objects.filter(course=course).order_by('start_time')
return render(request, 'schedule/course_detail.html', {
'course': course,
'lessons': lessons
})
@login_required
def lesson_detail(request, pk):
lesson = get_object_or_404(Lesson, pk=pk)
return render(request, 'schedule/lesson_detail.html', {'lesson': lesson})
@login_required
def create_lesson(request):
if not hasattr(request.user, 'instructor'):
messages.error(request, 'У вас нет прав для создания занятий.')
return redirect('schedule:list')
if request.method == 'POST':
form = LessonForm(request.POST)
if form.is_valid():
lesson = form.save(commit=False)
lesson.instructor = request.user.instructor
lesson.save()
messages.success(request, 'Занятие успешно создано.')
return redirect('schedule:list')
else:
form = LessonForm()
return render(request, 'schedule/lesson_form.html', {'form': form, 'action': 'create'})
@login_required
def update_lesson(request, pk):
lesson = get_object_or_404(Lesson, pk=pk)
if not hasattr(request.user, 'instructor') or lesson.instructor != request.user.instructor:
messages.error(request, 'У вас нет прав для редактирования этого занятия.')
return redirect('schedule:list')
if request.method == 'POST':
form = LessonForm(request.POST, instance=lesson)
if form.is_valid():
form.save()
messages.success(request, 'Занятие успешно обновлено.')
return redirect('schedule:list')
else:
form = LessonForm(instance=lesson)
return render(request, 'schedule/lesson_form.html', {'form': form, 'action': 'update'})
@login_required
def delete_lesson(request, pk):
lesson = get_object_or_404(Lesson, pk=pk)
if not hasattr(request.user, 'instructor') or lesson.instructor != request.user.instructor:
messages.error(request, 'У вас нет прав для удаления этого занятия.')
return redirect('schedule:list')
if request.method == 'POST':
lesson.delete()
messages.success(request, 'Занятие успешно удалено.')
return redirect('schedule:list')
return render(request, 'schedule/lesson_confirm_delete.html', {'lesson': lesson})
@login_required
def enroll_lesson(request, pk):
lesson = get_object_or_404(Lesson, pk=pk)
if not hasattr(request.user, 'student'):
messages.error(request, 'Только студенты могут записываться на занятия.')
return redirect('schedule:list')
if lesson.enrolled_students.count() >= lesson.max_students:
messages.error(request, 'К сожалению, все места на это занятие уже заняты.')
return redirect('schedule:list')
if request.user.student in lesson.enrolled_students.all():
messages.warning(request, 'Вы уже записаны на это занятие.')
else:
lesson.enrolled_students.add(request.user.student)
messages.success(request, 'Вы успешно записались на занятие.')
return redirect('schedule:detail', pk=pk)
@login_required
def enrollment_detail(request, pk):
enrollment = get_object_or_404(LessonEnrollment, pk=pk)
return render(request, 'schedule/enrollment_detail.html', {'enrollment': enrollment})
@login_required
def enrollment_create(request):
if request.method == 'POST':
form = EnrollmentForm(request.POST)
if form.is_valid():
enrollment = form.save()
messages.success(request, 'Запись на занятие успешно создана!')
return redirect('enrollment_detail', pk=enrollment.pk)
else:
form = EnrollmentForm()
return render(request, 'schedule/enrollment_form.html', {'form': form})