feature/fix_generate_pass
Andrey 8 years ago
parent 693895a90a
commit e97c343995
  1. 38
      access/migrations/0001_initial.py
  2. 61
      access/migrations/0002_auto_20171128_1518.py
  3. 27
      access/migrations/0003_auto_20171129_1639.py
  4. 46
      access/migrations/0004_auto_20171211_1418.py
  5. 20
      access/models/other.py
  6. 9
      access/models/user.py
  7. 2
      access/views.py
  8. 7
      achievements/migrations/0001_initial.py
  9. 56
      achievements/migrations/0002_auto_20171128_1518.py
  10. 24
      achievements/migrations/0003_auto_20171205_1821.py
  11. 26
      achievements/migrations/0004_auto_20171205_1823.py
  12. 25
      achievements/migrations/0005_auto_20171205_1833.py
  13. 20
      achievements/migrations/0006_auto_20171205_1836.py
  14. 20
      achievements/migrations/0007_achievements_text.py
  15. 1
      api_v1/urls.py
  16. 7
      courses/migrations/0001_initial.py
  17. 31
      courses/migrations/0002_auto_20171128_1518.py
  18. 19
      courses/migrations/0003_remove_course_mentors.py
  19. 3
      courses/serializers.py
  20. 21
      courses/views.py
  21. 6
      finance/migrations/0001_initial.py
  22. 1
      journals/__init__.py
  23. 5
      journals/admin.py
  24. 7
      journals/apps.py
  25. 75
      journals/default_threads.py
  26. 60
      journals/migrations/0001_initial.py
  27. 0
      journals/migrations/__init__.py
  28. 100
      journals/models.py
  29. 68
      journals/serilizers.py
  30. 9
      journals/urls.py
  31. 131
      journals/views.py
  32. 2
      library/migrations/0001_initial.py
  33. 1
      lms/settings.py
  34. 17
      maps/migrations/0001_initial.py
  35. 24
      maps/migrations/0002_auto_20171129_1439.py
  36. 25
      maps/migrations/0003_auto_20171129_1441.py
  37. 19
      maps/migrations/0004_auto_20171129_1447.py
  38. 20
      maps/migrations/0005_coursemap_name.py
  39. 20
      maps/migrations/0006_auto_20171206_1258.py
  40. 14
      storage/migrations/0001_initial.py
  41. 5
      storage/models.py

@ -1,11 +1,10 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-28 15:18
# Generated by Django 1.11.6 on 2017-12-12 16:07
from __future__ import unicode_literals
import access.models
from django.db import migrations, models
import access.models.user
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
@ -54,6 +53,25 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Дополнительная информация о пользователе',
},
),
migrations.CreateModel(
name='Invite',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('hash', models.CharField(max_length=15, unique=True, verbose_name='Уникальный код')),
('date', models.DateTimeField(blank=True, null=True, verbose_name='Дата сгорания приглошения')),
],
options={
'verbose_name': 'Приглошение в систему',
'verbose_name_plural': 'Приглошения в систему',
},
),
migrations.CreateModel(
name='PivotProgressVertex',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.SmallIntegerField(choices=[(0, 'Выполненно'), (1, 'Ожидание'), (2, 'Не выполненно')], default=0)),
],
),
migrations.CreateModel(
name='Progress',
fields=[
@ -64,4 +82,16 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Прогресс пользователя',
},
),
migrations.CreateModel(
name='ResetPassword',
fields=[
('invite_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.Invite')),
('password', models.CharField(max_length=63, verbose_name='Новый пароль, если есть')),
],
options={
'verbose_name': 'Запрос на сброс пароля',
'verbose_name_plural': 'Запросы на сброс пароля',
},
bases=('access.invite',),
),
]

@ -1,61 +0,0 @@
# -*- 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 = [
('access', '0001_initial'),
('auth', '0008_alter_user_username_max_length'),
('maps', '0001_initial'),
('courses', '0002_auto_20171128_1518'),
]
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='maps.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='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')]),
),
]

