Example #1
0
def delete_images(request, image_id):
    image = get_object_or_404(Image, id=image_id)
    if image.image_set.has_perm(
            'delete_images', request.user) and not image.image_set.image_lock:
        root().remove(image.path())
        image.delete()
        next_image = request.POST.get('next-image-id', '')
        if next_image == '':
            return redirect(
                reverse('images:view_imageset', args=(image.image_set.id, )))
        else:
            return redirect(
                reverse('annotations:annotate', args=(next_image, )))
Example #2
0
def delete_imageset(request, imageset_id):
    imageset = get_object_or_404(ImageSet, id=imageset_id)
    if not imageset.has_perm('delete_set', request.user):
        messages.warning(
            request, _('You do not have permission to delete this imageset.'))
        return redirect(reverse('images:imageset', args=(imageset.pk, )))

    if request.method == 'POST':
        root().removetree(imageset.root_path())
        imageset.delete()
        return redirect(reverse('users:team', args=(imageset.team.id, )))

    return render(request, 'images/delete_imageset.html', {
        'imageset': imageset,
    })
Example #3
0
def create_tool(request):
    if request.method == 'POST':
        form = ToolUploadForm(request.POST, request.FILES)
        if form.is_valid():
            tool = form.instance
            # TODO: use team permissions
            if tool.team and request.user not in tool.team.members.all():
                messages.error(
                    request,
                    'You are not permitted to create tools for the team "{}".'.
                    format(tool.team.name))
                return redirect(reverse('tools:overview'))
            with transaction.atomic():
                tool.creator = request.user
                tool.save()
            tool.filename = '{}_{}'.format(tool.id, request.FILES['file'].name)
            tools_dir = root().makedirs(settings.TOOLS_PATH, recreate=True)
            with tools_dir.open(tool.filename, 'wb+') as f:
                for chunk in request.FILES['file']:
                    f.write(chunk)
            messages.success(request, 'The tool was successfully uploaded')
            tool.save()
            return redirect(reverse('tools:overview'))

    messages.error(
        request,
        'There was an error with your upload. You can only upload files up to 2 MiB'
    )
    return redirect(reverse('tools:overview'))
Example #4
0
def create_imageset(request):
    team = get_object_or_404(Team, id=request.POST['team'])

    if not team.has_perm('create_set', request.user):
        messages.warning(
            request,
            _('You do not have permission to create image sets in the team {}.'
              ).format(team.name))
        return redirect(reverse('users:team', args=(team.id, )))

    form = ImageSetCreationForm()

    if request.method == 'POST':
        form = ImageSetCreationForm(request.POST)

        if form.is_valid():
            if team.image_sets\
                    .filter(name=form.cleaned_data.get('name')).exists():
                form.add_error(
                    'name',
                    _('The name is already in use by an imageset of this team.'
                      ))
            else:
                with transaction.atomic():
                    form.instance.team = team
                    form.instance.creator = request.user
                    form.instance.save()
                    form.instance.path = '{}_{}'.format(
                        team.id, form.instance.id)
                    form.instance.save()

                    # create a folder to store the images of the set
                    folder_path = form.instance.root_path()
                    root().makedirs(folder_path, recreate=True)
                    # TODO rfrigg: Check if necessary
                    # shutil.chown(folder_path, group=settings.UPLOAD_FS_GROUP)

                messages.success(request,
                                 _('The image set was created successfully.'))
                return redirect(
                    reverse('images:view_imageset', args=(form.instance.id, )))

    return render(request, 'images/create_imageset.html', {
        'team': team,
        'form': form,
    })
Example #5
0
def delete_tool(request, tool_id):
    if request.method == 'POST':
        tool = get_object_or_404(Tool, id=tool_id)
        if tool.has_perm('delete_tool', request.user):
            tools_dir = root().opendir(settings.TOOLS_PATH)
            try:
                tools_dir.remove(tool.filename)
            except ResourceNotFound:
                pass
            tool.delete()
        else:
            messages.error(
                request, 'You do not have the permission to delete the tool!')
    return redirect(reverse('tools:overview'))
