access update

feature/fix_generate_pass
Andrey 8 years ago
parent 6e71fb8da3
commit 14bf4b5f76
  1. 11
      access/models.py
  2. 1
      access/urls.py
  3. 54
      access/views.py
  4. 19
      courses/models.py
  5. 10
      courses/serializers.py
  6. 2
      courses/urls.py
  7. 35
      courses/views.py
  8. 2
      csv/access/account.csv
  9. 13907
      csv/access/progress.csv
  10. 2
      csv/access/users.csv
  11. 1
      csv/load_perm.py
  12. 34
      journals/migrations/0006_auto_20171031_1405.py
  13. 37
      journals/models.py
  14. 2
      library/models.py
  15. 1
      lms/settings.py
  16. 1
      management/__init__.py
  17. 6
      management/admin.py
  18. 7
      management/apps.py
  19. 52
      management/migrations/0001_initial.py
  20. 0
      management/migrations/__init__.py
  21. 39
      management/models.py

@ -111,8 +111,9 @@ class CustomUserManager(BaseUserManager):
"subject": 'Спасибо за регистрацию',
"message": '''
Вы были успешны зарегистрированны на портале go.skillbox.ru
ваш пароль %s
для подтверждения регистрации перейдите по ссылке
https://go.skillbox.ru/api/v1/users/registration/?hash=''' + invite.hash,
https://go.skillbox.ru/api/v1/users/registration/?hash=%s''' %(user.password, invite.hash),
"from_email": 'robo@skillbox.ru',
"recipient_list": [user.email],
}
@ -122,14 +123,14 @@ class CustomUserManager(BaseUserManager):
)
return user
def create_user(self, email, password, **extra_fields):
return self._create_user(email=email, password=password, **extra_fields)
def create_user(self, email, **extra_fields):
return self._create_user(email=email, **extra_fields)
def create_superuser(self, email, password):
return self._create_user(email=email, password=password, is_superuser=True, is_staff=True, is_active=True)
def create_student(self, email, password, **extra_fields):
return self.create_user(email=email, password=password, role_list=['students'], is_send=True, **extra_fields)
def create_student(self, email, **extra_fields):
return self.create_user(email=email, role_list=['students'], is_send=True, **extra_fields)
class User(AbstractBaseUser, PermissionsMixin):

@ -11,4 +11,5 @@ urlpatterns = [
url(r'change_password/$', views.ChangePasswordView.as_view()),
url(r'login/$', views.LoginView.as_view()),
url(r'logout/$', views.LogoutView.as_view()),
url(r'progress/$', views.UpdateProgress.as_view()),
]

@ -6,8 +6,9 @@ from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from django.db.models import Q
from access.models import Invite
from access.models import Invite, Progress, ExtraPrivilege
from access.serializers import UserInitSerializer, UserSearchSerializer
from courses.models import Vertex
class TeacherListView(APIView):
@ -108,10 +109,17 @@ class RegistrationView(APIView):
get_user_model().objects.get(email=request.JSON['email'].lower())
return Response('user already exist', status=403)
except get_user_model().DoesNotExist:
user = get_user_model().objects.create_student(
email=request.JSON['email'].lower(),
password=request.JSON['password']
)
password = request.JSON.get('password')
if password:
user = get_user_model().objects.create_student(
email=request.JSON['email'].lower(),
password=request.JSON['password']
)
else:
user = get_user_model().objects.create_student(
email=request.JSON['email'].lower(),
)
return Response(UserInitSerializer(user).data, status=200)
@ -149,3 +157,39 @@ class LogoutView(APIView):
if request.user.is_authenticated():
auth.logout(request)
return Response(status=204)
class UpdateProgress(APIView):
"""
Переводит ученика на следующую стадию прохождения курса
Запрос идёт синхронно возвращает id нового объекта прогресса
"""
renderer_classes = (JSONRenderer,)
@staticmethod
def post(request):
pk = int(request.JSON.get('id'))
try:
vertex = Vertex.objects.get(id=pk)
except Vertex.DoesNotExist:
return Response("Объект не найден", status=404)
next_vertex = vertex.get_next(['task', 'tutorial'])
try:
progress = Progress.objects.get(user=request.user, course=vertex.course)
if progress.active_obj == vertex:
if next_vertex.is_more(progress.active_obj):
progress.active_obj = next_vertex
progress.save()
return Response({'id': progress.active_obj.id, 'type': progress.active_obj.content_type.model}, status=200)
except Progress.DoesNotExist:
pass
try:
privilege = ExtraPrivilege.objects.get(user=request.user, subject=vertex)
privilege.is_done = True
privilege.save()
return Response({'id': next_vertex.id, 'type': next_vertex.content_type.model}, status=200)
except ExtraPrivilege.DoesNotExist:
return Response('У вас нет прав', status=403)

