diff --git a/access/migrations/0001_initial.py b/access/migrations/0001_initial.py index 885aebb..be411b3 100644 --- a/access/migrations/0001_initial.py +++ b/access/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-11-28 11:50 +# Generated by Django 1.11.6 on 2017-11-28 15:18 from __future__ import unicode_literals import access.models diff --git a/access/migrations/0002_auto_20171128_1150.py b/access/migrations/0002_auto_20171128_1150.py deleted file mode 100644 index 82e0680..0000000 --- a/access/migrations/0002_auto_20171128_1150.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-11-28 11:50 -from __future__ import unicode_literals - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0008_alter_user_username_max_length'), - ('access', '0001_initial'), - ('courses', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='progress', - name='course', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='Курс'), - ), - migrations.AddField( - model_name='progress', - name='progress_list', - field=models.ManyToManyField(blank=True, to='courses.Vertex', verbose_name='Лист пройденных объектов'), - ), - migrations.AddField( - model_name='progress', - name='template', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.CourseRoute', verbose_name='Шаблон для прохождения если не указан явно смотри функцию get_template()'), - ), - migrations.AddField( - model_name='progress', - name='user', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент'), - ), - migrations.AddField( - model_name='invite', - name='owner', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='account', - name='owner', - field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='user', - name='groups', - field=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'), - ), - migrations.AddField( - model_name='user', - name='user_permissions', - field=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'), - ), - migrations.AlterUniqueTogether( - name='progress', - unique_together=set([('user', 'course')]), - ), - ] diff --git a/access/models.py b/access/models.py index d4a1ca3..559b3b2 100755 --- a/access/models.py +++ b/access/models.py @@ -5,7 +5,8 @@ import string from django.contrib.contenttypes.models import ContentType from django_celery_results.models import TaskResult -from courses.models import Vertex, Course, CourseMap, CourseRoute +from courses.models import Vertex, Course +from maps.models import CourseMap, CourseRoute from storage.models import Storage from django.core.mail import send_mail diff --git a/achievements/__init__.py b/achievements/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/achievements/admin.py b/achievements/admin.py new file mode 100644 index 0000000..55e989a --- /dev/null +++ b/achievements/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from achievements.models import Skills, Achievements, SkillJ, DiplomaGen, Diploma + +admin.site.register(Skills) +admin.site.register(Achievements) +admin.site.register(SkillJ) +admin.site.register(Diploma) +admin.site.register(DiplomaGen) diff --git a/achievements/apps.py b/achievements/apps.py new file mode 100644 index 0000000..fe47629 --- /dev/null +++ b/achievements/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AchievementsConfig(AppConfig): + name = 'achievements' diff --git a/achievements/migrations/0001_initial.py b/achievements/migrations/0001_initial.py new file mode 100644 index 0000000..c6f3d4f --- /dev/null +++ b/achievements/migrations/0001_initial.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-28 15:18 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Achievements', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('icon', models.ImageField(blank=True, null=True, upload_to='diplomas', verbose_name='Отображение достижения')), + ], + options={ + 'verbose_name': 'Достижение', + 'verbose_name_plural': 'Достижения', + }, + ), + migrations.CreateModel( + name='Diploma', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('icon', models.ImageField(upload_to='diplomas', verbose_name='Иконка')), + ], + options={ + 'verbose_name': 'Диплом', + 'verbose_name_plural': 'Дипломы', + }, + ), + migrations.CreateModel( + name='DiplomaGen', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('template', models.URLField(verbose_name='Путь до шаблона')), + ], + options={ + 'verbose_name': 'Генератор дипломов', + 'verbose_name_plural': 'Генераторы дипловов', + }, + ), + migrations.CreateModel( + name='SkillJ', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + options={ + 'verbose_name': 'Размер навыка', + 'verbose_name_plural': 'Размеры навыков', + }, + ), + migrations.CreateModel( + name='Skills', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=255, verbose_name='Наименование')), + ('color', models.CharField(max_length=255, verbose_name='Цвет')), + ('icon', models.ImageField(help_text='65x65', null=True, upload_to='skills', verbose_name='Большая картинка')), + ('description', models.TextField(blank=True, verbose_name='Описание')), + ], + options={ + 'verbose_name': 'Навык', + 'verbose_name_plural': 'Навыки', + }, + ), + ] diff --git a/achievements/migrations/0002_auto_20171128_1518.py b/achievements/migrations/0002_auto_20171128_1518.py new file mode 100644 index 0000000..a48f2d5 --- /dev/null +++ b/achievements/migrations/0002_auto_20171128_1518.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-28 15:18 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('achievements', '0001_initial'), + ('courses', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='skillj', + name='lesson', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Vertex', verbose_name='Урок'), + ), + migrations.AddField( + model_name='skillj', + name='skill', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='achievements.Skills', verbose_name='Навык'), + ), + migrations.AddField( + model_name='diplomagen', + name='course', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course'), + ), + migrations.AddField( + model_name='diploma', + name='template', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='achievements.DiplomaGen', verbose_name='Использовать шаблон'), + ), + migrations.AddField( + model_name='diploma', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='achievements', + name='course', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course'), + ), + migrations.AddField( + model_name='achievements', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/achievements/migrations/__init__.py b/achievements/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/achievements/models.py b/achievements/models.py new file mode 100644 index 0000000..cd1b63d --- /dev/null +++ b/achievements/models.py @@ -0,0 +1,66 @@ +from django.db import models +from django.conf import settings + +from courses.models import Course, Vertex + + +class Achievements(models.Model): + course = models.ForeignKey(to=Course) + icon = models.ImageField(verbose_name='Отображение достижения', upload_to='diplomas', blank=True, null=True) + user = models.ForeignKey(to=settings.AUTH_USER_MODEL) + + def __str__(self): + return 'Студенту %s за курс %s' % (self.user.username, self.course.title) + + class Meta: + verbose_name = 'Достижение' + verbose_name_plural = 'Достижения' + + +class Skills(models.Model): + title = models.CharField(verbose_name='Наименование', max_length=255) + color = models.CharField(verbose_name='Цвет', max_length=255) + icon = models.ImageField(verbose_name='Большая картинка', upload_to='skills', null=True, help_text='65x65') + description = models.TextField(verbose_name='Описание', blank=True) + + def __str__(self): return '%s' % self.title + + class Meta: + verbose_name = 'Навык' + verbose_name_plural = 'Навыки' + + +class SkillJ(models.Model): + skill = models.ForeignKey(to=Skills, verbose_name='Навык') + lesson = models.ForeignKey(to=Vertex, verbose_name='Урок') + + def __str__(self): return '%s' % self.skill + + class Meta: + verbose_name = 'Размер навыка' + verbose_name_plural = 'Размеры навыков' + + +class DiplomaGen(models.Model): + course = models.ForeignKey(to=Course) + template = models.URLField(verbose_name="Путь до шаблона") + + def __str__(self): + return 'Шаблон можно найти по адресу: %s, диплом выдаётся за курс %s' % (self.template, self.course.title) + + class Meta: + verbose_name = 'Генератор дипломов' + verbose_name_plural = 'Генераторы дипловов' + + +class Diploma(models.Model): + icon = models.ImageField(verbose_name='Иконка', upload_to='diplomas') + template = models.ForeignKey(to=DiplomaGen, verbose_name='Использовать шаблон', blank=True, null=True) + user = models.ForeignKey(to=settings.AUTH_USER_MODEL) + + def __str__(self): + return 'Студенту %s за курс %s' % (self.user.username, self.template.course.title) + + class Meta: + verbose_name = 'Диплом' + verbose_name_plural = 'Дипломы' \ No newline at end of file diff --git a/achievements/tests.py b/achievements/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/achievements/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/achievements/views.py b/achievements/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/achievements/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/courses/admin.py b/courses/admin.py index ddbf93a..99b9a23 100755 --- a/courses/admin.py +++ b/courses/admin.py @@ -1,16 +1,9 @@ from django.contrib import admin -from courses.models import Course, Skills, Achievements, SkillJ,\ - CourseMap, Topic, Task, Vertex, Diploma, Tutorial, DiplomaGen +from courses.models import Course, Tutorial, Topic, Task, Vertex -admin.site.register(CourseMap) admin.site.register(Topic) admin.site.register(Task) admin.site.register(Vertex) admin.site.register(Tutorial) -admin.site.register(Course) -admin.site.register(Skills) -admin.site.register(Achievements) -admin.site.register(SkillJ) -admin.site.register(Diploma) -admin.site.register(DiplomaGen) \ No newline at end of file +admin.site.register(Course) \ No newline at end of file diff --git a/courses/migrations/0001_initial.py b/courses/migrations/0001_initial.py index 5a63200..ae011b6 100644 --- a/courses/migrations/0001_initial.py +++ b/courses/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-11-28 11:50 +# Generated by Django 1.11.6 on 2017-11-28 15:18 from __future__ import unicode_literals from django.conf import settings @@ -12,23 +12,12 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), ('storage', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ - migrations.CreateModel( - name='Achievements', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('icon', models.ImageField(blank=True, null=True, upload_to='diplomas', verbose_name='Отображение достижения')), - ], - options={ - 'verbose_name': 'Достижение', - 'verbose_name_plural': 'Достижения', - }, - ), migrations.CreateModel( name='Course', fields=[ @@ -37,6 +26,7 @@ class Migration(migrations.Migration): ('level', models.CharField(choices=[('B', 'Базовый'), ('A', 'Продвинутый'), ('E', 'Экспертный'), ('B+A', 'Базовый + Продвинутый')], default='B', max_length=3, verbose_name='Уровень')), ('slug', models.SlugField(blank=True, default='', editable=False, max_length=255, unique=True)), ('direction', models.SmallIntegerField(choices=[(3, 'Бизнес'), (2, 'Веб-дизайн'), (1, 'Разработка'), (4, 'Рисование')], null=True, verbose_name='Направление')), + ('sort', models.SmallIntegerField(null=True, verbose_name='Порядок сортировки')), ('public', models.BooleanField(default=False, verbose_name='Опубликовать')), ('title', models.CharField(max_length=255, verbose_name='Заголовок')), ('description', models.TextField(blank=True, verbose_name='Описание')), @@ -50,90 +40,6 @@ class Migration(migrations.Migration): 'verbose_name_plural': 'Курсы', }, ), - migrations.CreateModel( - name='CourseMap', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='К какому курсу привязан')), - ], - options={ - 'verbose_name': 'Карта линейного прохождения курсов', - 'verbose_name_plural': 'Карты линейного прохождения курсов', - }, - ), - migrations.CreateModel( - name='CourseRoute', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=255, null=True, unique=True, verbose_name='Имя шаблона')), - ('is_template', models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')), - ('maps', models.ManyToManyField(to='courses.CourseMap', verbose_name='Карта линейного прохождения курсов')), - ], - options={ - 'verbose_name': 'Маршрут прохождения', - 'verbose_name_plural': 'Маршруты прохождения', - }, - ), - migrations.CreateModel( - name='Diploma', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('icon', models.ImageField(upload_to='diplomas', verbose_name='Иконка')), - ], - options={ - 'verbose_name': 'Диплом', - 'verbose_name_plural': 'Дипломы', - }, - ), - migrations.CreateModel( - name='DiplomaGen', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('template', models.URLField(verbose_name='Путь до шаблона')), - ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course')), - ], - options={ - 'verbose_name': 'Генератор дипломов', - 'verbose_name_plural': 'Генераторы дипловов', - }, - ), - migrations.CreateModel( - name='PivotVertex', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sort', models.SmallIntegerField(unique=True, verbose_name='Порядок сортировки')), - ('map_course', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.CourseMap', verbose_name='К какой сортеровке имеетотношение')), - ], - options={ - 'verbose_name': 'Порядок сортировки узла', - 'verbose_name_plural': 'Порядки сортировок узла', - 'ordering': ('sort',), - }, - ), - migrations.CreateModel( - name='SkillJ', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ], - options={ - 'verbose_name': 'Размер навыка', - 'verbose_name_plural': 'Размеры навыков', - }, - ), - migrations.CreateModel( - name='Skills', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=255, verbose_name='Наименование')), - ('color', models.CharField(max_length=255, verbose_name='Цвет')), - ('icon', models.ImageField(help_text='65x65', null=True, upload_to='skills', verbose_name='Большая картинка')), - ('description', models.TextField(blank=True, verbose_name='Описание')), - ], - options={ - 'verbose_name': 'Навык', - 'verbose_name_plural': 'Навыки', - }, - ), migrations.CreateModel( name='Task', fields=[ @@ -171,53 +77,4 @@ class Migration(migrations.Migration): ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course')), ], ), - migrations.AddField( - model_name='skillj', - name='lesson', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Vertex', verbose_name='Урок'), - ), - migrations.AddField( - model_name='skillj', - name='skill', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Skills', verbose_name='Навык'), - ), - migrations.AddField( - model_name='pivotvertex', - name='vertex', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Vertex', verbose_name='К какому узлу'), - ), - migrations.AddField( - model_name='diploma', - name='template', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.DiplomaGen', verbose_name='Использовать шаблон'), - ), - migrations.AddField( - model_name='diploma', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AddField( - model_name='course', - name='route', - field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.CourseRoute', verbose_name='Порядок прохождения по умолчанию'), - ), - migrations.AddField( - model_name='course', - name='teachers', - field=models.ManyToManyField(related_name='course_teachers', to=settings.AUTH_USER_MODEL, verbose_name='Преподаватели'), - ), - migrations.AddField( - model_name='achievements', - name='course', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course'), - ), - migrations.AddField( - model_name='achievements', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), - ), - migrations.AlterUniqueTogether( - name='pivotvertex', - unique_together=set([('map_course', 'vertex')]), - ), ] diff --git a/courses/models.py b/courses/models.py index b8245eb..37a0a70 100755 --- a/courses/models.py +++ b/courses/models.py @@ -8,6 +8,7 @@ from django.core.exceptions import ObjectDoesNotExist import json import unidecode from django.template.defaultfilters import slugify +from maps.models import CourseRoute, CourseMap from lms.tools import decode_base64, get_real_name from lms.global_decorators import transaction_decorator @@ -80,6 +81,7 @@ class Course(models.Model): level = models.CharField(verbose_name='Уровень', choices=COURSE_LEVEL, default='B', max_length=3) slug = models.SlugField(max_length=255, blank=True, default='', unique=True, editable=False) direction = models.SmallIntegerField(choices=COURSE_DIRECTION, verbose_name='Направление', null=True) + sort = models.SmallIntegerField(null=True, verbose_name="Порядок сортировки") mentors = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Кураторы', blank=True, related_name='course_mentors') public = models.BooleanField(verbose_name='Опубликовать', default=False) @@ -91,7 +93,7 @@ class Course(models.Model): help_text='Большая картинка для мобильной версии', max_length=255) teachers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Преподаватели', related_name='course_teachers') - route = models.OneToOneField(to="CourseRoute", verbose_name="Порядок прохождения по умолчанию", blank=True, null=True) + route = models.OneToOneField(to=CourseRoute, verbose_name="Порядок прохождения по умолчанию", blank=True, null=True) def __str__(self): return self.title @@ -99,6 +101,13 @@ class Course(models.Model): def get_teacher(self): return random.choice(self.teachers.all()) + def get_map(self, user): + route = self.route + if user.is_authenticated: + route = user.progress_set.get(course=self).get_template() + map_list = route.get_sorted_maps() + return self.route.maps.all()[0] + def get_tree(self, serializer): """ Способ отображения дочерних элементов. @@ -171,70 +180,8 @@ class Course(models.Model): objects = CourseManager() class Meta: - verbose_name = u"Курс" - verbose_name_plural = u"Курсы" - - -class Skills(models.Model): - title = models.CharField(verbose_name='Наименование', max_length=255) - color = models.CharField(verbose_name='Цвет', max_length=255) - icon = models.ImageField(verbose_name='Большая картинка', upload_to='skills', null=True, help_text='65x65') - description = models.TextField(verbose_name='Описание', blank=True) - - def __str__(self): return '%s' % self.title - - class Meta: - verbose_name = 'Навык' - verbose_name_plural = 'Навыки' - - -class SkillJ(models.Model): - skill = models.ForeignKey(to="Skills", verbose_name='Навык') - lesson = models.ForeignKey(to="Vertex", verbose_name='Урок') - - def __str__(self): return '%s' % self.skill - - class Meta: - verbose_name = 'Размер навыка' - verbose_name_plural = 'Размеры навыков' - - -class Achievements(models.Model): - course = models.ForeignKey(to="Course") - icon = models.ImageField(verbose_name='Отображение достижения', upload_to='diplomas', blank=True, null=True) - user = models.ForeignKey(to=settings.AUTH_USER_MODEL) - - def __str__(self): - return 'Студенту %s за курс %s' % (self.user.username, self.course.title) - - class Meta: - verbose_name = 'Достижение' - verbose_name_plural = 'Достижения' - - -class DiplomaGen(models.Model): - course = models.ForeignKey(to=Course) - template = models.URLField(verbose_name="Путь до шаблона") - - def __str__(self): - return 'Шаблон можно найти по адресу: %s, диплом выдаётся за курс %s' % (self.template, self.course.title) - - class Meta: - verbose_name = 'Генератор дипломов' - verbose_name_plural = 'Генераторы дипловов' - - -class Diploma(models.Model): - icon = models.ImageField(verbose_name='Иконка', upload_to='diplomas') - template = models.ForeignKey(to=DiplomaGen, verbose_name='Использовать шаблон', blank=True, null=True) - user = models.ForeignKey(to=settings.AUTH_USER_MODEL) - - def __str__(self): - return 'Студенту %s за курс %s' % (self.user.username, self.template.course.title) - - class Meta: - verbose_name = 'Диплом' - verbose_name_plural = 'Дипломы' + verbose_name = "Курс" + verbose_name_plural = "Курсы" class VertexManager(models.Manager): @@ -397,69 +344,3 @@ class Topic(models.Model): Возможно поле icon перекачует в Vertex, а данная модель отвалится за ненадобностью """ icon = models.ImageField(verbose_name='Иконка темы', null=True, blank=True) - - -class CourseRoute(models.Model): - """ - Объединение нескольких мап курса, одназначно - определяет способ прохождения по курсу. - """ - name = models.CharField(max_length=255, verbose_name='Имя шаблона', blank=True, null=True, unique=True) - maps = models.ManyToManyField(to="CourseMap", verbose_name="Карта линейного прохождения курсов") - is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон') - - def is_finish(self, user): - return bool(sum([int(i.is_finish(user)) for i in self.maps.all()])) - - def get_active_objects(self, user): - return [i.getactive_object(user) for i in self.maps.all()] - - class Meta: - verbose_name = 'Маршрут прохождения' - verbose_name_plural = 'Маршруты прохождения' - - -class CourseMap(models.Model): - """ - Способы отображения курса. Упорядочены в порядке возрастания приоретета. - """ - course = models.ForeignKey(to=Course, verbose_name='К какому курсу привязан') - - @transaction_decorator - def add_vertex(self, vertex, sort): - if sort > self.pivotvertex_set.count()+1: - raise ValueError("list index out of range") - for i in self.pivotvertex_set.filter(sort__gte=sort): - i.sort += 1 - i.save() - - pivot = PivotVertex.objects.create(vertex=vertex, sort=sort, map_course=self) - pivot.save() - return pivot - - def get_difference(self, user) -> list: - return list(set( - [i.vertex for i in self.pivotvertex_set.all()]).difference(set(user.progress.progress_list.all()) - )) - - def is_finish(self, user) -> bool: - return self.get_difference(user) == [] - - def get_active_object(self, user): - return self.pivotvertex_set.exclude(vertex__in=self.get_difference(user))[0] - - class Meta: - verbose_name = 'Карта линейного прохождения курсов' - verbose_name_plural = 'Карты линейного прохождения курсов' - - -class PivotVertex(models.Model): - vertex = models.ForeignKey(to=Vertex, verbose_name="К какому узлу") - sort = models.SmallIntegerField(verbose_name='Порядок сортировки', unique=True) - map_course = models.ForeignKey(to=CourseMap, verbose_name='К какой сортеровке имеетотношение', blank=True, null=True) - - class Meta: - verbose_name = 'Порядок сортировки узла' - verbose_name_plural = 'Порядки сортировок узла' - unique_together = (('map_course', 'vertex'),) - ordering = ('sort', ) diff --git a/finance/migrations/0001_initial.py b/finance/migrations/0001_initial.py index 7a8b599..31e4cc6 100644 --- a/finance/migrations/0001_initial.py +++ b/finance/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-11-28 11:50 +# Generated by Django 1.11.6 on 2017-11-28 15:18 from __future__ import unicode_literals from django.conf import settings @@ -12,9 +12,9 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('yandex_money', '0001_initial'), + ('yandex_money', '0002_auto_20171128_1150'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('courses', '0001_initial'), + ('courses', '0002_auto_20171128_1518'), ] operations = [ diff --git a/journals/migrations/0001_initial.py b/journals/migrations/0001_initial.py index 244369f..8ecc534 100644 --- a/journals/migrations/0001_initial.py +++ b/journals/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-11-28 11:50 +# Generated by Django 1.11.6 on 2017-11-28 15:18 from __future__ import unicode_literals from django.conf import settings @@ -12,10 +12,10 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0008_alter_user_username_max_length'), - ('contenttypes', '0002_remove_content_type_name'), ('storage', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('auth', '0008_alter_user_username_max_length'), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ diff --git a/journals/models.py b/journals/models.py index 0d85d6d..46868bb 100755 --- a/journals/models.py +++ b/journals/models.py @@ -8,7 +8,7 @@ from django.contrib.contenttypes.models import ContentType from django.db import models from django.db import connection -from courses.models import Achievements, Course, CourseMap, Diploma +from courses.models import Course from finance.models import Bill from storage.models import Storage diff --git a/library/migrations/0001_initial.py b/library/migrations/0001_initial.py index 3b2155a..e027331 100644 --- a/library/migrations/0001_initial.py +++ b/library/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-11-28 11:50 +# Generated by Django 1.11.6 on 2017-11-28 15:18 from __future__ import unicode_literals import datetime diff --git a/lms/settings.py b/lms/settings.py index 400e94c..b7986ed 100644 --- a/lms/settings.py +++ b/lms/settings.py @@ -111,6 +111,8 @@ apps = ( 'finance', 'journals', 'library', + 'achievements', + 'maps', 'config_app', ) diff --git a/maps/__init__.py b/maps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/maps/admin.py b/maps/admin.py new file mode 100644 index 0000000..7532d97 --- /dev/null +++ b/maps/admin.py @@ -0,0 +1,6 @@ +from django.contrib import admin + +from maps.models import CourseMap, CourseRoute + +admin.site.register(CourseMap) +admin.site.register(CourseRoute) diff --git a/maps/apps.py b/maps/apps.py new file mode 100644 index 0000000..3c5fbef --- /dev/null +++ b/maps/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class MapsConfig(AppConfig): + name = 'maps' diff --git a/maps/migrations/0001_initial.py b/maps/migrations/0001_initial.py new file mode 100644 index 0000000..aaa8138 --- /dev/null +++ b/maps/migrations/0001_initial.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2017-11-28 15:18 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('courses', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='CourseMap', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='К какому курсу привязан')), + ], + options={ + 'verbose_name': 'Карта линейного прохождения курсов', + 'verbose_name_plural': 'Карты линейного прохождения курсов', + }, + ), + migrations.CreateModel( + name='CourseRoute', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(blank=True, max_length=255, null=True, unique=True, verbose_name='Имя шаблона')), + ('is_template', models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')), + ], + options={ + 'verbose_name': 'Маршрут прохождения', + 'verbose_name_plural': 'Маршруты прохождения', + }, + ), + migrations.CreateModel( + name='PivotCourseMap', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort', models.SmallIntegerField(unique=True, verbose_name='Порядок сортировки')), + ('map_course', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maps.CourseMap', verbose_name='К какой сортеровке имеетотношение')), + ('route', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='maps.CourseRoute', verbose_name='К какому узлу')), + ], + options={ + 'verbose_name': 'Порядок сортировки узла', + 'verbose_name_plural': 'Порядки сортировок узла', + 'ordering': ('sort',), + }, + ), + migrations.CreateModel( + name='PivotVertex', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort', models.SmallIntegerField(unique=True, verbose_name='Порядок сортировки')), + ('map_course', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maps.CourseMap', verbose_name='К какой сортеровке имеетотношение')), + ('vertex', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Vertex', verbose_name='К какому узлу')), + ], + options={ + 'verbose_name': 'Порядок сортировки узла', + 'verbose_name_plural': 'Порядки сортировок узла', + 'ordering': ('sort',), + }, + ), + migrations.AlterUniqueTogether( + name='pivotvertex', + unique_together=set([('map_course', 'vertex')]), + ), + migrations.AlterUniqueTogether( + name='pivotcoursemap', + unique_together=set([('map_course', 'route')]), + ), + ] diff --git a/maps/migrations/__init__.py b/maps/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/maps/models.py b/maps/models.py new file mode 100644 index 0000000..3d13b51 --- /dev/null +++ b/maps/models.py @@ -0,0 +1,86 @@ +from django.db import models + +from lms.global_decorators import transaction_decorator + + +class CourseRoute(models.Model): + """ + Объединение нескольких мап курса, одназначно + определяет способ прохождения по курсу. + """ + name = models.CharField(max_length=255, verbose_name='Имя шаблона', blank=True, null=True, unique=True) + is_template = models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон') + + def is_finish(self, user): + return bool(sum([int(i.map_course.is_finish(user)) for i in self.pivotcoursemap_set.all()])) + + def get_active_objects(self, user): + return (i.map_course.getactive_object(user) for i in self.pivotcoursemap_set.all()) + + def get_view(self): + return (i.map_course for i in self.pivotcoursemap_set.all()) + + class Meta: + verbose_name = 'Маршрут прохождения' + verbose_name_plural = 'Маршруты прохождения' + + +class CourseMap(models.Model): + """ + Способы отображения курса. Упорядочены в порядке возрастания приоретета. + """ + course = models.ForeignKey(to='courses.Course', verbose_name='К какому курсу привязан') + + @transaction_decorator + def add_vertex(self, vertex, sort): + if sort > self.pivotvertex_set.count()+1: + raise ValueError("list index out of range") + for i in self.pivotvertex_set.filter(sort__gte=sort): + i.sort += 1 + i.save() + + pivot = PivotVertex.objects.create(vertex=vertex, sort=sort, map_course=self) + pivot.save() + return pivot + + def merge(self, another): + return # TODO: Доделать!!! + + def get_difference(self, user) -> list: + return list(set( + [i.vertex for i in self.pivotvertex_set.all()]).difference(set(user.progress.progress_list.all()) + )) + + def is_finish(self, user) -> bool: + return self.get_difference(user) == [] + + def get_active_object(self, user): + return self.pivotvertex_set.exclude(vertex__in=self.get_difference(user))[0] + + class Meta: + verbose_name = 'Карта линейного прохождения курсов' + verbose_name_plural = 'Карты линейного прохождения курсов' + + +class PivotCourseMap(models.Model): + route = models.ForeignKey(to=CourseRoute, verbose_name="К какому узлу") + sort = models.SmallIntegerField(verbose_name='Порядок сортировки', unique=True) + map_course = models.ForeignKey(to=CourseMap, verbose_name='К какой сортеровке имеетотношение', blank=True, null=True) + + class Meta: + verbose_name = 'Порядок сортировки узла' + verbose_name_plural = 'Порядки сортировок узла' + unique_together = (('map_course', 'route'),) + ordering = ('sort', ) + + +class PivotVertex(models.Model): + vertex = models.ForeignKey(to='courses.Vertex', verbose_name="К какому узлу") + sort = models.SmallIntegerField(verbose_name='Порядок сортировки', unique=True) + map_course = models.ForeignKey(to=CourseMap, verbose_name='К какой сортеровке имеетотношение', blank=True, null=True) + + class Meta: + verbose_name = 'Порядок сортировки узла' + verbose_name_plural = 'Порядки сортировок узла' + unique_together = (('map_course', 'vertex'),) + ordering = ('sort', ) diff --git a/maps/tests.py b/maps/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/maps/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/maps/views.py b/maps/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/maps/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/storage/migrations/0001_initial.py b/storage/migrations/0001_initial.py index 531d58e..79f7ee1 100644 --- a/storage/migrations/0001_initial.py +++ b/storage/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.6 on 2017-11-28 11:50 +# Generated by Django 1.11.6 on 2017-11-28 15:18 from __future__ import unicode_literals from django.db import migrations, models