Add recently visited modules to user

This commit is contained in:
Ramon Wenger 2020-06-25 15:11:17 +02:00
parent 991efbe613
commit e899bbe21b
7 changed files with 89 additions and 28 deletions

File diff suppressed because one or more lines are too long

View File

@ -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)),
],
),
]

View File

@ -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'

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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):
""" """