@ -3,11 +3,11 @@ from django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.core.exceptions import ObjectDoesNotExist
import json
from lms.global_decorators import transaction_decorator
from library.models import Tags
from management.models import Comment
from storage.models import Storage
@ -263,6 +263,23 @@ class Vertex(models.Model):
course_map = CourseMap.objects.get(course=self.course)
return course_map.map_to_list().index(self.id) > course_map.map_to_list().index(vertex.id)
def check_vertex(self, user) -> bool:
if self.free:
return True
if not user.is_authenticated:
return False
if self.extraprivilege_set.filter(user=user).exists():
return True
try:
progress = self.course.progress_set.get(user=user)
except ObjectDoesNotExist:
return False
return progress.is_access(self)
# Модели нового API со временем всё, что выше будет выпилено
class Tutorial(models.Model):

@ -4,10 +4,16 @@ from courses.models import Course, Vertex, Tutorial, Topic, Task
class TutorialSerializer(serializers.ModelSerializer):
materials = serializers.SerializerMethodField()
class Meta:
model = Tutorial
exclude = ['id']
@staticmethod
def get_materials(self):
return [i.original.url for i in self.materials.all()]
class TaskSerializer(serializers.ModelSerializer):
class Meta:
@ -82,7 +88,7 @@ class CourseTreeSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = ['id', 'children']
fields = ['id', 'children', 'title']
@staticmethod
def get_children(self):
@ -104,7 +110,7 @@ class CourseDetailSerializer(serializers.ModelSerializer):
@staticmethod
def get_direction(self):
return self.direction.title
return self.get_direction_display()
@staticmethod
def get_teachers(self):

@ -4,6 +4,6 @@ from courses import views as views
urlpatterns = [
url(r'detail/([0-9]{1,99})/$', views.CourseDetailView.as_view()),
url(r'vertex/([0-9]{1,99})/$', views.VertexDetail.as_view()),
url(r'tree/(?P<slug>[-\w]+)/$', views.TreeView.as_view()),
url(r'tree/(?P<slug>.+)/$', views.TreeView.as_view()),
url(r'^$', views.CourseListView.as_view()),
]

@ -18,6 +18,7 @@ class TreeView(APIView):
return Response(status=204)
def get(self, request, slug):
try:
course = Course.objects.get(slug=slug)
except Course.DoesNotExist:
@ -63,17 +64,16 @@ class CourseListView(APIView):
status_code = 200
def get(self, request):
if not request.user.is_authenticated() or (not request.user.is_staff or request.GET.get('staff') == 'true'):
if request.user.is_authenticated() and request.user.is_staff:
return Response([CourseListSerializer(i).data for i in Course.objects.all()], self.status_code)
res = []
for course in Course.objects.filter(public=True):
if course.public:
course_serialize = CourseListSerializer(course).data
course_serialize['is_mine'] = False
if request.user.is_authenticated() and Progress.objects.filter(course=course, user=request.user).exists():
course_serialize['is_mine'] = True
res.append(course_serialize)
course_serialize = CourseListSerializer(course).data
course_serialize['is_mine'] = False
if request.user.is_authenticated() and Progress.objects.filter(course=course, user=request.user).exists():
course_serialize['is_mine'] = True
res.append(course_serialize)
return Response(res, self.status_code)
@ -90,22 +90,9 @@ class VertexDetail(APIView):
except Vertex.DoesNotExist:
return Response("Vertex doesn't exist", status=404)
res_a = Response(VertexSerializer(vertex).data, status=200) if status == 200 else Response(status=204)
if vertex.free:
return res_a
if not request.user.is_authenticated:
return Response("Access to detail of vertex, exist only for authenticated users", status=403)
if ExtraPrivilege.objects.filter(user=request.user, subject=vertex).exists():
return res_a
try:
if not Progress.objects.get(course=vertex.course, user=request.user).is_access(vertex):
return Response("permission denied", status=403)
except Progress.DoesNotExist:
if not vertex.check_vertex(request.user):
return Response("permission denied", status=403)
return res_a
res = VertexSerializer(vertex).data
return Response(res, status=200) if status == 200 else Response(status=204)