Example #6
0
def download_imageset_zip(request, image_set_id):
    """
    Get a zip archive containing the images of the imageset with id image_set_id.
    If the zip file generation is still in progress, a HTTP status 202 (ACCEPTED) is returned.
    For empty image sets, status 204 (NO CONTENT) is returned instead of an empty zip file.
    """
    image_set = get_object_or_404(ImageSet, id=image_set_id)

    if not settings.ENABLE_ZIP_DOWNLOAD:
        return HttpResponse(status=HTTP_404_NOT_FOUND)

    if not image_set.has_perm('read', request.user):
        return HttpResponseForbidden()

    if image_set.image_count == 0:
        # It should not be possible to download empty image sets. This
        # is already blocked in the UI, but it should also be checked
        # on the server side.
        return HttpResponse(status=HTTP_204_NO_CONTENT)

    if image_set.zip_state != ImageSet.ZipState.READY:
        return HttpResponse(content=b'Imageset is currently processed',
                            status=HTTP_202_ACCEPTED)

    if settings.USE_NGINX_IMAGE_PROVISION:
        response = HttpResponse(content_type='application/zip')
        response['X-Accel-Redirect'] = "/ngx_static_dn/{0}".format(
            image_set.relative_zip_path())
    else:
        response = FileResponse(root().open(image_set.zip_path(), 'rb'),
                                content_type='application/zip')

    response['Content-Length'] = root().getsize(image_set.zip_path())
    response['Content-Disposition'] = "attachment; filename={}".format(
        image_set.zip_name())
    return response
Example #7
0
def view_image(request, image_id):
    """
    This view is to authenticate direct access to the images via nginx auth_request directive

    it will return forbidden on if the user is not authenticated
    """
    image = get_object_or_404(Image, id=image_id)
    if not image.image_set.has_perm('read', request.user):
        return HttpResponseForbidden()

    if settings.USE_NGINX_IMAGE_PROVISION:
        response = HttpResponse()
        # Let nginx determine the Content-Type
        del response['Content-Type']
        response['X-Accel-Redirect'] = "/ngx_static_dn/{}".format(
            image.relative_path())
        response["Content-Length"] = root().getsize(image.path())
    else:
        bytes_io = BytesIO()
        root().download(image.path(), bytes_io)
        bytes_io.seek(0)
        response = FileResponse(bytes_io, content_type="image")
        response["Content-Length"] = bytes_io.getbuffer().nbytes
    return response
Example #8
0
    def _regenerate_zip(self, imageset):
        updated = ImageSet.objects.filter(pk=imageset.pk) \
            .filter(~Q(zip_state=ImageSet.ZipState.READY)) \
            .update(zip_state=ImageSet.ZipState.PROCESSING)
        if not updated:
            self.stderr.write(
                'skipping regeneration of ready imageset {}'.format(
                    imageset.name))
            return

        tmp_zip_path = imageset.tmp_zip_path()
        tmp().makedirs(path.dirname(tmp_zip_path), recreate=True)
        with tmp().open(tmp_zip_path, 'wb') as zip_file:
            with WriteZipFS(zip_file) as zip_fs:
                for image in imageset.images.all():
                    root().download(image.path(),
                                    zip_fs.openbin(image.name, 'w'))
        with tmp().open(tmp_zip_path, 'rb') as zip_file:
            root().upload(imageset.zip_path(), zip_file)
        tmp().remove(tmp_zip_path)

        # Set state to ready if image set has not been set to invalid during regeneration
        ImageSet.objects.filter(pk=imageset.pk, zip_state=ImageSet.ZipState.PROCESSING) \
            .update(zip_state=ImageSet.ZipState.READY)
