Improved statistics widget

This commit is contained in:
shamoon 2023-03-18 01:42:41 -07:00
parent 741152dd50
commit f3eedec402
7 changed files with 179 additions and 21 deletions

View File

@ -1826,7 +1826,7 @@
<source>Not assigned</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/filterable-dropdown/filterable-dropdown.component.ts</context>
<context context-type="linenumber">321</context>
<context context-type="linenumber">335</context>
</context-group>
<note priority="1" from="description">Filter drop down element to filter for documents with no correspondent/type/tag assigned</note>
</trans-unit>
@ -2202,20 +2202,48 @@
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="1171694977288479084" datatype="html">
<source>Documents in inbox: <x id="INTERPOLATION" equiv-text="{{statistics?.documents_inbox}}"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
</trans-unit>
<trans-unit id="4207135462646354574" datatype="html">
<source>Total documents: <x id="INTERPOLATION" equiv-text="{{statistics?.documents_total}}"/></source>
<trans-unit id="2028517964701399614" datatype="html">
<source>Go to inbox</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="3497361602348932709" datatype="html">
<source>Documents in inbox</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">5</context>
</context-group>
</trans-unit>
<trans-unit id="8809281703097241399" datatype="html">
<source>Go to documents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">8</context>
</context-group>
</trans-unit>
<trans-unit id="3823413855067727192" datatype="html">
<source>Total documents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">9</context>
</context-group>
</trans-unit>
<trans-unit id="6503529145162789855" datatype="html">
<source>Total characters</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="6125391559813574136" datatype="html">
<source>File types</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
<context context-type="linenumber">17</context>
</context-group>
</trans-unit>
<trans-unit id="8187573012244728580" datatype="html">
<source>Upload new documents</source>
<context-group purpose="location">

View File

@ -1,6 +1,46 @@
<app-widget-frame title="Statistics" [loading]="loading" i18n-title>
<ng-container content>
<p class="card-text" i18n *ngIf="statistics?.documents_inbox !== null">Documents in inbox: {{statistics?.documents_inbox}}</p>
<p class="card-text" i18n>Total documents: {{statistics?.documents_total}}</p>
<div class="list-group border-light">
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to inbox" i18n-title href="javascript:void()" *ngIf="statistics?.documents_inbox !== null" (click)="goToInbox()">
<ng-container i18n>Documents in inbox</ng-container>:
<span class="badge rounded-pill" [class.bg-primary]="statistics?.documents_inbox > 0" [class.bg-muted]="statistics?.documents_inbox === 0">{{statistics?.documents_inbox}}</span>
</a>
<a class="list-group-item list-group-item-action d-flex justify-content-between align-items-center" title="Go to documents" i18n-title routerLink="/documents/">
<ng-container i18n>Total documents</ng-container>:
<span class="badge bg-primary rounded-pill">{{statistics?.documents_total}}</span>
</a>
<div class="list-group-item d-flex justify-content-between align-items-center" routerLink="/documents/">
<ng-container i18n>Total characters</ng-container>:
<span class="badge bg-secondary text-light rounded-pill">{{statistics?.character_count | number}}</span>
</div>
<div class="list-group-item d-flex justify-content-between align-items-center">
<div class="flex-grow-1"><ng-container i18n>File types</ng-container>:</div>
<div class="d-flex flex-column flex-grow-1">
<div *ngFor="let filetype of statistics?.document_file_type_counts; let i = index" class="d-flex justify-content-between align-items-center">
<span class="fst-italic text-muted">{{filetype.mime_type}}</span>
<span class="badge bg-secondary text-light rounded-pill">{{getFileTypePercent(filetype) | number: '1.0-1'}}%</span>
</div>
</div>
</div>
<!-- <div class="list-group-item border-dark d-flex justify-content-between align-items-center">
<span class="me-3" i18n>File types:</span>
<div class="progress flex-grow-1">
<div *ngFor="let filetype of statistics?.document_file_type_counts; let i = index"
class="progress-bar bg-primary text-primary-contrast"
[class.me-1]="i < statistics?.document_file_type_counts.length - 1"
role="progressbar"
[ngbPopover]="filetype.mime_type"
i18n-ngbPopover
triggers="mouseenter:mouseleave"
[attr.aria-label]="filetype.mime_type"
[style.width]="getFileTypePercent(filetype) + '%'"
[attr.aria-valuenow]="getFileTypePercent(filetype)"
aria-valuemin="0"
aria-valuemax="100">
<ng-container *ngIf="getFileTypePercent(filetype) > 25">{{filetype.mime_type}}</ng-container>
</div>
</div>
</div> -->
</div>
</ng-container>
</app-widget-frame>

View File

@ -0,0 +1,3 @@
.flex-column {
row-gap: 0.2rem;
}

View File

