Example #1
0
class Upload(Model):
    id: int
    event: ForeignKey = ForeignKey(get_event_model(), on_delete=RESTRICT)
    material_id: CharField = CharField(max_length=32)
    filename: CharField = CharField(max_length=128)
    sha256: CharField = CharField(max_length=64, blank=True, null=True)
    size: IntegerField = IntegerField()
    uploader: ForeignKey = ForeignKey(get_user_model(),
                                      on_delete=RESTRICT,
                                      blank=True,
                                      null=True)  # FIXME: auth
    created: DateTimeField = DateTimeField(auto_now_add=True)
    updated: DateTimeField = DateTimeField(auto_now=True)
    uploaded: DateTimeField = DateTimeField(blank=True, null=True)
    deleted: DateTimeField = DateTimeField(blank=True, null=True)

    @property
    def state(self) -> str:
        if self.deleted:
            return UploadStates.DELETED
        if self.uploaded:
            return UploadStates.UPLOADED
        return UploadStates.CREATED

    @property
    def material(self) -> Material:
        return get_material(self.material_id)

    @property
    def storage_path(self) -> str:
        extension = self.filename.rsplit('.', 1)[-1]
        return f'{self.event.slug}-{self.material_id}-{self.id}.{extension}'
Example #2
0
 def setUp(self):
     super().setUp()
     self.event = get_event_model().objects.create(title='test',
                                                   slug='test')
     self.upload = Upload.objects.create(event=self.event,
                                         material_id='slides',
                                         filename='test.pdf',
                                         size=1024)
     self.args = make_args(resumableIdentifier=self.upload.id)
Example #3
0
class EventDetailView(DetailView):
    model = get_event_model()
    template_name = 'materials/event_detail.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        event = context['object']
        current_uploads = []
        for mat_set in settings.MATERIALS:
            material = mat_set.copy()
            try:
                material['upload'] = Upload.objects.get(
                    event=event,
                    deleted__isnull=True,
                    material_id=material['id'])
            except Upload.DoesNotExist:
                material['upload'] = None
            current_uploads.append(material)

        context['current_uploads'] = current_uploads
        context['deleted_uploads'] = Upload.objects.filter(
            event=event).exclude(deleted__isnull=True).order_by('-created')
        return context
Example #4
0
class ResumableUploadView(BaseDetailView):
    model = get_event_model()

    def dispatch(self, request, *args, **kwargs):
        self.event = self.get_object()
        self.material = self.kwargs['material']
        return super().dispatch(request, *args, **kwargs)

    def get(self, *args, **kwargs):
        """Resumable.js checking if a chunk is already fully uploaded.
        200 = yes, otherwise no.
        """
        resumable_upload = ResumableUpload.from_query_string(
            self.request, self.event)
        if resumable_upload.chunk_exists(self.request):
            return HttpResponse(status=200)
        # Not 200 but not an error to avoid JS console logging
        return HttpResponse(status=204)

    def post(self, *args, **kwargs):
        # Our Resumable.js wrapper: upload initiation & completion
        action = self.request.POST.get('action')
        if action == 'create':
            return self.create_upload(filename=self.request.POST['filename'],
                                      size=self.request.POST['size'])
        elif action == 'complete':
            return self.complete_upload(self.request.POST['identifier'])
        else:
            return self.save_chunk()

    def create_upload(self, filename, size):
        """Prepare to receive an upload"""
        # TODO validate size against settings

        # Supersede existing uploads
        for upload in Upload.objects.filter(event=self.event,
                                            material_id=self.material,
                                            deleted__isnull=True):
            if upload.state == UploadStates.CREATED:
                ResumableUpload(upload).delete_upload_chunks()
            upload.deleted = timezone.now()
            upload.save()

        upload = Upload(
            event=self.event,
            material_id=self.material,
            filename=filename,
            size=size,
        )
        upload.save()

        return JsonResponse({
            'identifier': upload.id,
        }, status=201)

    def save_chunk(self):
        resumable_upload = ResumableUpload.from_query_string(
            self.request, self.event)
        resumable_upload.save_chunk(self.request)
        return HttpResponse(status=201)

    def complete_upload(self, identifier):
        """Combine all the chunks and hash them"""
        upload = Upload.objects.get(id=identifier, event=self.event)
        resumable_upload = ResumableUpload(upload)
        resumable_upload.complete_upload()
        return JsonResponse({
            'state': upload.state,
            'sha256': upload.sha256,
        },
                            status=201)
Example #5
0
class EventListView(ListView):
    model = get_event_model()
    template_name = 'materials/event_list.html'
Example #6
0
 def setUp(self):
     super().setUp()
     get_event_model().objects.create(title='test', slug='test')