@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-29 16:39
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):
dependencies = [
('access', '0002_auto_20171128_1518'),
]
operations = [
migrations.AlterField(
model_name='progress',
name='course',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='Курс'),
),
migrations.AlterField(
model_name='progress',
name='user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Студент'),
),
]

@ -1,46 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-11 14: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):
dependencies = [
('access', '0003_auto_20171129_1639'),
]
operations = [
migrations.CreateModel(
name='Invite',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('hash', models.CharField(max_length=15, unique=True, verbose_name='Уникальный код')),
('date', models.DateTimeField(blank=True, null=True, verbose_name='Дата сгорания приглошения')),
],
options={
'verbose_name': 'Приглошение в систему',
'verbose_name_plural': 'Приглошения в систему',
},
),
migrations.CreateModel(
name='ResetPassword',
fields=[
('invite_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='access.Invite')),
('password', models.CharField(max_length=63, verbose_name='Новый пароль, если есть')),
],
options={
'verbose_name': 'Запрос на сброс пароля',
'verbose_name_plural': 'Запросы на сброс пароля',
},
bases=('access.invite',),
),
migrations.AddField(
model_name='invite',
name='owner',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Кому приглошение'),
),
]

@ -2,11 +2,12 @@ from django.conf import settings
from django.db import models
from courses.models import Course, Vertex
from journals.models import Thread
from lms.global_decorators import transaction_decorator
from maps.models import CourseRoute
from django.db.models import Q
from storage.models import Comment
class Invite(models.Model):
owner = models.OneToOneField(to=settings.AUTH_USER_MODEL, verbose_name="Кому приглошение", null=True, unique=True)
@ -48,9 +49,10 @@ class Account(models.Model):
class Progress(models.Model):
teacher = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name="Преподователь по умолчанию",
related_name='teacher_progress')
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name='Студент', null=True)
course = models.ForeignKey(to=Course, verbose_name='Курс', null=True)
progress_list = models.ManyToManyField(to=Vertex, verbose_name='Лист пройденных объектов', blank=True)
template = models.OneToOneField(to=CourseRoute, blank=True, null=True, verbose_name='Шаблон для прохождения если '
'не указан явно смотри '
'функцию get_template()')
@ -86,4 +88,16 @@ class Progress(models.Model):
class Meta:
verbose_name = 'Прогресс пользователя'
verbose_name_plural = 'Прогресс пользователя'
unique_together = (("user", "course"),)
unique_together = (("user", "course"),)
class PivotProgressVertex(models.Model):
VERTEX_STATUS = (
(0, 'Выполненно'),
(1, 'Ожидание'),
(2, 'Не выполненно'),
)
progress = models.ForeignKey(to=Progress)
vertex = models.ForeignKey(to=Vertex)
status = models.SmallIntegerField(choices=VERTEX_STATUS, default=0)
comment = models.ManyToManyField(to=Comment)

@ -9,8 +9,6 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from access.models.other import Invite, Account
from courses.models import Vertex
from journals.models import Thread
from lms.global_decorators import transaction_decorator
@ -58,14 +56,7 @@ class CustomUserManager(BaseUserManager):
user.save(using=self._db)
thread = Thread.objects.create(
key="""user_%s""" % user.id,
text="""Приватный тред пользователя %s""" % user.email,
is_recurse=True,
)
Account.objects.create(owner=user)
thread.subscribers.add(user)
if role_list:
for group in role_list:

