mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-30 03:56:23 -05:00 
			
		
		
		
	Add automatic coloring of tags
Please see this as proposal on how to implement automatic/random colors for tags while keeping the current set of hard coded colors in place (at least in the frontend). Bear with me as I have even less Angular knowledge than Django and just tried to get away with as few changes as possible. :-) AIUI you want to change to a color picking system anyways in the future, which could also play well with this. fixes #51
This commit is contained in:
		
							
								
								
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Pipfile
									
									
									
									
									
								
							| @@ -40,6 +40,7 @@ whoosh="~=2.7.4" | |||||||
| inotifyrecursive = ">=0.3.4" | inotifyrecursive = ">=0.3.4" | ||||||
| ocrmypdf = "*" | ocrmypdf = "*" | ||||||
| tqdm = "*" | tqdm = "*" | ||||||
|  | colorhash = "*" | ||||||
|  |  | ||||||
| [dev-packages] | [dev-packages] | ||||||
| coveralls = "*" | coveralls = "*" | ||||||
|   | |||||||
| @@ -1,2 +1,2 @@ | |||||||
| <span *ngIf="!clickable" class="badge" [style.background]="getColour().value" [style.color]="getColour().textColor">{{tag.name}}</span> | <span *ngIf="!clickable" class="badge" [style.background]="getColour().id" [style.color]="getColour().textColor">{{tag.name}}</span> | ||||||
| <a [routerLink]="" [title]="linkTitle" *ngIf="clickable" class="badge" [style.background]="getColour().value" [style.color]="getColour().textColor">{{tag.name}}</a> | <a [routerLink]="" [title]="linkTitle" *ngIf="clickable" class="badge" [style.background]="getColour().id" [style.color]="getColour().textColor">{{tag.name}}</a> | ||||||
| @@ -23,7 +23,11 @@ export class TagComponent implements OnInit { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   getColour() { |   getColour() { | ||||||
|     return TAG_COLOURS.find(c => c.id == this.tag.colour) |     var color = TAG_COLOURS.find(c => c.id == this.tag.colour) | ||||||
|  |     if (color) { | ||||||
|  |       return color | ||||||
|  |     } | ||||||
|  |     return { id: this.tag.colour, name: this.tag.colour, textColor: "#ffffff" } | ||||||
|   } |   } | ||||||
|  |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ | |||||||
|     </div> |     </div> | ||||||
|     <div class="modal-body"> |     <div class="modal-body"> | ||||||
|       <app-input-text title="Name" formControlName="name"></app-input-text> |       <app-input-text title="Name" formControlName="name"></app-input-text> | ||||||
|       <app-input-select title="Colour" [items]="getColours()" formControlName="colour" [textColor]="getColor(objectForm.value.colour).textColor" [backgroundColor]="getColor(objectForm.value.colour).value"></app-input-select> |       <app-input-select title="Colour" [items]="getColours()" formControlName="colour" [textColor]="getColor(objectForm.value.colour).textColor" [backgroundColor]="getColor(objectForm.value.colour).id"></app-input-select> | ||||||
|       <app-input-check title="Inbox tag" formControlName="is_inbox_tag" hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check> |       <app-input-check title="Inbox tag" formControlName="is_inbox_tag" hint="Inbox tags are automatically assigned to all consumed documents."></app-input-check> | ||||||
|       <app-input-text title="Match" formControlName="match"></app-input-text> |       <app-input-text title="Match" formControlName="match"></app-input-text> | ||||||
|       <app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> |       <app-input-select title="Matching algorithm" [items]="getMatchingAlgorithms()" formControlName="matching_algorithm"></app-input-select> | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> { | |||||||
|   getForm(): FormGroup { |   getForm(): FormGroup { | ||||||
|     return new FormGroup({ |     return new FormGroup({ | ||||||
|       name: new FormControl(''), |       name: new FormControl(''), | ||||||
|       colour: new FormControl(1), |       colour: new FormControl(''), | ||||||
|       is_inbox_tag: new FormControl(false), |       is_inbox_tag: new FormControl(false), | ||||||
|       matching_algorithm: new FormControl(1), |       matching_algorithm: new FormControl(1), | ||||||
|       match: new FormControl(""), |       match: new FormControl(""), | ||||||
| @@ -32,7 +32,7 @@ export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> { | |||||||
|     return TAG_COLOURS |     return TAG_COLOURS | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getColor(id: number) { |   getColor(id) { | ||||||
|     return TAG_COLOURS.find(c => c.id == id) |     return TAG_COLOURS.find(c => c.id == id) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,7 +23,7 @@ | |||||||
|     <tr *ngFor="let tag of data"> |     <tr *ngFor="let tag of data"> | ||||||
|       <td scope="row">{{ tag.name }}</td> |       <td scope="row">{{ tag.name }}</td> | ||||||
|       <td scope="row"><span class="badge" [style.color]="getColor(tag.colour).textColor" |       <td scope="row"><span class="badge" [style.color]="getColor(tag.colour).textColor" | ||||||
|           [style.background-color]="getColor(tag.colour).value">{{ getColor(tag.colour).name }}</span></td> |           [style.background-color]="tag.colour">{{ getColor(tag.colour).name }}</span></td> | ||||||
|       <td scope="row">{{ getMatching(tag) }}</td> |       <td scope="row">{{ getMatching(tag) }}</td> | ||||||
|       <td scope="row">{{ tag.document_count }}</td> |       <td scope="row">{{ tag.document_count }}</td> | ||||||
|       <td scope="row"> |       <td scope="row"> | ||||||
|   | |||||||
| @@ -18,7 +18,11 @@ export class TagListComponent extends GenericListComponent<PaperlessTag> { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   getColor(id) { |   getColor(id) { | ||||||
|     return TAG_COLOURS.find(c => c.id == id) |     var color = TAG_COLOURS.find(c => c.id == id) | ||||||
|  |     if (color) { | ||||||
|  |       return color | ||||||
|  |     } | ||||||
|  |     return { id: id, name: id, textColor: "#ffffff" } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   getObjectName(object: PaperlessTag) { |   getObjectName(object: PaperlessTag) { | ||||||
|   | |||||||
| @@ -3,24 +3,25 @@ import { ObjectWithId } from './object-with-id'; | |||||||
|  |  | ||||||
|  |  | ||||||
| export const TAG_COLOURS = [ | export const TAG_COLOURS = [ | ||||||
|     {id: 1, value: "#a6cee3", name: "Light Blue", textColor: "#000000"}, |     { id: "", name: "Auto", textColor: "#000000" }, | ||||||
|     {id: 2, value: "#1f78b4", name: "Blue", textColor: "#ffffff"}, |     { id: "#a6cee3", name: "Light Blue", textColor: "#000000" }, | ||||||
|     {id: 3, value: "#b2df8a", name: "Light Green", textColor: "#000000"}, |     { id: "#1f78b4", name: "Blue", textColor: "#ffffff" }, | ||||||
|     {id: 4, value: "#33a02c", name: "Green", textColor: "#000000"}, |     { id: "#b2df8a", name: "Light Green", textColor: "#000000" }, | ||||||
|     {id: 5, value: "#fb9a99", name: "Light Red", textColor: "#000000"}, |     { id: "#33a02c", name: "Green", textColor: "#000000" }, | ||||||
|     {id: 6, value: "#e31a1c", name: "Red ", textColor: "#ffffff"}, |     { id: "#fb9a99", name: "Light Red", textColor: "#000000" }, | ||||||
|     {id: 7, value: "#fdbf6f", name: "Light Orange", textColor: "#000000"}, |     { id: "#e31a1c", name: "Red ", textColor: "#ffffff" }, | ||||||
|     {id: 8, value: "#ff7f00", name: "Orange", textColor: "#000000"}, |     { id: "#fdbf6f", name: "Light Orange", textColor: "#000000" }, | ||||||
|     {id: 9, value: "#cab2d6", name: "Light Violet", textColor: "#000000"}, |     { id: "#ff7f00", name: "Orange", textColor: "#000000" }, | ||||||
|     {id: 10, value: "#6a3d9a", name: "Violet", textColor: "#ffffff"}, |     { id: "#cab2d6", name: "Light Violet", textColor: "#000000" }, | ||||||
|     {id: 11, value: "#b15928", name: "Brown", textColor: "#000000"}, |     { id: "#6a3d9a", name: "Violet", textColor: "#ffffff" }, | ||||||
|     {id: 12, value: "#000000", name: "Black", textColor: "#ffffff"}, |     { id: "#b15928", name: "Brown", textColor: "#000000" }, | ||||||
|     {id: 13, value: "#cccccc", name: "Light Grey", textColor: "#000000"} |     { id: "#000000", name: "Black", textColor: "#ffffff" }, | ||||||
|  |     { id: "#cccccc", name: "Light Grey", textColor: "#000000" } | ||||||
| ] | ] | ||||||
|  |  | ||||||
| export interface PaperlessTag extends MatchingModel { | export interface PaperlessTag extends MatchingModel { | ||||||
|  |  | ||||||
|     colour?: number |     colour?: string | ||||||
|  |  | ||||||
|     is_inbox_tag?: boolean |     is_inbox_tag?: boolean | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										70
									
								
								src/documents/migrations/1006_migrate_tag_colour.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								src/documents/migrations/1006_migrate_tag_colour.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | # Generated by Django 3.1.4 on 2020-12-02 21:43 | ||||||
|  |  | ||||||
|  | from django.db import migrations, models | ||||||
|  |  | ||||||
|  | COLOURS_OLD = { | ||||||
|  |     1: "#a6cee3", | ||||||
|  |     2: "#1f78b4", | ||||||
|  |     3: "#b2df8a", | ||||||
|  |     4: "#33a02c", | ||||||
|  |     5: "#fb9a99", | ||||||
|  |     6: "#e31a1c", | ||||||
|  |     7: "#fdbf6f", | ||||||
|  |     8: "#ff7f00", | ||||||
|  |     9: "#cab2d6", | ||||||
|  |     10: "#6a3d9a", | ||||||
|  |     11: "#b15928", | ||||||
|  |     12: "#000000", | ||||||
|  |     13: "#cccccc", | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def forward(apps, schema_editor): | ||||||
|  |     Tag = apps.get_model('documents', 'Tag') | ||||||
|  |  | ||||||
|  |     for tag in Tag.objects.all(): | ||||||
|  |         colour_old_id = tag.colour_old | ||||||
|  |         rgb = COLOURS_OLD[colour_old_id] | ||||||
|  |         tag.colour = rgb | ||||||
|  |         tag.save() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def reverse(apps, schema_editor): | ||||||
|  |     Tag = apps.get_model('documents', 'Tag') | ||||||
|  |  | ||||||
|  |     def _get_colour_id(rdb): | ||||||
|  |         for idx, rdbx in COLOURS_OLD.items(): | ||||||
|  |             if rdbx == rdb: | ||||||
|  |                 return idx | ||||||
|  |         # Return colour 1 if we can't match anything | ||||||
|  |         return 1 | ||||||
|  |  | ||||||
|  |     for tag in Tag.objects.all(): | ||||||
|  |         colour_id = _get_colour_id(tag.colour) | ||||||
|  |         tag.colour_old = colour_id | ||||||
|  |         tag.save() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class Migration(migrations.Migration): | ||||||
|  |  | ||||||
|  |     dependencies = [ | ||||||
|  |         ('documents', '1005_checksums'), | ||||||
|  |     ] | ||||||
|  |  | ||||||
|  |     operations = [ | ||||||
|  |         migrations.RenameField( | ||||||
|  |             model_name='tag', | ||||||
|  |             old_name='colour', | ||||||
|  |             new_name='colour_old', | ||||||
|  |         ), | ||||||
|  |         migrations.AddField( | ||||||
|  |             model_name='tag', | ||||||
|  |             name='colour', | ||||||
|  |             field=models.CharField(blank=True, max_length=7), | ||||||
|  |         ), | ||||||
|  |         migrations.RunPython(forward, reverse), | ||||||
|  |         migrations.RemoveField( | ||||||
|  |             model_name='tag', | ||||||
|  |             name='colour_old', | ||||||
|  |         ) | ||||||
|  |     ] | ||||||
| @@ -6,6 +6,7 @@ import re | |||||||
| from collections import OrderedDict | from collections import OrderedDict | ||||||
|  |  | ||||||
| import dateutil.parser | import dateutil.parser | ||||||
|  | from colorhash import ColorHash | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.db import models | from django.db import models | ||||||
| from django.utils import timezone | from django.utils import timezone | ||||||
| @@ -84,23 +85,7 @@ class Correspondent(MatchingModel): | |||||||
|  |  | ||||||
| class Tag(MatchingModel): | class Tag(MatchingModel): | ||||||
|  |  | ||||||
|     COLOURS = ( |     colour = models.CharField(blank=True, max_length=7) | ||||||
|         (1, "#a6cee3"), |  | ||||||
|         (2, "#1f78b4"), |  | ||||||
|         (3, "#b2df8a"), |  | ||||||
|         (4, "#33a02c"), |  | ||||||
|         (5, "#fb9a99"), |  | ||||||
|         (6, "#e31a1c"), |  | ||||||
|         (7, "#fdbf6f"), |  | ||||||
|         (8, "#ff7f00"), |  | ||||||
|         (9, "#cab2d6"), |  | ||||||
|         (10, "#6a3d9a"), |  | ||||||
|         (11, "#b15928"), |  | ||||||
|         (12, "#000000"), |  | ||||||
|         (13, "#cccccc") |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     colour = models.PositiveIntegerField(choices=COLOURS, default=1) |  | ||||||
|  |  | ||||||
|     is_inbox_tag = models.BooleanField( |     is_inbox_tag = models.BooleanField( | ||||||
|         default=False, |         default=False, | ||||||
| @@ -108,6 +93,15 @@ class Tag(MatchingModel): | |||||||
|                   "documents will be tagged with inbox tags." |                   "documents will be tagged with inbox tags." | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |     def save(self, *args, **kwargs): | ||||||
|  |         if self.colour == "": | ||||||
|  |             self.colour = ColorHash( | ||||||
|  |                 self.name, | ||||||
|  |                 lightness=(0.35, 0.45, 0.55, 0.65), | ||||||
|  |                 saturation=(0.2, 0.3, 0.4, 0.5)).hex | ||||||
|  |  | ||||||
|  |         super().save(*args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| class DocumentType(MatchingModel): | class DocumentType(MatchingModel): | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 jayme-github
					jayme-github