Example #1
0
def submit_comments(request, assignment_id, login, submit_num):
    submit = get_object_or_404(Submit,
                               assignment_id=assignment_id,
                               student__username=login,
                               submit_num=submit_num)

    if not is_teacher(
            request.user) and request.user.username != submit.student.username:
        raise PermissionDenied()

    submits = []
    for s in Submit.objects.filter(
            assignment_id=assignment_id,
            student__username=login).order_by('submit_num'):
        submits.append({
            'num': s.submit_num,
            'submitted': s.created_at,
            'points': s.assigned_points,
        })

    def get_comment_type(comment):
        if comment.author == comment.submit.student:
            return 'student'
        return 'teacher'

    notifications = {
        c.action_object.id: c
        for c in Notification.objects.filter(
            target_object_id=submit.id,
            target_content_type=ContentType.objects.get_for_model(Submit))
    }

    def dump_comment(comment):
        notification = notifications.get(comment.id, None)
        unread = False
        notification_id = None
        if notification:
            unread = notification.unread
            notification_id = notification.id

        return {
            'id': comment.id,
            'author': comment.author.get_full_name(),
            'author_id': comment.author.id,
            'text': comment.text,
            'can_edit': comment.author == request.user,
            'type': get_comment_type(comment),
            'unread': unread,
            'notification_id': notification_id,
        }

    if request.method == 'POST':
        data = json.loads(request.body)
        comment = Comment()
        comment.submit = submit
        comment.author = request.user
        comment.text = data['text']
        comment.source = data.get('source', None)
        comment.line = data.get('line', None)
        comment.save()

        notify.send(
            sender=request.user,
            recipient=comment_recipients(submit, request.user),
            verb='added new',
            action_object=comment,
            target=submit,
            public=False,
            important=True,
        )
        return JsonResponse({**dump_comment(comment), 'unread': True})
    elif request.method == 'PATCH':
        data = json.loads(request.body)
        comment = get_object_or_404(Comment, id=data['id'])

        if comment.author != request.user:
            raise PermissionDenied()

        Notification.objects.filter(
            action_object_object_id=comment.id,
            action_object_content_type=ContentType.objects.get_for_model(
                Comment)).delete()

        if not data['text']:
            comment.delete()
            return HttpResponse('{}')
        else:
            if comment.text != data['text']:
                comment.text = data['text']
                comment.save()

                notify.send(
                    sender=request.user,
                    recipient=comment_recipients(submit, request.user),
                    verb='updated',
                    action_object=comment,
                    target=submit,
                    public=False,
                    important=True,
                )
            return JsonResponse(dump_comment(comment))

    result = {}
    for source in submit.all_sources():
        mime = mimedetector.from_file(source.phys)
        if mime and mime.startswith('image/'):
            SUPPORTED_IMAGES = [
                'image/png',
                'image/jpeg',
                'image/gif',
                'image/webp',
                'image/svg+xml',
            ]

            result[source.virt] = {
                'type':
                'img',
                'path':
                source.virt,
                'src':
                reverse('submit_source', args=[submit.id, source.virt]) +
                ('?convert=1' if mime not in SUPPORTED_IMAGES else ''),
            }
        elif mime and mime.startswith("video/"):
            name = ('.'.join(source.virt.split('.')[:-1]))
            if name not in result:
                result[name] = {
                    'type': 'video',
                    'path': name,
                    'sources': [],
                }
            result[name]['sources'].append(
                reverse('submit_source', args=[submit.id, source.virt]))
        else:
            content = ''
            content_url = None
            error = None

            try:
                if is_file_small(source.phys):
                    with open(source.phys) as f:
                        content = f.read()
                else:
                    content_url = reverse("submit_source",
                                          kwargs=dict(submit_id=submit.id,
                                                      path=source.virt))
            except UnicodeDecodeError:
                error = "The file contains binary data or is not encoded in UTF-8"
            except FileNotFoundError:
                error = "source code not found"

            result[source.virt] = {
                'type': 'source',
                'path': source.virt,
                'content': content,
                'content_url': content_url,
                'error': error,
                'comments': {},
            }

    # add comments from pipeline
    resultset = get(submit)
    for pipe in resultset['results']:
        for source, comments in pipe.comments.items():
            for comment in comments:
                try:
                    line = min(result[source]['content'].count('\n'),
                               comment['line']) - 1
                    if not any(
                            filter(
                                lambda c: c['text'] == comment['text'],
                                result[source]['comments'].setdefault(
                                    line, []))):
                        result[source]['comments'].setdefault(line, []).append(
                            {
                                'id': -1,
                                'author': 'Kelvin',
                                'text': comment['text'],
                                'can_edit': False,
                                'type': 'automated',
                                'url': comment.get('url', None),
                            })
                except KeyError as e:
                    logging.exception(e)

    summary_comments = []
    for comment in Comment.objects.filter(submit_id=submit.id).order_by('id'):
        try:
            if not comment.source:
                summary_comments.append(dump_comment(comment))
            else:
                max_lines = result[comment.source]['content'].count('\n')
                line = 0 if comment.line > max_lines else comment.line
                result[comment.source]['comments'].setdefault(
                    comment.line - 1, []).append(dump_comment(comment))
        except KeyError as e:
            logging.exception(e)

    priorities = {
        'video': 0,
        'img': 1,
        'source': 2,
    }
    return JsonResponse({
        'sources':
        sorted(result.values(),
               key=lambda f: (priorities[f['type']], f['path'])),
        'summary_comments':
        summary_comments,
        'submits':
        submits,
        'current_submit':
        submit.submit_num,
        'deadline':
        submit.assignment.deadline,
    })