@ -186,7 +186,7 @@ class ChangePasswordView(APIView):
@staticmethod
def post(request):
if request.user.is_authenticated() and not request.user.check_password(request.JSON['old_password']):
return Response("Неверный пароль", status=404)
return Response("Неверный пароль", status=400)
request.user.set_password(request.JSON['new_password'])
request.user.save()
return Response("Пароль был изменён", status=200)

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-28 15:18
# Generated by Django 1.11.6 on 2017-12-12 16:07
from __future__ import unicode_literals
from django.db import migrations, models
@ -17,7 +17,8 @@ class Migration(migrations.Migration):
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='Отображение достижения')),
('img', models.ImageField(blank=True, null=True, upload_to='achives', verbose_name='Отображение достижения')),
('text', models.CharField(default='', max_length=255, verbose_name='Текст достижения')),
],
options={
'verbose_name': 'Достижение',
@ -28,7 +29,7 @@ class Migration(migrations.Migration):
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='Иконка')),
('img', models.ImageField(upload_to='diplomas', verbose_name='Иконка')),
],
options={
'verbose_name': 'Диплом',

@ -1,56 +0,0 @@
# -*- 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),
),
]

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-05 18:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('achievements', '0002_auto_20171128_1518'),
]
operations = [
migrations.RemoveField(
model_name='achievements',
name='course',
),
migrations.AlterField(
model_name='achievements',
name='icon',
field=models.ImageField(blank=True, null=True, upload_to='achives', verbose_name='Отображение достижения'),
),
]

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-05 18:23
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('achievements', '0003_auto_20171205_1821'),
]
operations = [
migrations.RemoveField(
model_name='achievements',
name='user',
),
migrations.AddField(
model_name='achievements',
name='user',
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL),
),
]

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-05 18:33
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('achievements', '0004_auto_20171205_1823'),
]
operations = [
migrations.RenameField(
model_name='achievements',
old_name='icon',
new_name='img',
),
migrations.RenameField(
model_name='diploma',
old_name='icon',
new_name='img',
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-05 18:36
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('achievements', '0005_auto_20171205_1833'),
]
operations = [
migrations.RenameField(
model_name='achievements',
old_name='user',
new_name='users',
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-06 12:44
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('achievements', '0006_auto_20171205_1836'),
]
operations = [
migrations.AddField(
model_name='achievements',
name='text',
field=models.CharField(default='', max_length=255, verbose_name='Текст достижения'),
),
]

@ -3,7 +3,6 @@ from django.conf.urls import url, include
urlpatterns = [
url(r'courses/', include('courses.urls')),
url(r'users/', include('access.urls')),
url(r'journals/', include('journals.urls')),
url(r'library/', include('library.urls')),
url(r'finance/', include('finance.urls')),
]

@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-28 15:18
# Generated by Django 1.11.6 on 2017-12-12 16:07
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
@ -12,9 +11,8 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('storage', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('contenttypes', '0002_remove_content_type_name'),
('storage', '0001_initial'),
]
operations = [
@ -33,7 +31,6 @@ class Migration(migrations.Migration):
('image', models.URLField(blank=True, max_length=255, verbose_name='Изображение')),
('big_image', models.URLField(blank=True, max_length=255, verbose_name='Большое изображение')),
('big_mobile_image', models.URLField(blank=True, help_text='Большая картинка для мобильной версии', max_length=255, null=True, verbose_name='Под мобилку')),
('mentors', models.ManyToManyField(blank=True, related_name='course_mentors', to=settings.AUTH_USER_MODEL, verbose_name='Кураторы')),
],
options={
'verbose_name': 'Курс',

@ -1,31 +0,0 @@
# -*- 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 = [
('courses', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('maps', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='course',
name='route',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='maps.CourseRoute', verbose_name='Порядок прохождения по умолчанию'),
),
migrations.AddField(
model_name='course',
name='teachers',
field=models.ManyToManyField(related_name='course_teachers', to=settings.AUTH_USER_MODEL, verbose_name='Преподаватели'),
),
]

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-05 11:23
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('courses', '0002_auto_20171128_1518'),
]
operations = [
migrations.RemoveField(
model_name='course',
name='mentors',
),
]

@ -1,7 +1,6 @@
from rest_framework import serializers
from courses.models import Course, Vertex, Tutorial, Topic, Task
from maps.serializers import CourseRouteSerializer
class TutorialSerializer(serializers.ModelSerializer):
@ -75,7 +74,7 @@ class CourseListSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = ['id', 'title', 'statistic', 'public',
fields = ['id', 'title', 'statistic', 'public', 'hidden',
'level', 'direction', 'image', 'slug']
@staticmethod

@ -6,7 +6,6 @@ from access.models import Progress
from maps.serializers import CourseMapSerializer
from courses.models import Course, Vertex
from courses.serializers import CourseDetailSerializer, CourseListSerializer, VertexSerializer, MiniVertexSerializer
from journals.models import Thread
class TreeView(APIView):
@ -84,15 +83,15 @@ class VertexDetail(APIView):
res = VertexSerializer(vertex).data
progress = vertex.course.progress_set.filter(user=request.user)
try:
if progress.exists():
next_vertex = vertex.get_next(progress[0].get_template())
if next_vertex:
res['next'] = MiniVertexSerializer(next_vertex).data
res['is_in_progress'] = vertex in progress[0].get_objects_in_progress()
else:
res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data
except Thread.DoesNotExist or Vertex.DoesNotExist:
res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data
# try:
# if progress.exists():
# next_vertex = vertex.get_next(progress[0].get_template())
# if next_vertex:
# res['next'] = MiniVertexSerializer(next_vertex).data
# res['is_in_progress'] = vertex in progress[0].get_objects_in_progress()
# else:
# res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data
# except Thread.DoesNotExist or Vertex.DoesNotExist:
# res['next'] = MiniVertexSerializer(vertex.get_next(vertex.course.route)).data
return Response(res, status=200) if status == 200 else Response(status=204)

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-28 15:18
# Generated by Django 1.11.6 on 2017-12-12 16:07
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', '0002_auto_20171128_1518'),
('courses', '0002_auto_20171212_1607'),
]
operations = [

@ -1 +0,0 @@
default_app_config = "journals.apps.JournalsAppConfig"

@ -1,5 +0,0 @@
from django.contrib import admin
from journals.models import Thread, Journal
admin.site.register(Thread)
admin.site.register(Journal)

@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class JournalsAppConfig(AppConfig):
name = "journals"
verbose_name = "Журналы"

@ -1,75 +0,0 @@
import os, sys, django
from django.contrib.auth import get_user_model
sys.path.append("../")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lms.settings")
django.setup()
from journals.models import Thread
from django.contrib.auth.models import Group
from lms.global_decorators import transaction_decorator
@transaction_decorator
def main_threads():
Thread.objects.all().delete()
admin_thread = Thread.objects.create(
key='Admin',
text='Тред для админов, сюда падают все журналируемые сообщения в системе, кроме личных переписок',
is_staff=True,
x=500,
y=75,
)
admin_thread.groups.add(Group.objects.get(name='admin'))
management_thread = Thread.objects.create(
key='Project_management',
text='Тред для проджект-менеджеров, сюда падает статистика разного рода',
is_staff=True,
)
management_thread.groups.add(Group.objects.get(name='project_managers'))
management_thread.parent.add(admin_thread)
support_thread = Thread.objects.create(
key='Support',
text='Тред сапортов, занимаются поддержкой клиента',
is_staff=True,
y=500,
)
support_thread.groups.add(Group.objects.get(name='supports'))
support_thread.parent.add(management_thread)
lead_managers = Thread.objects.create(
key='Sale_lead',
text='Тред лидов, сейлзов',
is_staff=True,
x=700,
)
lead_managers.groups.add(Group.objects.get(name='lead_managers'))
lead_managers.parent.add(admin_thread)
managers = Thread.objects.create(
key='Sale',
text='Тред сейлзов',
is_staff=True,
is_recurse=True,
x=700,
y=500,
)
managers.groups.add(Group.objects.get(name='managers'))
managers.parent.add(lead_managers)
for user in get_user_model().objects.all():
Thread.objects.get_or_create(key="""user_%s""" % user.id)
if __name__ == '__main__':
main_threads()

@ -1,60 +0,0 @@
# -*- 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 = [
('storage', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('auth', '0008_alter_user_username_max_length'),
('contenttypes', '0002_remove_content_type_name'),
]
operations = [
migrations.CreateModel(
name='Journal',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extra_data', models.TextField(blank=True, null=True)),
('object_id', models.PositiveIntegerField()),
('action_type', models.CharField(choices=[('try', 'попытался сдать'), ('yes', 'одобрил'), ('no', 'отклонил'), ('favorite', 'добавил в избранное'), ('watch', 'просмотрел'), ('like', 'лайкнул'), ('dislike', 'дизлайкнул'), ('comment', 'оставил комментарий'), ('start', 'начал прохождение'), ('end', 'закончил прохождение'), ('create', 'создал'), ('update', 'обновил'), ('delete', 'удалил')], max_length=31)),
('date', models.DateTimeField(auto_now_add=True)),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('files', models.ManyToManyField(blank=True, to='storage.Storage')),
],
),
migrations.CreateModel(
name='Thread',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('key', models.CharField(editable=False, max_length=200, unique=True)),
('text', models.TextField(default='', verbose_name='Описание треда')),
('is_staff', models.BooleanField(default=False, verbose_name='Админская ли табличка')),
('is_recurse', models.BooleanField(default=False, verbose_name='Поле аптимизации поиска')),
('check_subscribe', models.BooleanField(default=True, verbose_name='Проверять ли подписки')),
('x', models.SmallIntegerField(default=300)),
('y', models.SmallIntegerField(default=300)),
('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='Группы подписчиков')),
('parent', models.ManyToManyField(blank=True, to='journals.Thread')),
('subscribers', models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL, verbose_name='Подписчики')),
],
),
migrations.AddField(
model_name='journal',
name='thread',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='journals.Thread', verbose_name='Тред'),
),
migrations.AddField(
model_name='journal',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Инициатор действия или тот, на ком действие инициируется'),
),
]

