def cache_solution_info(user, solutions): task_ids = [x.task_id for x in solutions] tasks = [x.task for x in solutions] check_prerequisites_for_tasks(tasks, user) if user.is_authenticated(): my_solutions = Solution.objects.filter(author=user, task_id__in=task_ids) my_solutions = {x.task_id: x for x in my_solutions} else: my_solutions = {} # Can view solutions? # First, ignore those with SOLUTIONS_VISIBLE setting, and remove duplicates. explicit_ids = set([x.task_id for x in solutions \ if x.task.solution_settings != Task.SOLUTIONS_VISIBLE \ and x.task.author_id != user.id]) if explicit_ids: # Second, if any left, ask for permissions. explicit_ids = get_object_ids_with_exclusive_permission( user, VIEW_SOLUTIONS, model=Task, filter_ids=explicit_ids) explicit_ids = set(explicit_ids) for y in solutions: y._cache_my_solution = my_solutions.get(y.task_id) y.task._cache_can_view_solutions = y.task_id in explicit_ids \ or y.task.solution_settings == Task.SOLUTIONS_VISIBLE \ or y.task.author_id == user.id return ''
def _check_prereqs(self, tasks, user, expected): """ Checks prerequisites for given tasks for "current" user. """ check_prerequisites_for_tasks(tasks, user) for k, task in enumerate(tasks): msg = "{} is not {} for k = {}".format( not expected[k], expected[k], k) self.assertEqual(task.cache_prerequisites_met, expected[k], msg=msg)
def cache_task_info_lite(context, tasks): """ Prepares data for task list in options mode, where whole queryset is selected, and only basic info is visible (such as queryset length...) """ ids = [x.id for x in tasks] check_prerequisites_for_tasks(tasks, context['user']) unlocked_ids = [x.id for x in tasks if not x.cache_prerequisites_met] # ------ context variables -------- context['task_ids'] = ids context['unlocked_task_count'] = len(unlocked_ids) return ''
def homepage(request): recent_tasks = list(Task.objects.for_user(request.user, VIEW).select_related("content").order_by("-id")[:10]) check_prerequisites_for_tasks(recent_tasks, request.user) # Filter visible tasks recent_tasks = [x for x in recent_tasks if x.cache_prerequisites_met] if len(recent_tasks) > 2: recent_tasks = random.sample(recent_tasks, 2) if request.user.is_authenticated(): return homepage_online(request, recent_tasks) else: return homepage_offline(request, recent_tasks)
def homepage_online(request, recent_tasks): recommend = UserRecommendation.objects.raw( "SELECT A.id, A.task_id FROM recommend_userrecommendation A" " LEFT JOIN solution_solution B" " ON (A.task_id = B.task_id AND A.user_id = B.author_id)" " WHERE A.user_id = {} AND (B.status IS NULL OR B.status = 0);".format(request.user.id) ) recommend = list(x.task_id for x in recommend) if len(recommend) > 4: recommend = random.sample(recommend, 4) else: recommend = [] # Simplify design, accept only if there are 4 tasks. todo = Solution.objects.filter(author=request.user, status=SolutionStatus.TODO).values_list("task_id", flat=True)[ :20 ] if len(todo) > 2: todo = random.sample(todo, 2) else: todo = [] # Simplify design, ignore if only one to do task. all_tasks_to_read = todo + recommend if all_tasks_to_read: all_tasks = Task.objects.select_related("content").in_bulk(all_tasks_to_read) # Just in case something went wrong (probably with recommendations). check_prerequisites_for_tasks(all_tasks.itervalues(), request.user) all_tasks = {id: task for id, task in all_tasks.iteritems() if task.cache_prerequisites_met} recommend = [all_tasks[id] for id in recommend] todo = [all_tasks[id] for id in todo] # context, tasks cache_task_info({"user": request.user}, recommend + recent_tasks) return ( "homepage_online.html", { "homepage": True, "folder_shortcut_desc": settings.FOLDER_HOMEPAGE_SHORTCUTS_ONLINE, "recent_tasks": recent_tasks, "recommend": recommend, "todo": todo, }, )
def cache_task_info(context, tasks): """ Prepares data (tags, solution status and similar) for task list. Usually just one page of tasks is considered. """ # TODO: cache_task_info util method that takes user, not context. user = context['user'] task_content_type = ContentType.objects.get_by_natural_key(app_label="task", model="task") ids = [x.id for x in tasks] # ----- tags ----- tagovi = TaggedItem.objects.filter(content_type=task_content_type, object_id__in=ids).select_related('tag') tagged_items = collections.defaultdict(list) for x in tagovi: tagged_items[x.object_id].append(x) for task in tasks: task._cache_tagged_items = sorted(tagged_items[task.id], key=lambda x: (x.tag.name, x.votes_sum)) # ----- solutions ------ if user.is_authenticated(): solutions = Solution.objects.filter(author=user, task_id__in=ids) solutions = {x.task_id: x for x in solutions} for task in tasks: task.cache_solution = solutions.get(task.id) check_prerequisites_for_tasks(tasks, user) # ----- folder edit ----- if user.is_authenticated(): folder = user.profile.selected_folder if folder is not None: selected_tasks = folder.tasks.filter(id__in=ids).values_list('id', flat=True) for task in tasks: task.is_in_folder = task.id in selected_tasks # ------ context variables -------- context['task_ids'] = ids return ''
def profile(request, pk): if request.user.pk == pk: user = request.user solutions = Solution.objects tasks = Task.objects else: user = get_object_or_404(User.objects.select_related('profile'), pk=pk) solutions = Solution.objects.filter_visible_tasks_for_user(request.user) tasks = Task.objects.for_user(request.user, VIEW) # DEPRECATED. Distribution should be now updated automatically... # user.profile.refresh_diff_distribution() distribution = user.profile.get_diff_distribution() high = max(distribution) if high > 0: scale = 100.0 / max(high, 10) scaled = [int(x * scale) for x in distribution] distribution = zip(DIFFICULTY_RATING_ATTRS['titles'], scaled, distribution) else: distribution = None if request.user.id == pk: visible_groups = user.groups.select_related('data') else: where = '((SELECT id FROM auth_user_groups AG2 ' \ 'WHERE AG2.group_id = auth_group.id AND AG2.user_id = {} ' \ 'LIMIT 1)' \ ' IS NOT NULL OR usergroup_usergroup.hidden != 0)' \ .format(user.id) visible_groups = user.groups.select_related('data').extra(where=[where]) visible_groups = visible_groups.exclude(id=user.get_profile().private_group_id) tags = UserTagScore.objects.filter(user=user).select_related('tag').order_by('-cache_score')[:10] solutions = solutions.filter(author_id=pk) \ .select_related('task') \ .order_by('-date_created') todo = solutions.filter(status=SolutionStatus.TODO)[:10] solved = solutions.filter( status__in=[SolutionStatus.AS_SOLVED, SolutionStatus.SUBMITTED])[:10] if pk != request.user.pk: # TODO: optimize, do not load unnecessary my_solution # (separate check_accessibility should_obfuscate?) cache_solution_info(request.user, solved) for x in solved: x.t_can_view, dummy = x.check_accessibility( request.user, x._cache_my_solution) task_added = tasks.filter(author_id=pk).order_by('-id')[:10] all_tasks = [x.task for x in solved] \ + [x.task for x in todo] \ + list(task_added) check_prerequisites_for_tasks(all_tasks, request.user) return render_to_response('profile_detail.html', { 'profile': user, 'distribution': distribution, 'visible_groups': visible_groups, 'tags': tags, 'todo': todo, 'task_added': task_added, 'solved': solved, }, context_instance=RequestContext(request))
def export(request, format=None, ids=None): """ Exports tasks with given ids to given format. Format and ids can be given as GET or POST information. """ # Please note that both TaskExportForm and unnamed form (format, ids) # use POST method. To prevent collision, submit button in TaskExportForm # is named 'action'. POST = request.POST.copy() # Move URL / GET data to POST if format and ids: POST['format'] = format POST['ids'] = ids else: format = POST.get('format') ids = POST.get('ids') available_formats = dict(EXPORT_FORMAT_CHOICES) if not ids or format not in available_formats: raise Http404 try: id_list = [int(x) for x in ids.split(',')] except ValueError: raise Http404 # check for permissions # TODO: use some permissions util method tasks = Task.objects.for_user(request.user, VIEW).filter(id__in=id_list).distinct() if len(tasks) != len(id_list): raise Http404('Neki od navedenih zadataka ne postoje ili su skriveni.') check_prerequisites_for_tasks(tasks, request.user) removed_tasks = [x for x in tasks if not x.cache_prerequisites_met] if removed_tasks: # Remove them for the list. id_list = [x.id for x in tasks if x.cache_prerequisites_met] ids = ','.join(str(x) for x in id_list) # permission ok, use shortened query tasks = Task.objects.filter(id__in=id_list) # keep the same order as in id_list task_position = {id: position for position, id in enumerate(id_list)} sorted_tasks = list(tasks) sorted_tasks.sort(key=lambda task: task_position[task.id]) for x in sorted_tasks: x.cache_prerequisites_met = True # force queryset evaluation and prepare all attachments... content_to_task = {} for task in tasks: task.cache_file_list = [] content_to_task[task.content_id] = task # attachments query = "SELECT A.* FROM mathcontent_attachment A" \ " INNER JOIN task_task B ON A.content_id = B.content_id" \ " WHERE B.id IN ({})".format(ids) attachments = list(Attachment.objects.raw(query)) for attachment in attachments: content_to_task[attachment.content_id].cache_file_list.append(attachment) invalid_tasks = None if request.method == 'POST' and 'action' in POST: form = TaskExportForm(POST) if form.is_valid(): # note that attachments are imported into each task as .cache_file_list ignore_exceptions = request.POST.get('ignore-exceptions') try: return _export(ids, sorted_tasks, tasks, form, ignore_exceptions) except _ConvertException as e: invalid_tasks = e.invalid_tasks # otherwise, if form not given or not valid: create_archive = len(attachments) > 0 if len(id_list) == 1: data = (format, ids, True, True, True, False, False, create_archive) else: data = (format, ids, False, False, False, False, True, create_archive) data = dict(zip(('format', 'ids', 'has_title', 'has_url', 'has_source', 'has_index', 'has_id', 'create_archive'), data)) form = TaskExportForm(data) if len(attachments): form.fields['create_archive'].label = \ 'Zip arhiva (ukupno datoteka: {}+1)'.format(len(attachments)) else: form.fields['create_archive'].widget = forms.HiddenInput() return { 'all_tasks': removed_tasks + sorted_tasks, 'attachments': attachments, 'form': form, 'format': available_formats[format], 'invalid_tasks': invalid_tasks, 'removed_tasks': removed_tasks, 'tasks': sorted_tasks, }