@ -15665,11 +15665,11 @@ d_day,city,gender,owner,photo,phone
1985-12-17,Санкт-Петербург,0,9363,in_ava/ava_UbZDddi,+79219121509
1992-02-09,Москва,0,10667,2017_2_6_14_45_851,+79168608063
1991-07-30,минск,0,10724,2017_2_1_13_33_858,+375297977417
,Москва,0,15809,in_ava/ava_10k7npC,+7-916-192-73-11
,,0,1515,in_ava/ava_ke4HHv1,
,,0,35,in_ava/ava_KjWSNfF,
,,0,5261,in_ava/ava_l3k526i,+79788042038
,,0,63,in_ava/ava_oIVmeYH,89061185850
,Москва,0,15809,in_ava/ava_10k7npC,+7-916-192-73-11
,,0,48,in_ava/ava_Uxv3fQS,
1990-06-21,Йошкар-Ола,0,93,in_ava/ava_bqpnnXv,Игоревич
,,0,691,in_ava/ava_mXsosby,

Can't render this file because it is too large.

File diff suppressed because it is too large Load Diff

@ -15666,11 +15666,11 @@ id,email,first_name,last_name,last_login,role_list,is_superuser,date_joined,is_s
9363,4pavlov@list.ru,Никита,Павлов,2017-05-01 14:51:36.878400,['students'],False,2017-01-27 13:06:01.032972,False,True,False,pbkdf2_sha256$24000$Ebgngk1B60cL$AgAcrl2hVliVLlVA01SLgYtRmwRpL56kOhjiAvUjutU=
10667,mad-hatter613@yandex.ru,Татьяна ,Тихонова ,2017-08-02 17:23:59.817791,['students'],False,2017-03-03 11:53:39.739851,False,True,False,pbkdf2_sha256$24000$Jc4doAyshxRk$VBqyvAy5PgrXY6Cey+/OGvPIEYLrygvck426snYGPZ8=
10724,7977417@gmail.com,яна,сыревич,2017-07-27 13:55:21.776902,['students'],False,2017-03-06 13:09:37.538459,False,True,False,pbkdf2_sha256$24000$8eI8cRQw0bhE$XzePRiOYa2cHDoYN9p2TkmjmmHkOs6/607UMoOP3jyo=
15809,andrey.korolev@skillbox.ru,Андрей,Королёв,2017-10-06 11:09:50.887731,[],True,2017-08-24 18:26:40,True,True,False,pbkdf2_sha256$24000$nVAKvm1WWWXa$mj0hx2m4G6yNtcZnkbdcBDgF+r/ZfbXnbEQYScTGK5A=
1515,heiaheia25+5@gmail.com,,,2016-04-23 21:13:57.791906,['students'],False,2016-04-23 21:13:57.791913,False,False,False,pbkdf2_sha256$24000$KeysH3LMA3ze$EHrAmAfGynjMZ6zh7nul7Q2ownc2D6+W/DWy6od/tn0=
35,trubnikovvadim@gmail.com,,,2016-04-21 21:35:25.055910,['students'],False,2015-11-11 14:56:04.465000,False,False,False,pbkdf2_sha256$20000$3g1fr3aPCQXp$xV8WSPM1SGhfpvtf4eEAOwS6LvEnozZJ6K4kvC49has=
5261,formik52@gmail.com,,,2016-10-04 10:02:19.163373,['students'],False,2016-10-04 10:02:19.163378,False,False,False,!gftsRlQ3rA9ukdS6Ky7QDY9HR2M3YnIhrOvOozIz
63,eduard.s.1984@gmail.com,Эдуард,Самигуллин,2016-04-21 21:35:25.055910,['students'],False,2015-11-28 12:07:44.699000,False,True,False,pbkdf2_sha256$20000$VsDI2yXXtna2$eb43XhgxRjQ0V+Vt6AYeBuDpeKkiAOVCuWNX2GU5+Ek=
15809,andrey.korolev@skillbox.ru,Андрей,Королёв,2017-10-27 12:17:02.920485,[],True,2017-08-24 18:26:40,True,True,False,pbkdf2_sha256$24000$nVAKvm1WWWXa$mj0hx2m4G6yNtcZnkbdcBDgF+r/ZfbXnbEQYScTGK5A=
48,pro100lehsa@yandex.ru,,,2016-04-21 21:35:25.055910,['students'],False,2015-11-19 19:23:23.427000,False,False,False,pbkdf2_sha256$20000$XWrcnijH1x8N$d8zfkqiR/+UtnyKsf2VLp+Vt8RsULw9eEy64cydHGTM=
93,boltyn4ik@gmail.com,Павел,Фейгин,2016-04-21 21:35:25.055910,['students'],False,2015-12-11 11:23:35.830000,False,True,False,pbkdf2_sha256$20000$tzxhKyjW1x1E$/tuSjAaiE49PqMVHR64wiLQA2iP/CiGUrF1HPY4bH/8=
691,belocerkovecden@mail.ru,,,2016-04-21 21:35:25.055910,['students'],False,2016-03-24 15:02:20.761634,False,False,False,!UeR9h1bWgWi3Wr0RJXNfiTACMF7Xkff6T4aSNey6