@ -1,100 +0,0 @@
# coding=utf-8
from __future__ import unicode_literals
from django.conf import settings
from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db import connection
from courses.models import Course
from finance.models import Bill
from storage.models import Storage
ACTION_CHOICES = (
('try', 'попытался сдать'),
('yes', 'одобрил'),
('no', 'отклонил'),
('favorite', 'добавил в избранное'),
('watch', 'просмотрел'),
('like', 'лайкнул'),
('dislike', 'дизлайкнул'),
('comment', 'оставил комментарий'),
('start', 'начал прохождение'),
('end', 'закончил прохождение'),
('create', 'создал'),
('update', 'обновил'),
('delete', 'удалил'),
)
class Journal(models.Model):
thread = models.ForeignKey(to='Thread', verbose_name='Тред')
user = models.ForeignKey(
to=settings.AUTH_USER_MODEL, verbose_name='Инициатор действия или тот, на ком действие инициируется'
)
content_type = models.ForeignKey(to=ContentType)
extra_data = models.TextField(null=True, blank=True)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
action_type = models.CharField(max_length=31, choices=ACTION_CHOICES)
date = models.DateTimeField(auto_now_add=True)
files = models.ManyToManyField(to=Storage, blank=True)
def __str__(self):
return '%d Пользователь %s %s %s' % (self.id, self.user.email, self.get_action_type_display(), self.thread.key)
class Thread(models.Model):
key = models.CharField(max_length=200, unique=True, editable=False)
text = models.TextField(default='', verbose_name='Описание треда')
is_staff = models.BooleanField(default=False, verbose_name='Админская ли табличка')
is_recurse = models.BooleanField(default=False, verbose_name='Поле аптимизации поиска')
subscribers = models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Подписчики', blank=True)
groups = models.ManyToManyField(to=Group, verbose_name='Группы подписчиков', blank=True)
check_subscribe = models.BooleanField(default=True, verbose_name='Проверять ли подписки')
parent = models.ManyToManyField(to='self', blank=True, symmetrical=False)
x = models.SmallIntegerField(default=300)
y = models.SmallIntegerField(default=300)
def check_status(self):
# Определяет статус треда если такой есть.
# Возможно, костыль.
res = None
for i in self.journal_set.all():
if i.action_type in ['try', 'yes', 'no']:
res = i.action_type
return res
def check_perm(self, user):
res = user in self.subscribers.all()
for i in self.groups.all():
res = res or (i in user.groups.all())
return res or sum([int(i.check_perm(user)) for i in self.parent.all()])
def child_thread_count(self):
# cursor = connection.cursor()
# if self.is_recurse:
# cursor.execute("""
# WITH RECURSIVE temp1 (to_thread_id, from_thread_id) AS (
# SELECT T1.to_thread_id, T1.from_thread_id
# FROM journals_thread_parent T1
# WHERE to_thread_id = %s
# UNION
# SELECT T2.to_thread_id, T2.from_thread_id
# FROM journals_thread_parent T2
# INNER JOIN temp1 ON(T2.from_thread_id = temp1.to_thread_id)
# )
# SELECT COUNT(*) FROM temp1
# """, [self.id])
# count = cursor.fetchone()
# return int(count[0])
return self.thread_set.count()
def journals_count(self):
return self.journal_set.count()
def __str__(self):
return self.key

