Add recently visited modules to user
This commit is contained in:
parent
991efbe613
commit
e899bbe21b
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 2.2.12 on 2020-06-23 09:52
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('books', '0021_auto_20200520_0954'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='RecentModule',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('visited', models.DateTimeField(default=datetime.datetime.now)),
|
||||||
|
('module', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='books.Module')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList
|
from wagtail.admin.edit_handlers import FieldPanel, TabbedInterface, ObjectList
|
||||||
|
|
@ -58,3 +59,12 @@ class Module(StrictHierarchyPage):
|
||||||
|
|
||||||
def get_child_ids(self):
|
def get_child_ids(self):
|
||||||
return self.get_children().values_list('id', flat=True)
|
return self.get_children().values_list('id', flat=True)
|
||||||
|
|
||||||
|
|
||||||
|
class RecentModule(models.Model):
|
||||||
|
module = models.ForeignKey(Module, on_delete=models.CASCADE, related_name='recent_modules')
|
||||||
|
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
|
||||||
|
visited = models.DateTimeField(default=datetime.now)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
get_latest_by = 'visited'
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
from graphene import relay
|
from graphene import relay
|
||||||
|
|
||||||
from api.utils import get_errors, get_object
|
from api.utils import get_errors, get_object
|
||||||
from books.models import Module, Topic
|
from books.models import Module, Topic, RecentModule
|
||||||
from books.schema.queries import ModuleNode, TopicNode
|
from books.schema.queries import ModuleNode, TopicNode
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -47,12 +49,10 @@ class UpdateLastModule(relay.ClientIDMutation):
|
||||||
# todo: use slug here too
|
# todo: use slug here too
|
||||||
id = graphene.ID()
|
id = graphene.ID()
|
||||||
|
|
||||||
module = graphene.Field(ModuleNode)
|
last_module = graphene.Field(ModuleNode)
|
||||||
errors = graphene.List(graphene.String)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def mutate_and_get_payload(cls, root, info, **args):
|
def mutate_and_get_payload(cls, root, info, **args):
|
||||||
try:
|
|
||||||
user = info.context.user
|
user = info.context.user
|
||||||
id = args.get('id')
|
id = args.get('id')
|
||||||
|
|
||||||
|
|
@ -60,17 +60,19 @@ class UpdateLastModule(relay.ClientIDMutation):
|
||||||
if not module:
|
if not module:
|
||||||
raise Module.DoesNotExist
|
raise Module.DoesNotExist
|
||||||
|
|
||||||
user.last_module = module
|
try:
|
||||||
user.save()
|
same_module = RecentModule.objects.get(user=user, module=module)
|
||||||
|
same_module.visited = datetime.now()
|
||||||
|
same_module.save()
|
||||||
|
return cls(last_module=same_module.module)
|
||||||
|
except RecentModule.DoesNotExist:
|
||||||
|
recent_modules = RecentModule.objects.filter(user=user)
|
||||||
|
while recent_modules.all().count() >= 3:
|
||||||
|
recent_modules.earliest().delete()
|
||||||
|
|
||||||
return cls(module=module)
|
last_module = RecentModule.objects.create(user=user, module=module)
|
||||||
|
|
||||||
except Module.DoesNotExist:
|
return cls(last_module=last_module.module)
|
||||||
return cls(errors=['Module not found'])
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
errors = ['Error: {}'.format(e)]
|
|
||||||
return cls(errors=errors)
|
|
||||||
|
|
||||||
|
|
||||||
class UpdateLastTopic(relay.ClientIDMutation):
|
class UpdateLastTopic(relay.ClientIDMutation):
|
||||||
|
|
@ -93,4 +95,3 @@ class UpdateLastTopic(relay.ClientIDMutation):
|
||||||
user.save()
|
user.save()
|
||||||
|
|
||||||
return cls(topic=topic)
|
return cls(topic=topic)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ from notes.schema import ContentBlockBookmarkNode, ChapterBookmarkNode, ModuleBo
|
||||||
from rooms.models import ModuleRoomSlug
|
from rooms.models import ModuleRoomSlug
|
||||||
from surveys.models import Answer
|
from surveys.models import Answer
|
||||||
from surveys.schema import AnswerNode
|
from surveys.schema import AnswerNode
|
||||||
from ..models import Book, Topic, Module, Chapter, ContentBlock
|
from ..models import Book, Topic, Module, Chapter, ContentBlock, RecentModule
|
||||||
|
|
||||||
|
|
||||||
def process_module_room_slug_block(content):
|
def process_module_room_slug_block(content):
|
||||||
|
|
@ -187,6 +187,12 @@ class ModuleNode(DjangoObjectType):
|
||||||
.prefetch_related('objectives__objective_progress')
|
.prefetch_related('objectives__objective_progress')
|
||||||
|
|
||||||
|
|
||||||
|
class RecentModuleNode(DjangoObjectType):
|
||||||
|
class Meta:
|
||||||
|
model = RecentModule
|
||||||
|
interfaces = (relay.Node,)
|
||||||
|
|
||||||
|
|
||||||
class TopicNode(DjangoObjectType):
|
class TopicNode(DjangoObjectType):
|
||||||
pk = graphene.Int()
|
pk = graphene.Int()
|
||||||
modules = DjangoFilterConnectionField(ModuleNode)
|
modules = DjangoFilterConnectionField(ModuleNode)
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ DEFAULT_SCHOOL_ID = 1
|
||||||
|
|
||||||
class User(AbstractUser):
|
class User(AbstractUser):
|
||||||
last_module = models.ForeignKey('books.Module', related_name='+', on_delete=models.SET_NULL, null=True)
|
last_module = models.ForeignKey('books.Module', related_name='+', on_delete=models.SET_NULL, null=True)
|
||||||
|
# recent_modules = models.ManyToManyField('books.Module', related_name='+', through='books.RecentModule')
|
||||||
last_topic = models.ForeignKey('books.Topic', related_name='+', on_delete=models.SET_NULL, null=True)
|
last_topic = models.ForeignKey('books.Topic', related_name='+', on_delete=models.SET_NULL, null=True)
|
||||||
avatar_url = models.CharField(max_length=254, blank=True, default='')
|
avatar_url = models.CharField(max_length=254, blank=True, default='')
|
||||||
email = models.EmailField(_('email address'), unique=True)
|
email = models.EmailField(_('email address'), unique=True)
|
||||||
|
|
@ -97,7 +98,6 @@ class User(AbstractUser):
|
||||||
user_settings.selected_class = school_class
|
user_settings.selected_class = school_class
|
||||||
user_settings.save()
|
user_settings.save()
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_name(self):
|
def full_name(self):
|
||||||
return self.get_full_name()
|
return self.get_full_name()
|
||||||
|
|
@ -245,7 +245,7 @@ class UserData(models.Model):
|
||||||
class License(models.Model):
|
class License(models.Model):
|
||||||
for_role = models.ForeignKey(Role, blank=False, null=True, on_delete=models.CASCADE)
|
for_role = models.ForeignKey(Role, blank=False, null=True, on_delete=models.CASCADE)
|
||||||
licensee = models.ForeignKey(User, blank=False, null=True, on_delete=models.CASCADE)
|
licensee = models.ForeignKey(User, blank=False, null=True, on_delete=models.CASCADE)
|
||||||
expire_date = models.DateField(blank=False, null=True,)
|
expire_date = models.DateField(blank=False, null=True, )
|
||||||
order_id = models.IntegerField(blank=False, null=False, default=-1)
|
order_id = models.IntegerField(blank=False, null=False, default=-1)
|
||||||
raw = models.TextField(default='')
|
raw = models.TextField(default='')
|
||||||
|
|
||||||
|
|
@ -255,7 +255,8 @@ class License(models.Model):
|
||||||
return self.for_role.key == RoleManager.TEACHER_KEY
|
return self.for_role.key == RoleManager.TEACHER_KEY
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return HepClient.is_product_active(datetime(self.expire_date.year, self.expire_date.month, self.expire_date.day),
|
return HepClient.is_product_active(
|
||||||
|
datetime(self.expire_date.year, self.expire_date.month, self.expire_date.day),
|
||||||
self.for_role.key)
|
self.for_role.key)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -266,4 +267,3 @@ class SchoolClassMember(models.Model):
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
school_class = models.ForeignKey(SchoolClass, on_delete=models.CASCADE)
|
school_class = models.ForeignKey(SchoolClass, on_delete=models.CASCADE)
|
||||||
active = models.BooleanField(default=True)
|
active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ from datetime import datetime
|
||||||
|
|
||||||
import graphene
|
import graphene
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
from django_filters import FilterSet, OrderingFilter
|
||||||
from graphene import relay, ObjectType
|
from graphene import relay, ObjectType
|
||||||
from graphene_django import DjangoObjectType
|
from graphene_django import DjangoObjectType
|
||||||
from graphene_django.filter import DjangoFilterConnectionField
|
from graphene_django.filter import DjangoFilterConnectionField
|
||||||
|
|
@ -10,7 +11,7 @@ from graphql_relay import to_global_id
|
||||||
|
|
||||||
from basicknowledge.models import BasicKnowledge
|
from basicknowledge.models import BasicKnowledge
|
||||||
from basicknowledge.queries import InstrumentNode
|
from basicknowledge.queries import InstrumentNode
|
||||||
from books.models import Module
|
from books.models import Module, RecentModule
|
||||||
from books.schema.queries import ModuleNode
|
from books.schema.queries import ModuleNode
|
||||||
from users.models import User, SchoolClass, SchoolClassMember
|
from users.models import User, SchoolClass, SchoolClassMember
|
||||||
|
|
||||||
|
|
@ -39,6 +40,18 @@ class SchoolClassNode(DjangoObjectType):
|
||||||
return self.code
|
return self.code
|
||||||
|
|
||||||
|
|
||||||
|
class RecentModuleFilter(FilterSet):
|
||||||
|
class Meta:
|
||||||
|
model = Module
|
||||||
|
fields = ('recent_modules',)
|
||||||
|
|
||||||
|
order_by = OrderingFilter(
|
||||||
|
fields=(
|
||||||
|
('recent_modules__visited', 'visited'),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class UserNode(DjangoObjectType):
|
class UserNode(DjangoObjectType):
|
||||||
pk = graphene.Int()
|
pk = graphene.Int()
|
||||||
permissions = graphene.List(graphene.String)
|
permissions = graphene.List(graphene.String)
|
||||||
|
|
@ -46,6 +59,7 @@ class UserNode(DjangoObjectType):
|
||||||
expiry_date = graphene.String()
|
expiry_date = graphene.String()
|
||||||
is_teacher = graphene.Boolean()
|
is_teacher = graphene.Boolean()
|
||||||
old_classes = DjangoFilterConnectionField(SchoolClassNode)
|
old_classes = DjangoFilterConnectionField(SchoolClassNode)
|
||||||
|
recent_modules = DjangoFilterConnectionField(ModuleNode, filterset_class=RecentModuleFilter)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
|
|
@ -82,6 +96,10 @@ class UserNode(DjangoObjectType):
|
||||||
def resolve_old_classes(self, info):
|
def resolve_old_classes(self, info):
|
||||||
return SchoolClass.objects.filter(schoolclassmember__active=False, schoolclassmember__user=self)
|
return SchoolClass.objects.filter(schoolclassmember__active=False, schoolclassmember__user=self)
|
||||||
|
|
||||||
|
def resolve_recent_modules(self, info, **kwargs):
|
||||||
|
# see https://docs.graphene-python.org/projects/django/en/latest/filtering/
|
||||||
|
return RecentModuleFilter(kwargs).qs.filter(recent_modules__user=self)
|
||||||
|
|
||||||
|
|
||||||
class ClassMemberNode(ObjectType):
|
class ClassMemberNode(ObjectType):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue