Example #1
0
 def _set_up_tasks(self):
     # TODO: add base test for model with userprofiles.
     self.admin = User.objects.get(id=1)
     self.alice = User.objects.get(id=2)
     content1 = MathContent.objects.create(text="Test text for the task1")
     content2 = MathContent.objects.create(text="Test text for the task2")
     self.admin_task = Task.objects.create(name="First", content=content1,
             author=self.admin, hidden=True)
     self.alice_task = Task.objects.create(name="Second", content=content2,
             author=self.alice, hidden=True)
     set_tags(self.admin_task, ["IMO", "geo"])
     set_tags(self.alice_task, ["MEMO", "alg"])
Example #2
0
def create_tasks_from_json(description):
    """
    Given a list of Task description dictionaries, create Task instances,
    together with other related objects.

    Supported special data:
        _content (string) - the text of the task, MathContent text
        _folder_id (int) - ID of the folder where to add the task
        _folder_position (int) - position in that folder
        _tags (string) - a comma-separated list of tags
        _difficulty (int) - difficulty rating to be assigned by the author
        _permissions (dict {"permission type ID": [list of group IDs]})
                - group permission to automatically assign.
                - (ID is a string because in JSON keys must be strings)

    All other elements with an underscore are ignored.

    Params:
        description (list): list of dict object, describing the tasks

    Returns:
        A list of the new Task objects.

    If an exception is thrown in the middle of the process, the exception is
    caputed, the message changed, and raised again.
    """
    # Approx.
    task_fields = set(Task._meta.get_all_field_names())
    task_fields |= set(x + '_id' for x in task_fields)

    created_objects = []
    created_tasks = []
    message_list = []

    # List of objects to create
    folder_tasks = []
    object_permissions = []

    try:
        for k, desc in enumerate(description):
            message_list.append('Creating {}. task...'.format(k + 1))

            # First, prepare data to be able to create Task.
            # --- math content ---
            math_content = MathContent()
            math_content.text = desc['_content']
            message_list.append(desc['_content'])
            math_content.save()
            created_objects.append(math_content)

            # Second, create Task.
            task = Task()
            task.content = math_content
            for key, value in desc.iteritems():
                if key[0] != '_' and key in task_fields:
                    setattr(task, key, value)
            task.save()
            created_objects.append(task)
            created_tasks.append(task)

            # Third, save other data.

            # --- tags ---
            tags = desc.get('_tags', '')
            set_tags(task, tags)

            # --- difficulty ---
            difficulty = desc.get('_difficulty')
            if difficulty:
                task.difficulty_rating.update(task.author, int(difficulty))

            # --- folder ids ---
            folder_id = desc.get('_folder_id')
            if folder_id is not None:
                folder_tasks.append(FolderTask(
                        folder_id=folder_id, task=task,
                        position=desc.get('_folder_position', 0)))

            # --- group permissions ---
            for perm, group_ids in desc.get('_permissions', {}).iteritems():
                for group_id in group_ids:
                    object_permissions.append(ObjectPermission(
                            content_object=task, group_id=group_id,
                            permission_type=perm))


        FolderTask.objects.bulk_create(folder_tasks)
        ObjectPermission.objects.bulk_create(object_permissions)
    except Exception as e:
        # This should remove all dependend objects.
        for obj in created_objects:
            obj.delete()

        message_list.append("Reverting changes...")
        message = "\n".join(message_list) + "\n\n" + traceback.format_exc()
        raise type(e)(message)
    finally:
        # SPEED: Don't invalidate everything.
        invalidate_cache_for_folders(Folder.objects.all())
    return created_tasks
Example #3
0
def new(request, task_id=None, is_file=None, is_lecture=None):
    """
        New Task and Edit Task
        + New TaskFile and Edit TaskFile
    """
    content_type = ContentType.objects.get_for_model(Task)

    if task_id:
        task = get_object_or_404(Task.objects.select_related('content'), pk=task_id)
        perm = task.get_user_permissions(request.user)
        if EDIT not in perm:
            return 403
        math_content = task.content
        edit = True
        is_file = task.is_file()
        is_lecture = task.is_lecture
    else:
        perm = []
        task = math_content = None
        edit = False

    # Make sure each lecture is a file.
    assert is_lecture and is_file or not is_lecture

    form_class = TaskLectureForm if is_lecture \
            else (TaskFileForm if is_file else TaskForm)
    math_content_label = 'Opis' if is_file else None    # else default

    if request.method == 'POST':
        old_hidden = getattr(task, 'hidden', -1)
        old_solvable = getattr(task, 'solvable', -1)

        # Files can have blank description (i.e. math content)
        task_form = form_class(request.POST, instance=task, user=request.user)
        math_content_form = MathContentForm(request.POST, instance=math_content,
            blank=is_file, label=math_content_label, auto_preview=False)
        attachment_form = is_file and not edit \
            and AttachmentForm(request.POST, request.FILES)

        if task_form.is_valid() and math_content_form.is_valid()    \
                and (not attachment_form or attachment_form.is_valid()):

            task = task_form.save(commit=False)
            math_content = math_content_form.save()

            if not edit:
                if attachment_form:
                    attachment, attachment_form = check_and_save_attachment(
                        request, math_content)
                    task.file_attachment = attachment   # This is a file.
                    path = attachment.get_full_path_and_filename()
                    try:
                        thumbnail_path = create_file_thumbnail(path)
                        task.cache_file_attachment_thumbnail_url = \
                                media_path_to_url(thumbnail_path)
                    except ThumbnailRenderingException:
                        pass

                    # Immediately remember file url, so that we don't have to
                    # access Attachment table to show the link.
                    task.cache_file_attachment_url = attachment.get_url()
                else:
                    task.file_attachment = None         # This is a task.

            if is_file:
                task.cache_file_attachment_url = task.file_attachment.get_url()
            if not edit:
                task.author = request.user
            task.content = math_content

            task.save()
            # Do not call task_form.save_m2m()!
            set_tags(task, task_form.cleaned_data['tags'])

            # TODO: signals!
            if not edit or old_hidden != task.hidden    \
                    or old_solvable != task.solvable:   \
                invalidate_folder_cache_for_task(task)

            # send action if creating a new nonhidden task
            if not edit:
                # TODO: signals!
                type = _action.LECTURE_ADD if is_lecture \
                        else (_action.FILE_ADD if is_file else _action.TASK_ADD)

                _action.add(request.user, type,
                    action_object=task, target=task)

            # TODO: izbrisati task_new_finish.html i url
            #return HttpResponseRedirect('/task/%d/' % task.id if edit else '/task/new/finish/')
            return HttpResponseRedirect(task.get_absolute_url())
    else:
        task_form = form_class(instance=task)
        math_content_form = MathContentForm(instance=math_content,
            blank=is_file, label=math_content_label, auto_preview=False)
        attachment_form = is_file and not edit and AttachmentForm()

    forms = [task_form, math_content_form]
    if attachment_form:
        forms.append(attachment_form)

    data = get_task_folder_data(task, request.user) if task else {}

    data.update({
        'action_url': request.path,
        'bulk_add_url': '/task/new/bulk/',
        'can_edit_permissions': EDIT_PERMISSIONS in perm,
        'content_type': content_type,
        'edit': edit,
        'forms': forms,
        'is_file': is_file,
        'is_lecture': is_lecture,
        'lectures_folder_url': settings.LECTURES_FOLDER_URL,
        'task_name': task.name if task else None,  # Convenience.
        'task': task,
    })

    return data
Example #4
0
def _create_task(author, tags, hidden):
    content = MathContent.objects.create(text="Test text", html="Test text")
    task = Task.objects.create(name="Test task", content=content,
            author=author, hidden=hidden)
    set_tags(task, tags)
    return task
Example #5
0
def _create_folders(author, parent, structure, p):
    vars = {'p': p}

    level, separator, rest = structure.partition('|')
    rest = rest.strip()

    # Split the level description into lines, remove trailing and leading
    # whitespaces, and remove empty lines
    lines = filter(None, [x.strip() for x in level.strip().split('\n')])

    # Child format defined in the first line
    # Format: var_name1/var_name2/.../var_nameN
    var_names = [x.strip() for x in lines[0].split('/')]

    # Evaluate variables in specified order, don't shuffle them!
    var_formats = []

    # List of children tuples
    children = []

    # Skip first line!
    for x in lines[1:]:
        left, separator, right = x.partition('=')

        if separator:
            # Variable definition: var_name=this is a example number {x}
            var_formats.append((left, right))
        elif left[0] == '%':
            # Special command
            if left.startswith('%RANGE'):
                # E.g. %RANGE 2012, 1996
                # --> Adds children: 2012, 2011, ..., 1997, 1996
                a, b = [int(x) for x in left[6:].split(',')]
                r = range(a, b + 1) if a <= b else range(a, b - 1, -1)
                children.extend([str(x) for x in r])
            else:
                raise Exception('Nepoznata naredba: ' + left)
        else:
            # Child definition: var_value1/var_value2/.../var_valueN
            children.append(left)

    created = 0
    existing = 0
    for index, x in enumerate(children):
        # Update vars with child var values. (values are stripped!)
        vars.update({k: v.strip() for k, v in zip(var_names, x.split('/'))})

        # Update additional vars
        for var in var_formats:
            # Note we are using same dictionary that is being updated, that's
            # why order matters
            vars[var[0]] = var[1].format(**vars)

        try:
            # Check if folder with the same name, short name and parent exists.
            folder = Folder.objects.get(parent=parent, name=vars['name'],
                    short_name=vars['short'])
            existing += 1
        except:
            # If not, create new folder.
            folder = Folder(author=author, parent=parent, parent_index=index,
                hidden=False, editable=False, name=vars['name'],
                short_name=vars['short'])
            folder.save()
            created += 1

        # Note that the object has to exist to use this!
        set_tags(folder, vars['tags'])
        # TODO: call folder._refresh_cache_tags() on change signal.

        # Call recursion if there is any level left
        if rest:
            # Note that parent changed!
            _c, _e = _create_folders(author, folder, rest, _dict_to_object(vars))
            created += _c
            existing += _e

    return created, existing
Example #6
0
def new(request, folder_id=None):
    # Analogous to task.models.new

    if folder_id:
        folder = get_object_or_404(Folder, id=folder_id)
        edit = True
        old_parent_id = folder.parent_id
    else:
        folder = old_parent_id = None
        edit = False

    data = {}

    initial_parent_id = None

    if edit:
        if not folder.editable:
            return response.FORBIDDEN

        permissions = folder.get_user_permissions(request.user)

        if EDIT not in permissions:
            return response.FORBIDDEN

        if EDIT_PERMISSIONS in permissions:
            data['can_edit_permissions'] = True
            data['content_type'] = ContentType.objects.get_for_model(Folder)

        data['children'] = children = list(Folder.objects   \
            .for_user(request.user, VIEW)                   \
            .filter(parent=folder).order_by('parent_index').distinct())

        data['has_subfolders_strict'] = Folder.objects  \
            .filter(parent_id=folder_id).exists()
    else:
        referrer = get_referrer_path(request)
        if referrer and referrer.startswith('/folder/'):
            try:
                initial_parent_id = int(referrer[8:referrer.find('/', 8)])
            except:
                pass

    if request.method == 'POST':
        folder_form = FolderForm(request.POST, instance=folder, user=request.user)
        if folder_form.is_valid():
            folder = folder_form.save(commit=False)

            # TODO: Make a util function for creating Folder instances, to be
            # able to create tests easier.

            # If user can't edit short name, copy full name.
            if 'short_name' not in folder_form.fields:
                folder.short_name = folder.name

            if not edit:
                folder.author = request.user
            else:
                for x in children:
                    parent_index = request.POST.get('child-{}'.format(x.id))
                    parent_index = int(parent_index)
                    if parent_index is not None \
                            and x.parent_index != parent_index:
                        x.parent_index = parent_index
                        x.save()

                # Update order...
                children.sort(key=lambda x: x.parent_index)

                # If editing, refresh immediately before saving.
                # Do not call folder_form.save_m2m()!
                folder._no_save = True
                set_tags(folder, folder_form.cleaned_data['tags'])

            folder.save()

            if not edit:
                # If new, first save folder (and define ID), and then save tags.
                set_tags(folder, folder_form.cleaned_data['tags'])

            # Refresh Folder cache.
            if old_parent_id != folder.parent_id:
                # The only folders that have to be refreshed are those in
                # the folder's subtree, together with folder itself.
                descendant_ids = get_folder_descendant_ids(folder.id)
                descendant_ids.append(folder.id)
                # refresh_path_cache also requires ancestors. Following code
                # works, because you can't move a folder into its child folder.
                descendant_ids.extend([int(x)
                    for x in folder.parent.cache_ancestor_ids.split(',') if x])
                descendant_ids.append(folder.parent_id)
                refresh_path_cache(Folder.objects.filter(id__in=descendant_ids))

            if not edit:
                return ('/folder/{}/edit/'.format(folder.id), )
            # return HttpResponseRedirect(folder.get_absolute_url())
    else:
        folder_form = FolderForm(instance=folder, user=request.user,
            initial_parent_id=initial_parent_id)

        # If parent given and acceptable, show menu. (new mode)
        initial_parent = getattr(folder_form, 'initial_parent', None)
        if initial_parent:
            data.update(prepare_folder_menu([initial_parent], request.user))

    if edit:
        data.update(prepare_folder_menu([folder], request.user))
        data['folder'] = folder

    data['form'] = folder_form
    data['edit'] = edit

    if request.user.has_perm('folder.advanced_create'):
        data['advanced_create_permission'] = True

    return data