mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	more metadata #32
This commit is contained in:
		| @@ -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. |   * 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 file upload box now shows upload progress. | ||||||
|   * The document edit page was reorganized. |   * The document edit page was reorganized. | ||||||
|  |   * The document edit page shows various information about a document. | ||||||
|   * Table issues with too long document titles fixed. |   * Table issues with too long document titles fixed. | ||||||
|  |  | ||||||
| * API | * API | ||||||
|   | |||||||
| @@ -47,6 +47,7 @@ import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload | |||||||
| import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'; | import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component'; | ||||||
| import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'; | import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component'; | ||||||
| import { YesNoPipe } from './pipes/yes-no.pipe'; | import { YesNoPipe } from './pipes/yes-no.pipe'; | ||||||
|  | import { FileSizePipe } from './pipes/file-size.pipe'; | ||||||
|  |  | ||||||
| @NgModule({ | @NgModule({ | ||||||
|   declarations: [ |   declarations: [ | ||||||
| @@ -86,7 +87,8 @@ import { YesNoPipe } from './pipes/yes-no.pipe'; | |||||||
|     UploadFileWidgetComponent, |     UploadFileWidgetComponent, | ||||||
|     WidgetFrameComponent, |     WidgetFrameComponent, | ||||||
|     WelcomeWidgetComponent, |     WelcomeWidgetComponent, | ||||||
|     YesNoPipe |     YesNoPipe, | ||||||
|  |     FileSizePipe | ||||||
|   ], |   ], | ||||||
|   imports: [ |   imports: [ | ||||||
|     BrowserModule, |     BrowserModule, | ||||||
|   | |||||||
| @@ -83,25 +83,29 @@ | |||||||
|                                     <td>Date added</td> |                                     <td>Date added</td> | ||||||
|                                     <td>{{document.added | date}}</td> |                                     <td>{{document.added | date}}</td> | ||||||
|                                 </tr> |                                 </tr> | ||||||
|  |                                 <tr> | ||||||
|  |                                     <td>Media filename</td> | ||||||
|  |                                     <td>{{metadata?.media_filename}}</td> | ||||||
|  |                                 </tr> | ||||||
|                                 <tr> |                                 <tr> | ||||||
|                                     <td>Original MD5 Checksum</td> |                                     <td>Original MD5 Checksum</td> | ||||||
|                                     <td>{{metadata?.original_checksum}}</td> |                                     <td>{{metadata?.original_checksum}}</td> | ||||||
|                                 </tr> |                                 </tr> | ||||||
|                                 <tr> |                                 <tr> | ||||||
|                                     <td>Archive MD5 Checksum</td> |                                     <td>Original file size</td> | ||||||
|                                     <td>{{metadata?.archived_checksum}}</td> |                                     <td>{{metadata?.original_size | fileSize}}</td> | ||||||
|                                 </tr> |                                 </tr> | ||||||
|                                 <tr> |                                 <tr> | ||||||
|                                     <td>Original mime type</td> |                                     <td>Original mime type</td> | ||||||
|                                     <td>{{metadata?.original_mime_type}}</td> |                                     <td>{{metadata?.original_mime_type}}</td> | ||||||
|                                 </tr> |                                 </tr> | ||||||
|                                 <tr> |                                 <tr *ngIf="metadata?.has_archive_version"> | ||||||
|                                     <td>Is archived?</td> |                                     <td>Archive MD5 Checksum</td> | ||||||
|                                     <td>{{metadata?.has_archive_version | yesno}}</td> |                                     <td>{{metadata?.archive_checksum}}</td> | ||||||
|                                 </tr> |                                 </tr> | ||||||
|                                 <tr> |                                 <tr *ngIf="metadata?.has_archive_version"> | ||||||
|                                     <td>Media filename</td> |                                     <td>Archive file size</td> | ||||||
|                                     <td>{{metadata?.media_filename}}</td> |                                     <td>{{metadata?.archive_size | fileSize}}</td> | ||||||
|                                 </tr> |                                 </tr> | ||||||
|                             </tbody> |                             </tbody> | ||||||
|                         </table> |                         </table> | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								src-ui/src/app/pipes/file-size.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src-ui/src/app/pipes/file-size.pipe.spec.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | import { FileSizePipe } from './file-size.pipe'; | ||||||
|  |  | ||||||
|  | describe('FileSizePipe', () => { | ||||||
|  |   it('create an instance', () => { | ||||||
|  |     const pipe = new FileSizePipe(); | ||||||
|  |     expect(pipe).toBeTruthy(); | ||||||
|  |   }); | ||||||
|  | }); | ||||||
							
								
								
									
										77
									
								
								src-ui/src/app/pipes/file-size.pipe.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src-ui/src/app/pipes/file-size.pipe.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -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}`; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -496,7 +496,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase): | |||||||
|         async_task.assert_not_called() |         async_task.assert_not_called() | ||||||
|  |  | ||||||
|     def test_get_metadata(self): |     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", "documents", "thumbnails", "0000001.png"), doc.source_path) | ||||||
|         shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), doc.archive_path) |         shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), doc.archive_path) | ||||||
|   | |||||||
| @@ -196,17 +196,28 @@ class DocumentViewSet(RetrieveModelMixin, | |||||||
|     def metadata(self, request, pk=None): |     def metadata(self, request, pk=None): | ||||||
|         try: |         try: | ||||||
|             doc = Document.objects.get(pk=pk) |             doc = Document.objects.get(pk=pk) | ||||||
|             return Response({ |  | ||||||
|  |             meta = { | ||||||
|                 "original_checksum": doc.checksum, |                 "original_checksum": doc.checksum, | ||||||
|                 "archived_checksum": doc.archive_checksum, |                 "original_size": os.stat(doc.source_path).st_size, | ||||||
|                 "original_mime_type": doc.mime_type, |                 "original_mime_type": doc.mime_type, | ||||||
|                 "media_filename": doc.filename, |                 "media_filename": doc.filename, | ||||||
|                 "has_archive_version": os.path.isfile(doc.archive_path), |                 "has_archive_version": os.path.isfile(doc.archive_path), | ||||||
|                 "original_metadata": self.get_metadata( |                 "original_metadata": self.get_metadata( | ||||||
|                     doc.source_path, doc.mime_type), |                     doc.source_path, doc.mime_type) | ||||||
|                 "archive_metadata": self.get_metadata( |             } | ||||||
|  |  | ||||||
|  |             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") |                     doc.archive_path, "application/pdf") | ||||||
|             }) |             else: | ||||||
|  |                 meta['archive_checksum'] = None | ||||||
|  |                 meta['archive_size'] = None | ||||||
|  |                 meta['archive_metadata'] = None | ||||||
|  |  | ||||||
|  |             return Response(meta) | ||||||
|         except Document.DoesNotExist: |         except Document.DoesNotExist: | ||||||
|             raise Http404() |             raise Http404() | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 jonaswinkler
					jonaswinkler