Can't render this file because it is too large.

@ -9,6 +9,7 @@ from access.models import Progress
from courses.models import Vertex, Course
if __name__ == '__main__':
Progress.objects.all().delete()
with open('./access/progress.csv') as progress_csv:
progress_reader = csv.DictReader(progress_csv)
for row in progress_reader:

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-10-31 14:05
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('journals', '0005_auto_20171024_1352'),
]
operations = [
migrations.RemoveField(
model_name='thread',
name='recurse_step',
),
migrations.AddField(
model_name='journal',
name='children',
field=models.ManyToManyField(blank=True, to='journals.Journal'),
),
migrations.AddField(
model_name='thread',
name='is_recurse',
field=models.BooleanField(default=False, verbose_name='Поле аптимизации поиска'),
),
migrations.AlterField(
model_name='journal',
name='action_type',
field=models.SmallIntegerField(choices=[(0, 'try'), (1, 'yes'), (2, 'no'), (3, 'favorite'), (4, 'watch'), (5, 'like'), (6, 'dislike'), (7, 'comment'), (8, 'start'), (9, 'end'), (10, 'create'), (11, 'update'), (12, 'delete')]),
),
]

@ -8,7 +8,6 @@ from django.contrib.contenttypes.models import ContentType
from django.db import models
from courses.models import Achievements, Course, CourseMap, Diploma
from management.models import Comment
from finance.models import Bill
ACTION_CHOICES = (
@ -17,11 +16,14 @@ ACTION_CHOICES = (
(2, 'no'),
(3, 'favorite'),
(4, 'watch'),
(5, 'start'),
(6, 'end'),
(7, 'create'),
(8, 'update'),
(9, 'delete'),
(5, 'like'),
(6, 'dislike'),
(7, 'comment'),
(8, 'start'),
(9, 'end'),
(10, 'create'),
(11, 'update'),
(12, 'delete'),
)
@ -37,6 +39,7 @@ class Journal(models.Model):
content_object = GenericForeignKey('content_type', 'object_id')
action_type = models.SmallIntegerField(choices=ACTION_CHOICES)
date = models.DateTimeField(auto_now=True)
children = models.ManyToManyField(to='self', blank=True, symmetrical=False)
def __str__(self):
return '%d Пользователь %s %s %s' % (self.id, self.user.email, self.get_action_type_display(), self.thread.key)
@ -45,8 +48,8 @@ class Journal(models.Model):
class Thread(models.Model):
key = models.CharField(max_length=200)
text = models.TextField(default='', verbose_name=u'Описание треда')
is_staff = models.BooleanField(default=False, verbose_name=u'Админская ли табличка')
recurse_step = models.SmallIntegerField(default=0, verbose_name=u'Поле аптимизации поиска')
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='Проверять ли подписки')
@ -61,31 +64,29 @@ class Thread(models.Model):
def check_perm(self, user):
return (user in self.subscribers.all()) or bool(sum([int(i.check_perm(user)) for i in self.parent.all()]))
def child_thread_count(self, step=None):
step = self.recurse_step if step is None else step
def child_thread_count(self):
if step == 0:
if self.is_recurse:
return self.thread_set.count()
return sum([i.child_thread_count(step-1) for i in self.thread_set.all()])
return sum([i.child_thread_count() for i in self.thread_set.all()])
def journals_count(self, step=None):
step = self.recurse_step if step is None else step
children = list(self.get_children(step))
def journals_count(self):
children = list(self.get_children())
children.append(self)
return Journal.objects.filter(thread__in=children).count()
def get_children(self, step):
def get_children(self):
children = self.thread_set.filter(is_staff=False)
if step == 0:
if self.is_recurse:
list(children).append(self)
return children
res = [self]
for child in children:
res += child.get_children(step=step-1)
res += child.get_children()
return res