Example #9
0
def download_tool(request, tool_id):
    tool = get_object_or_404(Tool, id=tool_id)
    if tool.has_perm('download_tool', request.user):
        tools_dir = root().opendir(settings.TOOLS_PATH)
        if tool.filename and tools_dir.isfile(tool.filename):
            with tools_dir.open(tool.filename, 'rb') as fh:
                response = HttpResponse(fh.read(), content_type="application")
                response[
                    'Content-Disposition'] = 'attachment; filename=' + tool.filename
                return response
        else:
            messages.error(request, 'There was an error accessing the tool')
    else:
        messages.error(request,
                       'You do not have the permission to download the tool!')
    return redirect(reverse('tools:overview'))
Example #10
0
def edit_tool(request, tool_id):
    if request.method == 'POST':
        tool = get_object_or_404(Tool, id=tool_id)
        changed_name = False
        changed_description = False
        changed_public = False
        changed_file = False
        public = 'public' in request.POST.keys()
        if tool.name != request.POST['name']:
            tool.name = request.POST['name']
            changed_name = True
        if tool.description != request.POST['description']:
            tool.description = request.POST['description']
            changed_description = True
        if tool.public != public:
            tool.public = public
            changed_public = True
        file_form = FileUploadForm(request.POST, request.FILES)
        if file_form.is_valid() and 'file' in request.FILES.keys():
            changed_file = True
            tools_dir = root().opendir(settings.TOOLS_PATH)
            old_name = tool.filename
            tool.filename = request.FILES['file'].name
            tools_dir.remove(old_name)
            with tools_dir.open(tool.filename, 'wb+') as f:
                for chunk in request.FILES['file']:
                    f.write(chunk)
        tool.save()
        msg = 'changed'
        if changed_name:
            msg += ' name'
        if changed_description:
            msg += ' description'
        if changed_public:
            msg += ' public status'
        if changed_file:
            msg += ' file'
        if not (changed_public or changed_description or changed_file
                or changed_name):
            msg += ' nothing'
        msg += ' in the tool'
        messages.warning(request, msg)
        return redirect(reverse('tools:overview'))
