Пример #1
0
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))
Пример #2
0
def get_recent_activities(user, max_count, exclude_user=None, target=None,
        action_object=None):
    """
        Returns recent activities visible to the given user.
        Selects also actors and actor profiles.

        Specifically, for SOLUTION_SUBMIT checks if the user can view the
        solution, and saves as ._hide_solution_link.
    """
    # Currently, to show recent N activities, we load 2 * N and then manually
    # iterate and remove those that are not visible to the current user. So,
    # the number of shown activities might not be constant. Also, the good
    # thing about this implementation is that we can preload all necessary data
    # about these activities.

    # TODO: refactor activities. for what do we need the cache, as we are
    # anyway loading half of the data again? also, cache must be updated
    # together with the data...

    activity = Action.objects
    if exclude_user and exclude_user.is_authenticated():
        activity = activity.exclude(actor=exclude_user)
    if target:
        activity = activity.filter(target=target)
    if action_object:
        activity = activity.filter(action_object=action_object)

    activity = activity.select_related('actor', 'actor__profile',
        'target_content_type').order_by('-id')[:int(max_count * 2)]
    activity = list(activity)
    activity_by_id = {x.id: x for x in activity}

    task_content_type_id = ContentType.objects.get_for_model(Task).id

    # Check permissions
    to_check = []           # list of (content_type_id, object_id, action_id)
    solutions_to_check = [] # list of (solution_id, atsk_id, action_id)
    kill = set()            # set of action_id to remove
    for x in activity:
        ttype = (x.type, x.subtype)

        # Hide 'marked as official' messages.
        # TODO: create a general way to hide solutions.
        if ttype == SOLUTION_AS_OFFICIAL:
            kill.add(x.id)
            continue

        if ttype in [TASK_ADD, FILE_ADD, LECTURE_ADD, SOLUTION_SUBMIT,
                SOLUTION_AS_SOLVED, SOLUTION_TODO, SOLUTION_AS_OFFICIAL]:
            # check task permissions...
            to_check.append((x.target_content_type_id, x.target_id, x.id))
            if ttype == SOLUTION_SUBMIT:
                # for ._hide_solution_link (kind of hack)
                solutions_to_check.append((x.action_object_id, x.target_id, x.id))
        elif ttype in [GROUP_ADD, GROUP_LEAVE]:
            # check group
            to_check.append((x.target_content_type_id, x.target_id, x.id))
        elif ttype in [POST_SEND, SOLUTION_RATE]:
            # SOLUTION_RATE behaves as POST_SEND for Solution comments...
            # TODO: this is stupid, separate POST_SEND into two different
            # constants?
            if x.target_content_type_id == task_content_type_id:
                to_check.append((x.target_content_type_id, x.target_id, x.id))
            else: # Solution
                try:
                    dummy, dummy, task_id, dummy, dummy = x.target_cache.split(POST_SEND_CACHE_SEPARATOR)
                    task_id = int(task_id)
                    to_check.append((task_content_type_id, task_id, x.id))
                    solutions_to_check.append((x.target_id, task_id, x.id))
                except ValueError:
                    kill.add(x.id)

    # Get content_type_id and object_id pairs
    pairs = [(a, b) for a, b, c in to_check]

    # Get objects
    visible_objects = get_objects_with_permissions(pairs, user, VIEW)

    # Mark invisible actions
    for content_type_id, object_id, action_id in to_check:
        if (content_type_id, object_id) not in visible_objects:
            kill.add(action_id)

    # Now, check solution accessibility...
    # Also, remove anything related to the official solutions.
    solutions_to_check_strict = []
    solution_ids = [id for id, task_id, action_id in solutions_to_check]
    solutions = dict(Solution.objects \
                             .filter(id__in=solution_ids) \
                             .values_list('id', 'is_official'))
    for solution_id, task_id, action_id in solutions_to_check:
        is_official = solutions.get(solution_id)
        # Remove if not found or official (first case should never happen).
        if is_official is None or is_official is True:
            kill.add(action_id)
            continue

        task = visible_objects.get((task_content_type_id, task_id))
        if not task:
            # kill.add(action_id) # already added
            continue
        if task.solution_settings != Task.SOLUTIONS_VISIBLE:
            solutions_to_check_strict.append((solution_id, action_id))

    if solutions_to_check_strict:
        solution_ids = [id for id, action_id in solutions_to_check_strict]
        # Ah, just reload those tasks...
        # TODO: or is it safe to reuse old task instances?
        solutions = list(Solution.objects   \
            .filter(id__in=solution_ids).select_related('task'))

        # Yes, this is the easiest way to do this...
        cache_solution_info(user, solutions)
        solution_by_id = {x.id: x for x in solutions}
        for solution_id, action_id in solutions_to_check_strict:
            solution = solution_by_id[solution_id]
            # TODO: separate can_view and obfuscate into two different methods?
            # we don't need should_obfuscate here...
            can_view, obfuscate = solution.check_accessibility(user,
                solution._cache_my_solution)
            if can_view:
                continue
            action = activity_by_id[action_id]
            if (action.type, action.subtype) in (SOLUTION_SUBMIT, SOLUTION_RATE):
                # don't kill, just remove link
                action._hide_solution_link = True
            else:
                kill.add(action_id)

    # And, finally, remove hidden activities and limit the size of the output.
    activity = [x for x in activity if x.id not in kill]
    return activity[:max_count]