@ -2,8 +2,6 @@
from __future__ import unicode_literals
import datetime
from django.db import models
from management.models import Comment
class ArticleSection(models.Model):
name = models.CharField(verbose_name=u'Раздел', max_length=255)

@ -91,7 +91,6 @@ INSTALLED_APPS = [
'access',
'courses',
'storage',
'management',
'finance',
'journals',
'library',

@ -1 +0,0 @@
default_app_config = "management.apps.ManagementAppConfig"

@ -1,6 +0,0 @@
from django.contrib import admin
from management.models import Comment, Like
admin.site.register(Comment)
admin.site.register(Like)

@ -1,7 +0,0 @@
# -*- coding: utf-8 -*-
from django.apps import AppConfig
class ManagementAppConfig(AppConfig):
name = "management"
verbose_name = "Менеджмент"

@ -1,52 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.6 on 2017-10-18 14:37
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 = [
('contenttypes', '0002_remove_content_type_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('storage', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Comment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('closed', models.BooleanField(default=False, editable=False, verbose_name='Закрыт')),
('text', models.TextField(default='', verbose_name='Текст')),
('object_id', models.PositiveIntegerField()),
('children', models.ManyToManyField(blank=True, to='management.Comment')),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('files', models.ManyToManyField(blank=True, editable=False, to='storage.Storage', verbose_name='Прикрепленые файлы')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
],
options={
'verbose_name': 'Комментарий',
'verbose_name_plural': 'Комментарии',
},
),
migrations.CreateModel(
name='Like',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_positive', models.BooleanField(default=True)),
('object_id', models.PositiveIntegerField()),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Автор')),
],
options={
'verbose_name': 'Лайк-дизлайк',
'verbose_name_plural': 'Лайки-дизлайки',
},
),
]

@ -1,39 +0,0 @@
# coding=utf-8
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.conf import settings
from storage.models import Storage
class Comment(models.Model):
closed = models.BooleanField(verbose_name='Закрыт', default=False, editable=False)
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name=u'Автор')
text = models.TextField(verbose_name='Текст', default='')
files = models.ManyToManyField(Storage, verbose_name='Прикрепленые файлы', blank=True, editable=False)
children = models.ManyToManyField(to='self', blank=True, symmetrical=False)
content_type = models.ForeignKey(to=ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return str(self.id)
class Meta:
verbose_name = 'Комментарий'
verbose_name_plural = 'Комментарии'
class Like(models.Model):
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, verbose_name=u'Автор')
is_positive = models.BooleanField(default=True)
content_type = models.ForeignKey(to=ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return str(self.id) + ': ' + 'Лайк' if self.is_positive else 'Дизлайк'
class Meta:
verbose_name = 'Лайк-дизлайк'
verbose_name_plural = 'Лайки-дизлайки'
Loading…
Cancel
Save