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:
jayme-github 2020-12-03 20:12:55 +01:00
parent 72a4ff0fca
commit 26784a5325
10 changed files with 116 additions and 42 deletions

View File

@ -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 = "*"

View File

@ -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>

View File

@ -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" }
} }
} }

View File

@ -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>

View File

@ -13,14 +13,14 @@ import { ToastService } from 'src/app/services/toast.service';
}) })
export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> { export class TagEditDialogComponent extends EditDialogComponent<PaperlessTag> {
constructor(service: TagService, activeModal: NgbActiveModal, toastService: ToastService) { constructor(service: TagService, activeModal: NgbActiveModal, toastService: ToastService) {
super(service, activeModal, toastService, 'tag') super(service, activeModal, toastService, 'tag')
} }
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)
} }

View File

@ -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">

View File

@ -15,10 +15,14 @@ export class TagListComponent extends GenericListComponent<PaperlessTag> {
constructor(tagService: TagService, modalService: NgbModal) { constructor(tagService: TagService, modalService: NgbModal) {
super(tagService, modalService, TagEditDialogComponent) super(tagService, modalService, TagEditDialogComponent)
} }
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) {

View File

@ -3,26 +3,27 @@ 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
document_count?: number document_count?: number
} }

View 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',
)
]

View File

@ -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):