mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-04-02 13:45:10 -05:00
filter by title or title+content fixes #636
This commit is contained in:
parent
e466c31bb3
commit
efa7b7b0b5
@ -1039,81 +1039,95 @@
|
||||
<context context-type="linenumber">106</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5701618810648052610" datatype="html">
|
||||
<source>Title</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">73</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="3100631071441658964" datatype="html">
|
||||
<source>Title & content</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">74</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5195932016807797291" datatype="html">
|
||||
<source>Correspondent: <x id="PH" equiv-text="this.correspondents.find(c => c.id == +rule.value)?.name"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">29</context>
|
||||
<context context-type="linenumber">32</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8170755470576301659" datatype="html">
|
||||
<source>Without correspondent</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">31</context>
|
||||
<context context-type="linenumber">34</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8705701325879965907" datatype="html">
|
||||
<source>Type: <x id="PH" equiv-text="this.documentTypes.find(dt => dt.id == +rule.value)?.name"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">36</context>
|
||||
<context context-type="linenumber">39</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4362173610367509215" datatype="html">
|
||||
<source>Without document type</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">38</context>
|
||||
<context context-type="linenumber">41</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="8180755793012580465" datatype="html">
|
||||
<source>Tag: <x id="PH" equiv-text="this.tags.find(t => t.id == +rule.value)?.name"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">42</context>
|
||||
<context context-type="linenumber">45</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6494566478302448576" datatype="html">
|
||||
<source>Without any tag</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">46</context>
|
||||
<context context-type="linenumber">49</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="6523384805359286307" datatype="html">
|
||||
<source>Title: <x id="PH" equiv-text="rule.value"/></source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.ts</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
<context context-type="linenumber">53</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="02d184c288f567825a1fcbf83bcd3099a10853d5" datatype="html">
|
||||
<source>Filter tags</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">12</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="4b089ca12c472cf0b46167bb5afe4b527b301bbc" datatype="html">
|
||||
<source>Filter correspondents</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">20</context>
|
||||
<context context-type="linenumber">28</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="0ad509732aaf702b7ea8c771c7809fa84bc85908" datatype="html">
|
||||
<source>Filter document types</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">27</context>
|
||||
<context context-type="linenumber">35</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="2d9d55f1b70142ff4597ba32179d16888fd9c6b2" datatype="html">
|
||||
<source>Reset filters</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/components/document-list/filter-editor/filter-editor.component.html</context>
|
||||
<context context-type="linenumber">50</context>
|
||||
<context context-type="linenumber">58</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="7593728289020204896" datatype="html">
|
||||
@ -1819,13 +1833,6 @@
|
||||
<context context-type="linenumber">18</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5701618810648052610" datatype="html">
|
||||
<source>Title</source>
|
||||
<context-group purpose="location">
|
||||
<context context-type="sourcefile">src/app/services/rest/document.service.ts</context>
|
||||
<context context-type="linenumber">19</context>
|
||||
</context-group>
|
||||
</trans-unit>
|
||||
<trans-unit id="5066119607229701477" datatype="html">
|
||||
<source>Document type</source>
|
||||
<context-group purpose="location">
|
||||
|
@ -2,7 +2,15 @@
|
||||
<div class="col mb-2 mb-xl-0">
|
||||
<div class="form-inline d-flex align-items-center">
|
||||
<label class="text-muted mr-2 mb-0" i18n>Filter by:</label>
|
||||
<input class="form-control form-control-sm flex-fill w-auto" type="text" [(ngModel)]="titleFilter" placeholder="Title" i18n-placeholder>
|
||||
<div class="input-group input-group-sm flex-fill w-auto">
|
||||
<div class="input-group-prepend" ngbDropdown>
|
||||
<button class="btn btn-outline-primary" ngbDropdownToggle>{{textFilterTargetName}}</button>
|
||||
<div class="dropdown-menu shadow" ngbDropdownMenu>
|
||||
<button *ngFor="let t of textFilterTargets" ngbDropdownItem [class.active]="textFilterTarget == t.id" (click)="changeTextFilterTarget(t.id)">{{t.name}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<input class="form-control form-control-sm" type="text" [(ngModel)]="textFilter">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-100 d-xl-none"></div>
|
||||
|
@ -8,10 +8,13 @@ import { DocumentTypeService } from 'src/app/services/rest/document-type.service
|
||||
import { TagService } from 'src/app/services/rest/tag.service';
|
||||
import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
|
||||
import { FilterRule } from 'src/app/data/filter-rule';
|
||||
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_ANY_TAG, FILTER_HAS_TAG, FILTER_TITLE } from 'src/app/data/filter-rule-type';
|
||||
import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_ANY_TAG, FILTER_HAS_TAG, FILTER_TITLE, FILTER_TITLE_CONTENT } from 'src/app/data/filter-rule-type';
|
||||
import { FilterableDropdownSelectionModel } from '../../common/filterable-dropdown/filterable-dropdown.component';
|
||||
import { ToggleableItemState } from '../../common/filterable-dropdown/toggleable-dropdown-button/toggleable-dropdown-button.component';
|
||||
|
||||
const TEXT_FILTER_TARGET_TITLE = "title"
|
||||
const TEXT_FILTER_TARGET_TITLE_CONTENT = "title-content"
|
||||
|
||||
@Component({
|
||||
selector: 'app-filter-editor',
|
||||
templateUrl: './filter-editor.component.html',
|
||||
@ -64,7 +67,19 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
correspondents: PaperlessCorrespondent[] = []
|
||||
documentTypes: PaperlessDocumentType[] = []
|
||||
|
||||
_titleFilter = ""
|
||||
_textFilter = ""
|
||||
|
||||
textFilterTargets = [
|
||||
{id: TEXT_FILTER_TARGET_TITLE, name: $localize`Title`},
|
||||
{id: TEXT_FILTER_TARGET_TITLE_CONTENT, name: $localize`Title & content`}
|
||||
]
|
||||
|
||||
textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
|
||||
get textFilterTargetName() {
|
||||
return this.textFilterTargets.find(t => t.id == this.textFilterTarget)?.name
|
||||
}
|
||||
|
||||
|
||||
tagSelectionModel = new FilterableDropdownSelectionModel()
|
||||
correspondentSelectionModel = new FilterableDropdownSelectionModel()
|
||||
@ -80,7 +95,7 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
this.documentTypeSelectionModel.clear(false)
|
||||
this.tagSelectionModel.clear(false)
|
||||
this.correspondentSelectionModel.clear(false)
|
||||
this._titleFilter = null
|
||||
this._textFilter = null
|
||||
this.dateAddedBefore = null
|
||||
this.dateAddedAfter = null
|
||||
this.dateCreatedBefore = null
|
||||
@ -89,7 +104,12 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
value.forEach(rule => {
|
||||
switch (rule.rule_type) {
|
||||
case FILTER_TITLE:
|
||||
this._titleFilter = rule.value
|
||||
this._textFilter = rule.value
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE
|
||||
break
|
||||
case FILTER_TITLE_CONTENT:
|
||||
this._textFilter = rule.value
|
||||
this.textFilterTarget = TEXT_FILTER_TARGET_TITLE_CONTENT
|
||||
break
|
||||
case FILTER_CREATED_AFTER:
|
||||
this.dateCreatedAfter = rule.value
|
||||
@ -121,8 +141,11 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
|
||||
get filterRules(): FilterRule[] {
|
||||
let filterRules: FilterRule[] = []
|
||||
if (this._titleFilter) {
|
||||
filterRules.push({rule_type: FILTER_TITLE, value: this._titleFilter})
|
||||
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE_CONTENT) {
|
||||
filterRules.push({rule_type: FILTER_TITLE_CONTENT, value: this._textFilter})
|
||||
}
|
||||
if (this._textFilter && this.textFilterTarget == TEXT_FILTER_TARGET_TITLE) {
|
||||
filterRules.push({rule_type: FILTER_TITLE, value: this._textFilter})
|
||||
}
|
||||
if (this.tagSelectionModel.isNoneSelected()) {
|
||||
filterRules.push({rule_type: FILTER_HAS_ANY_TAG, value: "false"})
|
||||
@ -165,15 +188,15 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
this.filterRulesChange.next(this.filterRules)
|
||||
}
|
||||
|
||||
get titleFilter() {
|
||||
return this._titleFilter
|
||||
get textFilter() {
|
||||
return this._textFilter
|
||||
}
|
||||
|
||||
set titleFilter(value) {
|
||||
this.titleFilterDebounce.next(value)
|
||||
set textFilter(value) {
|
||||
this.textFilterDebounce.next(value)
|
||||
}
|
||||
|
||||
titleFilterDebounce: Subject<string>
|
||||
textFilterDebounce: Subject<string>
|
||||
subscription: Subscription
|
||||
|
||||
ngOnInit() {
|
||||
@ -181,19 +204,19 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
|
||||
this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
|
||||
|
||||
this.titleFilterDebounce = new Subject<string>()
|
||||
this.textFilterDebounce = new Subject<string>()
|
||||
|
||||
this.subscription = this.titleFilterDebounce.pipe(
|
||||
this.subscription = this.textFilterDebounce.pipe(
|
||||
debounceTime(400),
|
||||
distinctUntilChanged()
|
||||
).subscribe(title => {
|
||||
this._titleFilter = title
|
||||
).subscribe(text => {
|
||||
this._textFilter = text
|
||||
this.updateRules()
|
||||
})
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.titleFilterDebounce.complete()
|
||||
this.textFilterDebounce.complete()
|
||||
}
|
||||
|
||||
resetSelected() {
|
||||
@ -223,4 +246,9 @@ export class FilterEditorComponent implements OnInit, OnDestroy {
|
||||
onDocumentTypeDropdownOpen() {
|
||||
this.documentTypeSelectionModel.apply()
|
||||
}
|
||||
|
||||
changeTextFilterTarget(target) {
|
||||
this.textFilterTarget = target
|
||||
this.updateRules()
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ export const FILTER_DOES_NOT_HAVE_TAG = 17
|
||||
|
||||
export const FILTER_ASN_ISNULL = 18
|
||||
|
||||
export const FILTER_TITLE_CONTENT = 19
|
||||
|
||||
export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
||||
|
||||
{id: FILTER_TITLE, filtervar: "title__icontains", datatype: "string", multi: false, default: ""},
|
||||
@ -47,7 +49,9 @@ export const FILTER_RULE_TYPES: FilterRuleType[] = [
|
||||
|
||||
{id: FILTER_MODIFIED_BEFORE, filtervar: "modified__date__lt", datatype: "date", multi: false},
|
||||
{id: FILTER_MODIFIED_AFTER, filtervar: "modified__date__gt", datatype: "date", multi: false},
|
||||
{id: FILTER_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false}
|
||||
{id: FILTER_ASN_ISNULL, filtervar: "archive_serial_number__isnull", datatype: "boolean", multi: false},
|
||||
|
||||
{id: FILTER_TITLE_CONTENT, filtervar: "title_content", datatype: "string", multi: false}
|
||||
]
|
||||
|
||||
export interface FilterRuleType {
|
||||
|
@ -1,3 +1,4 @@
|
||||
from django.db.models import Q
|
||||
from django_filters.rest_framework import BooleanFilter, FilterSet, Filter
|
||||
|
||||
from .models import Correspondent, Document, Tag, DocumentType, Log
|
||||
@ -70,6 +71,16 @@ class InboxFilter(Filter):
|
||||
return qs
|
||||
|
||||
|
||||
class TitleContentFilter(Filter):
|
||||
|
||||
def filter(self, qs, value):
|
||||
if value:
|
||||
return qs.filter(Q(title__icontains=value) |
|
||||
Q(content__icontains=value))
|
||||
else:
|
||||
return qs
|
||||
|
||||
|
||||
class DocumentFilterSet(FilterSet):
|
||||
|
||||
is_tagged = BooleanFilter(
|
||||
@ -85,6 +96,8 @@ class DocumentFilterSet(FilterSet):
|
||||
|
||||
is_in_inbox = InboxFilter()
|
||||
|
||||
title_content = TitleContentFilter()
|
||||
|
||||
class Meta:
|
||||
model = Document
|
||||
fields = {
|
||||
|
18
src/documents/migrations/1014_auto_20210228_1614.py
Normal file
18
src/documents/migrations/1014_auto_20210228_1614.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.1.7 on 2021-02-28 15:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('documents', '1013_migrate_tag_colour'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='savedviewfilterrule',
|
||||
name='rule_type',
|
||||
field=models.PositiveIntegerField(choices=[(0, 'title contains'), (1, 'content contains'), (2, 'ASN is'), (3, 'correspondent is'), (4, 'document type is'), (5, 'is in inbox'), (6, 'has tag'), (7, 'has any tag'), (8, 'created before'), (9, 'created after'), (10, 'created year is'), (11, 'created month is'), (12, 'created day is'), (13, 'added before'), (14, 'added after'), (15, 'modified before'), (16, 'modified after'), (17, 'does not have tag'), (18, 'does not have ASN'), (19, 'title or content contains')], verbose_name='rule type'),
|
||||
),
|
||||
]
|
@ -385,6 +385,8 @@ class SavedViewFilterRule(models.Model):
|
||||
(15, _("modified before")),
|
||||
(16, _("modified after")),
|
||||
(17, _("does not have tag")),
|
||||
(18, _("does not have ASN")),
|
||||
(19, _("title or content contains")),
|
||||
]
|
||||
|
||||
saved_view = models.ForeignKey(
|
||||
|
Loading…
x
Reference in New Issue
Block a user