Example #11
0
def upload_image(request, imageset_id):
    imageset = get_object_or_404(ImageSet, id=imageset_id)
    imageset_dir = root().makedirs(imageset.root_path(), recreate=True)
    if request.method == 'POST' \
            and imageset.has_perm('edit_set', request.user) \
            and not imageset.image_lock:
        if request.FILES is None:
            return HttpResponseBadRequest('Must have files attached!')
        json_files = []
        for uploaded_file in request.FILES.getlist('files[]'):
            error = {
                'duplicates': 0,
                'damaged': False,
                'directories': False,
                'exists': False,
                'unsupported': False,
                'zip': False,
            }
            magic_number = uploaded_file.read(4)
            uploaded_file.seek(
                0)  # reset file cursor to the beginning of the file
            if magic_number == b'PK\x03\x04':  # ZIP file magic number
                error['zip'] = True
                with ReadZipFS(uploaded_file) as zip_fs:
                    try:
                        # Deal with weird structure of zip files created with macOS Finder.
                        subfolder = zip_fs.listdir("__MACOSX")[0]
                        zip_fs = zip_fs.opendir(subfolder)
                    except ResourceNotFound:
                        pass
                    filenames = sorted(
                        f for f in zip_fs.walk.files(path="", max_depth=0))
                    duplicate_count = 0
                    for filename in filenames:
                        image_file = zip_fs.open(filename, 'rb')
                        try:
                            if imghdr.what(
                                    image_file) in settings.IMAGE_EXTENSION:
                                image_file.seek(0)
                                # creates a checksum for image
                                fchecksum = hashlib.sha512()
                                while True:
                                    buf = image_file.read(10000)
                                    if not buf:
                                        break
                                    fchecksum.update(buf)
                                image_file.seek(0)
                                fchecksum = fchecksum.digest()
                                # Tests for duplicates in imageset
                                if Image.objects.filter(
                                        checksum=fchecksum,
                                        image_set=imageset).count() == 0:
                                    (shortname, extension) = splitext(filename)
                                    img_fname = (
                                        ''.join(shortname) + '_' + ''.join(
                                            random.choice(
                                                string.ascii_uppercase +
                                                string.ascii_lowercase +
                                                string.digits)
                                            for _ in range(6)) + extension)
                                    try:
                                        with PIL_Image.open(
                                                image_file) as image:
                                            width, height = image.size
                                        image_file.seek(0)
                                        imageset_dir.upload(
                                            img_fname, image_file)
                                        # TODO rfrigg: Check if necessary
                                        # shutil.chown(file_new_path, group=settings.UPLOAD_FS_GROUP)
                                        new_image = Image(name=filename,
                                                          image_set=imageset,
                                                          filename=img_fname,
                                                          checksum=fchecksum,
                                                          width=width,
                                                          height=height)
                                        new_image.save()
                                    except (OSError, IOError):
                                        error['damaged'] = True
                                else:
                                    duplicate_count = duplicate_count + 1
                            else:
                                error['unsupported'] = True
                        except IsADirectoryError:
                            error['directories'] = True
                        finally:
                            image_file.close()

                    if duplicate_count > 0:
                        error['duplicates'] = duplicate_count
            else:
                # creates a checksum for image
                fchecksum = hashlib.sha512()
                for chunk in uploaded_file.chunks():
                    fchecksum.update(chunk)
                fchecksum = fchecksum.digest()
                # tests for duplicats in  imageset
                if Image.objects.filter(checksum=fchecksum, image_set=imageset)\
                        .count() == 0:
                    fname = uploaded_file.name.split('.')
                    fname = ('_'.join(fname[:-1]) + '_' + ''.join(
                        random.choice(string.ascii_uppercase +
                                      string.ascii_lowercase + string.digits)
                        for _ in range(6)) + '.' + fname[-1])
                    image = Image(name=uploaded_file.name,
                                  image_set=imageset,
                                  filename=fname,
                                  checksum=fchecksum)
                    buffer = BytesIO()
                    for chunk in uploaded_file.chunks():
                        buffer.write(chunk)
                    buffer.seek(0)
                    # TODO rfrigg: Check if necessary
                    # shutil.chown(image.path(), group=settings.UPLOAD_FS_GROUP)
                    if imghdr.what(buffer) in settings.IMAGE_EXTENSION:
                        try:
                            buffer.seek(0)
                            with PIL_Image.open(buffer) as pil_image:
                                width, height = pil_image.size
                            image.height = height
                            image.width = width
                            image.save()
                            buffer.seek(0)
                            imageset_dir.upload(fname, buffer)
                        except (OSError, IOError):
                            error['damaged'] = True
                    else:
                        error['unsupported'] = True
                else:
                    error['exists'] = True
            errormessage = ''
            if error['zip']:
                errors = list()
                if error['directories']:
                    errors.append('directories')
                if error['unsupported']:
                    errors.append('unsupported files')
                if error['duplicates'] > 0:
                    errors.append(str(error['duplicates']) + ' duplicates')
                if error['damaged']:
                    errors.append('damaged files')
                if len(errors) > 0:
                    # Build beautiful error message
                    errormessage += ', '.join(
                        errors) + ' in the archive have been skipped!'
                    p = errormessage.rfind(',')
                    if p != -1:
                        errormessage = errormessage[:p].capitalize(
                        ) + ' and' + errormessage[p + 1:]
                    else:
                        errormessage = errormessage.capitalize()
            else:
                if error['unsupported']:
                    errormessage = 'This file type is unsupported!'
                elif error['damaged']:
                    errormessage = 'This file seems to be damaged!'
                elif error['exists']:
                    errormessage = 'This image already exists in the imageset!'
            if errormessage == '':
                json_files.append({
                    'name': uploaded_file.name,
                    'size': uploaded_file.size,
                    # 'url': reverse('images_imageview', args=(image.id, )),
                    # 'thumbnailUrl': reverse('images_imageview', args=(image.id, )),
                    # 'deleteUrl': reverse('images_imagedeleteview', args=(image.id, )),
                    # 'deleteType': "DELETE",
                })
            else:
                json_files.append({
                    'name': uploaded_file.name,
                    'size': uploaded_file.size,
                    'error': errormessage,
                })

        return JsonResponse({'files': json_files})