@ -1,12 +1,25 @@
import { HttpClient } from '@angular/common/http'
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Observable, Subscription } from 'rxjs'
import {
FILTER_HAS_TAGS_ALL,
FILTER_IS_IN_INBOX,
} from 'src/app/data/filter-rule-type'
import { ConsumerStatusService } from 'src/app/services/consumer-status.service'
import { DocumentListViewService } from 'src/app/services/document-list-view.service'
import { environment } from 'src/environments/environment'
export interface Statistics {
documents_total?: number
documents_inbox?: number
inbox_tag?: number
document_file_type_counts?: DocumentFileType[]
character_count?: number
}
interface DocumentFileType {
mime_type: string
mime_type_count: number
}
@Component({
@ -19,7 +32,8 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
constructor(
private http: HttpClient,
private consumerStatusService: ConsumerStatusService
private consumerStatusService: ConsumerStatusService,
private documentListViewService: DocumentListViewService
) {}
statistics: Statistics = {}
@ -50,4 +64,17 @@ export class StatisticsWidgetComponent implements OnInit, OnDestroy {
ngOnDestroy(): void {
this.subscription.unsubscribe()
}
goToInbox() {
this.documentListViewService.quickFilter([
{
rule_type: FILTER_HAS_TAGS_ALL,
value: this.statistics.inbox_tag.toString(),
},
])
}
getFileTypePercent(filetype: DocumentFileType): number {
return (filetype.mime_type_count / this.statistics?.documents_total) * 100
}
}

View File

@ -223,6 +223,14 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
.dropdown-menu {
--bs-dropdown-color: var(--bs-body-color);
}
.card .list-group-item {
--bs-border-color: rgb(var(--bs-dark-rgb));
.bg-secondary {
background-color: rgb(var(--bs-dark-rgb)) !important;
}
}
}
body.color-scheme-dark {

View File

@ -1039,9 +1039,24 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
def test_statistics(self):
doc1 = Document.objects.create(title="none1", checksum="A")
doc2 = Document.objects.create(title="none2", checksum="B")
doc3 = Document.objects.create(title="none3", checksum="C")
doc1 = Document.objects.create(
title="none1",
checksum="A",
mime_type="application/pdf",
content="abc",
)
doc2 = Document.objects.create(
title="none2",
checksum="B",
mime_type="application/pdf",
content="123",
)
doc3 = Document.objects.create(
title="none3",
checksum="C",
mime_type="text/plain",
content="hello",
)
tag_inbox = Tag.objects.create(name="t1", is_inbox_tag=True)
@ -1051,6 +1066,16 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["documents_total"], 3)
self.assertEqual(response.data["documents_inbox"], 1)
self.assertEqual(response.data["inbox_tag"], tag_inbox.pk)
self.assertEqual(
response.data["document_file_type_counts"][0]["mime_type_count"],
2,
)
self.assertEqual(
response.data["document_file_type_counts"][1]["mime_type_count"],
1,
)
self.assertEqual(response.data["character_count"], 11)
def test_statistics_no_inbox_tag(self):
Document.objects.create(title="none1", checksum="A")
@ -1058,6 +1083,7 @@ class TestDocumentApi(DirectoriesMixin, APITestCase):
response = self.client.get("/api/statistics/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data["documents_inbox"], None)
self.assertEqual(response.data["inbox_tag"], None)
@mock.patch("documents.views.consume_file.delay")
def test_upload(self, m):

View File

@ -21,6 +21,7 @@ from django.db.models import Count
from django.db.models import IntegerField
from django.db.models import Max
from django.db.models import When
from django.db.models.functions import Length
from django.db.models.functions import Lower
from django.http import Http404
from django.http import HttpResponse
@ -186,6 +187,7 @@ class TagViewSet(ModelViewSet, PassUserMixin):
)
def get_serializer_class(self, *args, **kwargs):
print(self.request.version)
if int(self.request.version) == 1:
return TagSerializerVersion1
else:
@ -794,17 +796,41 @@ class StatisticsView(APIView):
def get(self, request, format=None):
documents_total = Document.objects.all().count()
if Tag.objects.filter(is_inbox_tag=True).exists():
documents_inbox = (
Document.objects.filter(tags__is_inbox_tag=True).distinct().count()
inbox_tag = Tag.objects.filter(is_inbox_tag=True)
documents_inbox = (
Document.objects.filter(tags__is_inbox_tag=True).distinct().count()
if inbox_tag.exists()
else None
)
document_file_type_counts = (
Document.objects.values("mime_type")
.annotate(mime_type_count=Count("mime_type"))
.order_by("-mime_type_count")
if documents_total > 0
else 0
)
character_count = (
sum(
Document.objects.annotate(characters=Length("content")).values_list(
"characters",
flat=True,
),
)
else:
documents_inbox = None
if documents_total > 0
else 0
)
return Response(
{
"documents_total": documents_total,
"documents_inbox": documents_inbox,
"inbox_tag": inbox_tag.first().pk if inbox_tag.exists() else None,
"document_file_type_counts": document_file_type_counts,
"character_count": character_count,
},
)