@ -1,68 +0,0 @@
from rest_framework import serializers
from journals.models import Thread, Journal
from storage.serializers import StorageSerializer
class JournalSerializer(serializers.ModelSerializer):
user = serializers.SerializerMethodField()
files = serializers.SerializerMethodField()
label = serializers.SerializerMethodField()
class Meta:
model = Journal
fields = ('label', 'date', 'user', 'files', 'action_type', 'object_id', )
@staticmethod
def get_user(self):
return self.user.get_full_name()
@staticmethod
def get_label(self):
return self.extra_data if self.extra_data else """%s %s"""\
% (self.user.get_full_name(), self.get_action_type_display())
@staticmethod
def get_files(self):
return [StorageSerializer(i).data for i in self.files.all()]
class ThreadDetailSerializer(serializers.ModelSerializer):
journals = serializers.SerializerMethodField()
children = serializers.SerializerMethodField()
class Meta:
model = Thread
fields = ('journals', 'id', 'text', 'children', 'key',)
@staticmethod
def get_journals(self):
return [JournalSerializer(i).data for i in self.journal_set.all()]
@staticmethod
def get_children(self):
return [ThreadDetailSerializer(i).data for i in self.thread_set.all()]
class ThreadAdminSerializer(serializers.ModelSerializer):
count_children = serializers.SerializerMethodField()
count_journals = serializers.SerializerMethodField()
class Meta:
model = Thread
exclude = ('is_staff', )
@staticmethod
def get_count_children(self):
return self.child_thread_count()
@staticmethod
def get_count_journals(self):
return self.journals_count()
class ThreadUserSerializer(serializers.ModelSerializer):
class Meta:
model = Thread
fields = ('id', 'text')

