<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>
<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>
+ <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">3</context>
+ <context context-type="linenumber">4</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="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">4</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">
<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>
+.flex-column {
+ row-gap: 0.2rem;
+}
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({
constructor(
private http: HttpClient,
- private consumerStatusService: ConsumerStatusService
+ private consumerStatusService: ConsumerStatusService,
+ private documentListViewService: DocumentListViewService
) {}
statistics: Statistics = {}
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
+ }
}
.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 {
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)
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")
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):
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
)
def get_serializer_class(self, *args, **kwargs):
+ print(self.request.version)
if int(self.request.version) == 1:
return TagSerializerVersion1
else:
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,
},
)