From 0028fde2fd4e4d1db64d537a7f784f8ce1272c38 Mon Sep 17 00:00:00 2001 From: jonaswinkler Date: Tue, 8 Dec 2020 16:09:47 +0100 Subject: [PATCH] more metadata #32 --- docs/changelog.rst | 1 + src-ui/src/app/app.module.ts | 4 +- .../document-detail.component.html | 20 +++-- src-ui/src/app/pipes/file-size.pipe.spec.ts | 8 ++ src-ui/src/app/pipes/file-size.pipe.ts | 77 +++++++++++++++++++ src/documents/tests/test_api.py | 2 +- src/documents/views.py | 21 +++-- 7 files changed, 118 insertions(+), 15 deletions(-) create mode 100644 src-ui/src/app/pipes/file-size.pipe.spec.ts create mode 100644 src-ui/src/app/pipes/file-size.pipe.ts diff --git a/docs/changelog.rst b/docs/changelog.rst index 2e3ed07f6..b6c88be92 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -19,6 +19,7 @@ This release focusses primarily on many small issues with the UI. * There's a new filter to filter for documents that do *not* have a certain tag. * The file upload box now shows upload progress. * The document edit page was reorganized. + * The document edit page shows various information about a document. * Table issues with too long document titles fixed. * API diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts index e186cde50..ad12c9c47 100644 --- a/src-ui/src/app/app.module.ts +++ b/src-ui/src/app/app.module.ts @@ -47,6 +47,7 @@ import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'; import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'; import { YesNoPipe } from './pipes/yes-no.pipe'; +import { FileSizePipe } from './pipes/file-size.pipe'; @NgModule({ declarations: [ @@ -86,7 +87,8 @@ import { YesNoPipe } from './pipes/yes-no.pipe'; UploadFileWidgetComponent, WidgetFrameComponent, WelcomeWidgetComponent, - YesNoPipe + YesNoPipe, + FileSizePipe ], imports: [ BrowserModule, diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html index e905c35e6..774ea8869 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.html +++ b/src-ui/src/app/components/document-detail/document-detail.component.html @@ -83,25 +83,29 @@ Date added {{document.added | date}} + + Media filename + {{metadata?.media_filename}} + Original MD5 Checksum {{metadata?.original_checksum}} - Archive MD5 Checksum - {{metadata?.archived_checksum}} + Original file size + {{metadata?.original_size | fileSize}} Original mime type {{metadata?.original_mime_type}} - - Is archived? - {{metadata?.has_archive_version | yesno}} + + Archive MD5 Checksum + {{metadata?.archive_checksum}} - - Media filename - {{metadata?.media_filename}} + + Archive file size + {{metadata?.archive_size | fileSize}} diff --git a/src-ui/src/app/pipes/file-size.pipe.spec.ts b/src-ui/src/app/pipes/file-size.pipe.spec.ts new file mode 100644 index 000000000..8c7a39d22 --- /dev/null +++ b/src-ui/src/app/pipes/file-size.pipe.spec.ts @@ -0,0 +1,8 @@ +import { FileSizePipe } from './file-size.pipe'; + +describe('FileSizePipe', () => { + it('create an instance', () => { + const pipe = new FileSizePipe(); + expect(pipe).toBeTruthy(); + }); +}); diff --git a/src-ui/src/app/pipes/file-size.pipe.ts b/src-ui/src/app/pipes/file-size.pipe.ts new file mode 100644 index 000000000..7d742c876 --- /dev/null +++ b/src-ui/src/app/pipes/file-size.pipe.ts @@ -0,0 +1,77 @@ +/** + * https://gist.github.com/JonCatmull/ecdf9441aaa37336d9ae2c7f9cb7289a + * + * @license + * Copyright (c) 2019 Jonathan Catmull. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import { Pipe, PipeTransform } from '@angular/core'; + +type unit = 'bytes' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB'; +type unitPrecisionMap = { + [u in unit]: number; +}; + +const defaultPrecisionMap: unitPrecisionMap = { + bytes: 0, + KB: 0, + MB: 1, + GB: 1, + TB: 2, + PB: 2 +}; + +/* + * Convert bytes into largest possible unit. + * Takes an precision argument that can be a number or a map for each unit. + * Usage: + * bytes | fileSize:precision + * @example + * // returns 1 KB + * {{ 1500 | fileSize }} + * @example + * // returns 2.1 GB + * {{ 2100000000 | fileSize }} + * @example + * // returns 1.46 KB + * {{ 1500 | fileSize:2 }} + */ +@Pipe({ name: 'fileSize' }) +export class FileSizePipe implements PipeTransform { + private readonly units: unit[] = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + + transform(bytes: number = 0, precision: number | unitPrecisionMap = defaultPrecisionMap): string { + if (isNaN(parseFloat(String(bytes))) || !isFinite(bytes)) return '?'; + + let unitIndex = 0; + + while (bytes >= 1024) { + bytes /= 1024; + unitIndex++; + } + + const unit = this.units[unitIndex]; + + if (typeof precision === 'number') { + return `${bytes.toFixed(+precision)} ${unit}`; + } + return `${bytes.toFixed(precision[unit])} ${unit}`; + } +} diff --git a/src/documents/tests/test_api.py b/src/documents/tests/test_api.py index c2f9c950c..572667406 100644 --- a/src/documents/tests/test_api.py +++ b/src/documents/tests/test_api.py @@ -496,7 +496,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase): async_task.assert_not_called() def test_get_metadata(self): - doc = Document.objects.create(title="test", filename="file.pdf", mime_type="image/png") + doc = Document.objects.create(title="test", filename="file.pdf", mime_type="image/png", archive_checksum="A") shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "thumbnails", "0000001.png"), doc.source_path) shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), doc.archive_path) diff --git a/src/documents/views.py b/src/documents/views.py index e058b0f56..8dbb61dc7 100755 --- a/src/documents/views.py +++ b/src/documents/views.py @@ -196,17 +196,28 @@ class DocumentViewSet(RetrieveModelMixin, def metadata(self, request, pk=None): try: doc = Document.objects.get(pk=pk) - return Response({ + + meta = { "original_checksum": doc.checksum, - "archived_checksum": doc.archive_checksum, + "original_size": os.stat(doc.source_path).st_size, "original_mime_type": doc.mime_type, "media_filename": doc.filename, "has_archive_version": os.path.isfile(doc.archive_path), "original_metadata": self.get_metadata( - doc.source_path, doc.mime_type), - "archive_metadata": self.get_metadata( + doc.source_path, doc.mime_type) + } + + if doc.archive_checksum and os.path.isfile(doc.archive_path): + meta['archive_checksum'] = doc.archive_checksum + meta['archive_size'] = os.stat(doc.archive_path).st_size, + meta['archive_metadata'] = self.get_metadata( doc.archive_path, "application/pdf") - }) + else: + meta['archive_checksum'] = None + meta['archive_size'] = None + meta['archive_metadata'] = None + + return Response(meta) except Document.DoesNotExist: raise Http404()