@ -1,9 +0,0 @@
from django.conf.urls import url
from journals import views as views
urlpatterns = [
url(r'thread/$', views.ThreadAdminListView.as_view()),
url(r'pay-stat/([0-9]{1,99})/$', views.get_pay_stat),
url(r'thread/(?P<key>[-\w]+)/$', views.ThreadDetailView.as_view()),
url(r'journal/$', views.JournalCreateView.as_view()),
]

@ -1,131 +0,0 @@
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
import csv
from django.http import HttpResponse, HttpResponseForbidden
from access.models.other import Progress
from journals.models import Thread, Journal
from journals.serilizers import ThreadDetailSerializer, ThreadAdminSerializer, JournalSerializer
from lms.global_decorators import transaction_decorator
from lms.tools import decode_base64
from storage.models import Storage
class ThreadAdminListView(APIView):
renderer_classes = (JSONRenderer,)
status_code = 200
def get(self, request):
return Response(
[ThreadAdminSerializer(thread).data for thread in Thread.objects.filter(is_staff=True)],
self.status_code,
)
class ThreadDetailView(APIView):
renderer_classes = (JSONRenderer,)
permission_classes = ()
status_code = 200
def post(self, request, key):
try:
thread = Thread.objects.get(key=key)
thread.text = request.JSON.get('text', thread.text)
thread.key = request.JSON.get('key', thread.key)
thread.x = request.JSON.get('x', thread.x)
thread.y = request.JSON.get('y', thread.y)
thread.save()
return Response(ThreadAdminSerializer(thread).data, self.status_code, )
except Thread.DoesNotExist:
return Response("Thread doesn't exist.", self.status_code, )
@staticmethod
def get(request, key):
try:
thread = Thread.objects.get(key=key)
if thread.check_perm(request.user):
return Response(ThreadDetailSerializer(thread).data, status=200)
return Response("permission denied", status=403)
except Thread.DoesNotExist:
return Response("Thread not found", status=404)
def get_pay_stat(request, pk):
if not request.user.is_authenticated and (request.user.groups.filter(name="supports") or request.user.is_superuser):
return HttpResponseForbidden()
date_from = request.GET.get('from', None)
date_to = request.GET.get('to', None)
file_name = "teacher_%s" % pk
file_name = file_name + "__from_%s" % date_from if date_from else file_name
file_name = file_name + "__to_%s" % date_to if date_to else file_name
journals = Journal.objects.filter(user_id=pk, action_type="yes")
journals = journals.filter(date__lt=date_to) if date_to else journals
journals = journals.filter(date__gte=date_from) if date_from else journals
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="%s"' % file_name
writer = csv.writer(response)
writer.writerow(['student_email', 'full_name', 'course', 'theme', 'task_id', 'date'])
for i in journals.order_by('-date'):
student = get_user_model().objects.get(id=i.thread.key.split('user_')[1].split('__vertex')[0])
writer.writerow([
student.email,
student.get_full_name(),
i.content_object.course.title,
i.content_object.vertex_set.all()[0].title,
i.content_object.id,
i.date,
])
return response
class JournalCreateView(APIView):
renderer_classes = (JSONRenderer,)
permission_classes = (IsAuthenticated,)
status_code = 200
@transaction_decorator
def post(self, request):
pk = request.JSON.get('thread_id', None)
try:
thread = Thread.objects.get(id=pk)
if request.user.is_authenticated and thread.check_perm(request.user):
action_type = request.JSON.get('action_type', None)
extra_data = request.JSON.get('extra_data', None)
object_id = request.JSON.get('object_id', None)
content_type__model = request.JSON.get('content_type__model', None)
content_type__app_label = request.JSON.get('content_type__app_label', None)
content_type = ContentType.objects.get(model=content_type__model, app_label=content_type__app_label)
files = request.JSON.get('files', [])
files = [{'data': decode_base64(i['data']), 'name': i['name']} for i in files]
journal = Journal.objects.create(
action_type=action_type, extra_data=extra_data, thread=thread,
content_type=content_type, object_id=object_id,
user=request.user
)
for i in files:
s = Storage.objects.create(original=decode_base64(i['data']), name=i['name'])
journal.files.add(s)
if action_type == 'yes':
p = Progress.objects.get(course=journal.content_object.course, user=journal.thread.subscribers.all()[0])
p.add_vertex(journal.content_object)
return Response(JournalSerializer(journal).data, status=200)
return Response("permission denied", status=403)
except Thread.DoesNotExist:
return Response("Thread doesn't exist.", self.status_code,)

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-28 15:18
# Generated by Django 1.11.6 on 2017-12-12 16:07
from __future__ import unicode_literals
import datetime

@ -109,7 +109,6 @@ apps = (
'courses',
'storage',
'finance',
'journals',
'library',
'achievements',
'maps',

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-28 15:18
# Generated by Django 1.11.6 on 2017-12-12 16:07
from __future__ import unicode_literals
from django.db import migrations, models
@ -19,6 +19,7 @@ class Migration(migrations.Migration):
name='CourseMap',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(default='Линейное прохождение', max_length=255, verbose_name='Имя прохождения')),
('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Course', verbose_name='К какому курсу привязан')),
],
options={
@ -30,7 +31,7 @@ class Migration(migrations.Migration):
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='Имя шаблона')),
('name', models.CharField(blank=True, max_length=255, null=True, verbose_name='Имя шаблона')),
('is_template', models.BooleanField(default=True, verbose_name='Может ли быть использован как шаблон')),
],
options={
@ -42,13 +43,13 @@ class Migration(migrations.Migration):
name='PivotCourseMap',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort', models.SmallIntegerField(unique=True, verbose_name='Порядок сортировки')),
('sort', models.SmallIntegerField(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': 'Порядки сортировок узла',
'verbose_name': 'Порядок сортировки маршрута',
'verbose_name_plural': 'Порядки сортировок маршрутов',
'ordering': ('sort',),
},
),
@ -56,7 +57,7 @@ class Migration(migrations.Migration):
name='PivotVertex',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort', models.SmallIntegerField(unique=True, verbose_name='Порядок сортировки')),
('sort', models.SmallIntegerField(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='К какому узлу')),
],
@ -68,10 +69,10 @@ class Migration(migrations.Migration):
),
migrations.AlterUniqueTogether(
name='pivotvertex',
unique_together=set([('map_course', 'vertex')]),
unique_together=set([('map_course', 'vertex'), ('sort', 'map_course')]),
),
migrations.AlterUniqueTogether(
name='pivotcoursemap',
unique_together=set([('map_course', 'route')]),
unique_together=set([('sort', 'route'), ('map_course', 'route')]),
),
]

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-29 14:39
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('courses', '0002_auto_20171128_1518'),
('maps', '0001_initial'),
]
operations = [
migrations.AlterUniqueTogether(
name='pivotcoursemap',
unique_together=set([('map_course', 'route'), ('sort', 'route')]),
),
migrations.AlterUniqueTogether(
name='pivotvertex',
unique_together=set([('sort', 'map_course'), ('map_course', 'vertex')]),
),
]

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-29 14:41
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maps', '0002_auto_20171129_1439'),
]
operations = [
migrations.AlterField(
model_name='pivotcoursemap',
name='sort',
field=models.SmallIntegerField(verbose_name='Порядок сортировки'),
),
migrations.AlterField(
model_name='pivotvertex',
name='sort',
field=models.SmallIntegerField(verbose_name='Порядок сортировки'),
),
]

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-29 14:47
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('maps', '0003_auto_20171129_1441'),
]
operations = [
migrations.AlterModelOptions(
name='pivotcoursemap',
options={'ordering': ('sort',), 'verbose_name': 'Порядок сортировки маршрута', 'verbose_name_plural': 'Порядки сортировок маршрутов'},
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-01 20:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maps', '0004_auto_20171129_1447'),
]
operations = [
migrations.AddField(
model_name='coursemap',
name='name',
field=models.CharField(default='Линейное прохождение', max_length=255, verbose_name='Имя прохождения'),
),
]

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-12-06 12:58
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maps', '0005_coursemap_name'),
]
operations = [
migrations.AlterField(
model_name='courseroute',
name='name',
field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Имя шаблона'),
),
]

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-11-28 15:18
# Generated by Django 1.11.6 on 2017-12-12 16:07
from __future__ import unicode_literals
from django.db import migrations, models
@ -13,6 +13,13 @@ class Migration(migrations.Migration):
]
operations = [
migrations.CreateModel(
name='Comment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField(default='')),
],
),
migrations.CreateModel(
name='Storage',
fields=[
@ -25,4 +32,9 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'Файлы',
},
),
migrations.AddField(
model_name='comment',
name='files',
field=models.ManyToManyField(blank=True, to='storage.Storage'),
),
]

@ -12,3 +12,8 @@ class Storage(models.Model):
class Meta:
verbose_name = 'Файл'
verbose_name_plural = 'Файлы'
class Comment(models.Model):
text = models.TextField(default="")
files = models.ManyToManyField(to=Storage, blank=True)
Loading…
Cancel
Save