mirror of
https://github.com/Xevion/paperless-mobile.git
synced 2025-12-09 16:07:57 -06:00
Initial commit
This commit is contained in:
19
lib/features/documents/model/bulk_edit.model.dart
Normal file
19
lib/features/documents/model/bulk_edit.model.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:flutter_paperless_mobile/core/type/json.dart';
|
||||
|
||||
class BulkEditAction {
|
||||
final List<int> documents;
|
||||
final String _method;
|
||||
final Map<String, dynamic> parameters;
|
||||
|
||||
BulkEditAction.delete(this.documents)
|
||||
: _method = 'delete',
|
||||
parameters = {};
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
'documents': documents,
|
||||
'method': _method,
|
||||
'parameters': parameters,
|
||||
};
|
||||
}
|
||||
}
|
||||
148
lib/features/documents/model/document.model.dart
Normal file
148
lib/features/documents/model/document.model.dart
Normal file
@@ -0,0 +1,148 @@
|
||||
// ignore_for_file: non_constant_identifier_names
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_paperless_mobile/core/type/json.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/ids_query_parameter.dart';
|
||||
|
||||
class DocumentModel extends Equatable {
|
||||
static const idKey = 'id';
|
||||
static const titleKey = "title";
|
||||
static const contentKey = "content";
|
||||
static const archivedFileNameKey = "archived_file_name";
|
||||
static const asnKey = "archive_serial_number";
|
||||
static const createdKey = "created";
|
||||
static const modifiedKey = "modified";
|
||||
static const addedKey = "added";
|
||||
static const correspondentKey = "correspondent";
|
||||
static const originalFileNameKey = 'original_file_name';
|
||||
static const documentTypeKey = "document_type";
|
||||
static const tagsKey = "tags";
|
||||
static const storagePathKey = "storage_path";
|
||||
|
||||
final int id;
|
||||
final String title;
|
||||
final String? content;
|
||||
final List<int> tags;
|
||||
final int? documentType;
|
||||
final int? correspondent;
|
||||
final int? storagePath;
|
||||
final DateTime created;
|
||||
final DateTime modified;
|
||||
final DateTime added;
|
||||
final int? archiveSerialNumber;
|
||||
final String originalFileName;
|
||||
final String? archivedFileName;
|
||||
|
||||
const DocumentModel({
|
||||
required this.id,
|
||||
required this.title,
|
||||
this.content,
|
||||
this.tags = const <int>[],
|
||||
required this.documentType,
|
||||
required this.correspondent,
|
||||
required this.created,
|
||||
required this.modified,
|
||||
required this.added,
|
||||
this.archiveSerialNumber,
|
||||
required this.originalFileName,
|
||||
this.archivedFileName,
|
||||
this.storagePath,
|
||||
});
|
||||
|
||||
DocumentModel.fromJson(JSON json)
|
||||
: id = json[idKey],
|
||||
title = json[titleKey],
|
||||
content = json[contentKey],
|
||||
created = DateTime.parse(json[createdKey]),
|
||||
modified = DateTime.parse(json[modifiedKey]),
|
||||
added = DateTime.parse(json[addedKey]),
|
||||
archiveSerialNumber = json[asnKey],
|
||||
originalFileName = json[originalFileNameKey],
|
||||
archivedFileName = json[archivedFileNameKey],
|
||||
tags = (json[tagsKey] as List<dynamic>).cast<int>(),
|
||||
correspondent = json[correspondentKey],
|
||||
documentType = json[documentTypeKey],
|
||||
storagePath = json[storagePathKey];
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
idKey: id,
|
||||
titleKey: title,
|
||||
asnKey: archiveSerialNumber,
|
||||
archivedFileNameKey: archivedFileName,
|
||||
contentKey: content,
|
||||
correspondentKey: correspondent,
|
||||
documentTypeKey: documentType,
|
||||
createdKey: created.toUtc().toIso8601String(),
|
||||
modifiedKey: modified.toUtc().toIso8601String(),
|
||||
addedKey: added.toUtc().toIso8601String(),
|
||||
originalFileNameKey: originalFileName,
|
||||
tagsKey: tags,
|
||||
storagePathKey: storagePath,
|
||||
};
|
||||
}
|
||||
|
||||
DocumentModel copyWith({
|
||||
String? title,
|
||||
String? content,
|
||||
IdsQueryParameter? tags,
|
||||
IdQueryParameter? documentType,
|
||||
IdQueryParameter? correspondent,
|
||||
IdQueryParameter? storagePath,
|
||||
DateTime? created,
|
||||
DateTime? modified,
|
||||
DateTime? added,
|
||||
int? archiveSerialNumber,
|
||||
String? originalFileName,
|
||||
String? archivedFileName,
|
||||
}) {
|
||||
return DocumentModel(
|
||||
id: id,
|
||||
title: title ?? this.title,
|
||||
content: content ?? this.content,
|
||||
documentType: fromQuery(documentType, this.documentType),
|
||||
correspondent: fromQuery(correspondent, this.correspondent),
|
||||
storagePath: fromQuery(storagePath, this.storagePath),
|
||||
tags: fromListQuery(tags, this.tags),
|
||||
created: created ?? this.created,
|
||||
modified: modified ?? this.modified,
|
||||
added: added ?? this.added,
|
||||
originalFileName: originalFileName ?? this.originalFileName,
|
||||
archiveSerialNumber: archiveSerialNumber ?? this.archiveSerialNumber,
|
||||
archivedFileName: archivedFileName ?? this.archivedFileName,
|
||||
);
|
||||
}
|
||||
|
||||
int? fromQuery(IdQueryParameter? query, int? previous) {
|
||||
if (query == null) {
|
||||
return previous;
|
||||
}
|
||||
return query.id;
|
||||
}
|
||||
|
||||
List<int> fromListQuery(IdsQueryParameter? query, List<int> previous) {
|
||||
if (query == null) {
|
||||
return previous;
|
||||
}
|
||||
return query.ids;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
title,
|
||||
content,
|
||||
tags,
|
||||
documentType,
|
||||
storagePath,
|
||||
correspondent,
|
||||
created,
|
||||
modified,
|
||||
added,
|
||||
archiveSerialNumber,
|
||||
originalFileName,
|
||||
archivedFileName,
|
||||
storagePath
|
||||
];
|
||||
}
|
||||
168
lib/features/documents/model/document_filter.dart
Normal file
168
lib/features/documents/model/document_filter.dart
Normal file
@@ -0,0 +1,168 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/asn_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/query_type.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/sort_order.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:flutter_paperless_mobile/util.dart';
|
||||
|
||||
class DocumentFilter with EquatableMixin {
|
||||
static const DocumentFilter initial = DocumentFilter();
|
||||
|
||||
static const DocumentFilter latestDocument = DocumentFilter(
|
||||
sortField: SortField.added,
|
||||
sortOrder: SortOrder.descending,
|
||||
pageSize: 1,
|
||||
page: 1,
|
||||
);
|
||||
|
||||
final int pageSize;
|
||||
final int page;
|
||||
final DocumentTypeQuery documentType;
|
||||
final CorrespondentQuery correspondent;
|
||||
final StoragePathQuery storagePath;
|
||||
final AsnQuery asn;
|
||||
final TagsQuery tags;
|
||||
final SortField sortField;
|
||||
final SortOrder sortOrder;
|
||||
final DateTime? addedDateAfter;
|
||||
final DateTime? addedDateBefore;
|
||||
final DateTime? createdDateAfter;
|
||||
final DateTime? createdDateBefore;
|
||||
final QueryType queryType;
|
||||
final String? queryText;
|
||||
|
||||
const DocumentFilter({
|
||||
this.createdDateAfter,
|
||||
this.createdDateBefore,
|
||||
this.documentType = const DocumentTypeQuery.unset(),
|
||||
this.correspondent = const CorrespondentQuery.unset(),
|
||||
this.storagePath = const StoragePathQuery.unset(),
|
||||
this.asn = const AsnQuery.unset(),
|
||||
this.tags = const TagsQuery.unset(),
|
||||
this.sortField = SortField.created,
|
||||
this.sortOrder = SortOrder.descending,
|
||||
this.page = 1,
|
||||
this.pageSize = 25,
|
||||
this.addedDateAfter,
|
||||
this.addedDateBefore,
|
||||
this.queryType = QueryType.titleAndContent,
|
||||
this.queryText,
|
||||
});
|
||||
|
||||
String toQueryString() {
|
||||
final StringBuffer sb = StringBuffer("page=$page&page_size=$pageSize");
|
||||
sb.write(documentType.toQueryParameter());
|
||||
sb.write(correspondent.toQueryParameter());
|
||||
sb.write(tags.toQueryParameter());
|
||||
sb.write(storagePath.toQueryParameter());
|
||||
sb.write(asn.toQueryParameter());
|
||||
|
||||
if (queryText?.isNotEmpty ?? false) {
|
||||
sb.write("&${queryType.queryParam}=$queryText");
|
||||
}
|
||||
|
||||
sb.write("&ordering=${sortOrder.queryString}${sortField.queryString}");
|
||||
|
||||
if (addedDateAfter != null) {
|
||||
sb.write("&added__date__gt=${dateFormat.format(addedDateAfter!)}");
|
||||
}
|
||||
|
||||
if (addedDateBefore != null) {
|
||||
sb.write("&added__date__lt=${dateFormat.format(addedDateBefore!)}");
|
||||
}
|
||||
|
||||
if (createdDateAfter != null) {
|
||||
sb.write("&created__date__gt=${dateFormat.format(createdDateAfter!)}");
|
||||
}
|
||||
|
||||
if (createdDateBefore != null) {
|
||||
sb.write("&created__date__lt=${dateFormat.format(createdDateBefore!)}");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toQueryString();
|
||||
}
|
||||
|
||||
DocumentFilter copyWith({
|
||||
int? pageSize,
|
||||
int? page,
|
||||
bool? onlyNoDocumentType,
|
||||
DocumentTypeQuery? documentType,
|
||||
CorrespondentQuery? correspondent,
|
||||
StoragePathQuery? storagePath,
|
||||
TagsQuery? tags,
|
||||
SortField? sortField,
|
||||
SortOrder? sortOrder,
|
||||
DateTime? addedDateAfter,
|
||||
DateTime? addedDateBefore,
|
||||
DateTime? createdDateBefore,
|
||||
DateTime? createdDateAfter,
|
||||
QueryType? queryType,
|
||||
String? queryText,
|
||||
}) {
|
||||
return DocumentFilter(
|
||||
pageSize: pageSize ?? this.pageSize,
|
||||
page: page ?? this.page,
|
||||
documentType: documentType ?? this.documentType,
|
||||
correspondent: correspondent ?? this.correspondent,
|
||||
storagePath: storagePath ?? this.storagePath,
|
||||
tags: tags ?? this.tags,
|
||||
sortField: sortField ?? this.sortField,
|
||||
sortOrder: sortOrder ?? this.sortOrder,
|
||||
addedDateAfter: addedDateAfter ?? this.addedDateAfter,
|
||||
addedDateBefore: addedDateBefore ?? this.addedDateBefore,
|
||||
queryType: queryType ?? this.queryType,
|
||||
queryText: queryText ?? this.queryText,
|
||||
createdDateBefore: createdDateBefore ?? this.createdDateBefore,
|
||||
createdDateAfter: createdDateAfter ?? this.createdDateAfter,
|
||||
);
|
||||
}
|
||||
|
||||
String? get titleOnlyMatchString {
|
||||
if (queryType == QueryType.title) {
|
||||
return queryText?.isEmpty ?? true ? null : queryText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? get titleAndContentMatchString {
|
||||
if (queryType == QueryType.titleAndContent) {
|
||||
return queryText?.isEmpty ?? true ? null : queryText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String? get extendedMatchString {
|
||||
if (queryType == QueryType.extended) {
|
||||
return queryText?.isEmpty ?? true ? null : queryText;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
pageSize,
|
||||
page,
|
||||
documentType,
|
||||
correspondent,
|
||||
storagePath,
|
||||
asn,
|
||||
tags,
|
||||
sortField,
|
||||
sortOrder,
|
||||
addedDateAfter,
|
||||
addedDateBefore,
|
||||
createdDateAfter,
|
||||
createdDateBefore,
|
||||
queryType,
|
||||
queryText,
|
||||
];
|
||||
}
|
||||
40
lib/features/documents/model/document_meta_data.model.dart
Normal file
40
lib/features/documents/model/document_meta_data.model.dart
Normal file
@@ -0,0 +1,40 @@
|
||||
class DocumentMetaData {
|
||||
String originalChecksum;
|
||||
int originalSize;
|
||||
String originalMimeType;
|
||||
String mediaFilename;
|
||||
bool hasArchiveVersion;
|
||||
String? archiveChecksum;
|
||||
int? archiveSize;
|
||||
|
||||
DocumentMetaData({
|
||||
required this.originalChecksum,
|
||||
required this.originalSize,
|
||||
required this.originalMimeType,
|
||||
required this.mediaFilename,
|
||||
required this.hasArchiveVersion,
|
||||
this.archiveChecksum,
|
||||
this.archiveSize,
|
||||
});
|
||||
|
||||
DocumentMetaData.fromJson(Map<String, dynamic> json)
|
||||
: originalChecksum = json['original_checksum'],
|
||||
originalSize = json['original_size'],
|
||||
originalMimeType = json['original_mime_type'],
|
||||
mediaFilename = json['media_filename'],
|
||||
hasArchiveVersion = json['has_archive_version'],
|
||||
archiveChecksum = json['archive_checksum'],
|
||||
archiveSize = json['archive_size'];
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final data = <String, dynamic>{};
|
||||
data['original_checksum'] = originalChecksum;
|
||||
data['original_size'] = originalSize;
|
||||
data['original_mime_type'] = originalMimeType;
|
||||
data['media_filename'] = mediaFilename;
|
||||
data['has_archive_version'] = hasArchiveVersion;
|
||||
data['archive_checksum'] = archiveChecksum;
|
||||
data['archive_size'] = archiveSize;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
166
lib/features/documents/model/filter_rule.model.dart
Normal file
166
lib/features/documents/model/filter_rule.model.dart
Normal file
@@ -0,0 +1,166 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_paperless_mobile/core/type/json.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/document_filter.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/correspondent_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/document_type_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/query_type.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/storage_path_query.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/tags_query.dart';
|
||||
import 'package:flutter_paperless_mobile/util.dart';
|
||||
|
||||
class FilterRule with EquatableMixin {
|
||||
static const int titleRule = 0;
|
||||
static const int asnRule = 2;
|
||||
static const int correspondentRule = 3;
|
||||
static const int documentTypeRule = 4;
|
||||
static const int tagRule = 6;
|
||||
static const int createdBeforeRule = 8;
|
||||
static const int createdAfterRule = 9;
|
||||
static const int addedBeforeRule = 13;
|
||||
static const int addedAfterRule = 14;
|
||||
static const int titleAndContentRule = 19;
|
||||
static const int extendedRule = 20;
|
||||
static const int storagePathRule = 25;
|
||||
// Currently unsupported view optiosn:
|
||||
static const int _content = 1;
|
||||
static const int _isInInbox = 5;
|
||||
static const int _hasAnyTag = 7;
|
||||
static const int _createdYearIs = 10;
|
||||
static const int _createdMonthIs = 11;
|
||||
static const int _createdDayIs = 12;
|
||||
static const int _modifiedBefore = 15;
|
||||
static const int _modifiedAfter = 16;
|
||||
static const int _doesNotHaveTag = 17;
|
||||
static const int _doesNotHaveAsn = 18;
|
||||
static const int _moreLikeThis = 21;
|
||||
static const int _hasTagsIn = 22;
|
||||
static const int _asnGreaterThan = 23;
|
||||
static const int _asnLessThan = 24;
|
||||
|
||||
final int ruleType;
|
||||
final String? value;
|
||||
|
||||
FilterRule(this.ruleType, this.value);
|
||||
|
||||
FilterRule.fromJson(JSON json)
|
||||
: ruleType = json['rule_type'],
|
||||
value = json['value'];
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
'rule_type': ruleType,
|
||||
'value': value,
|
||||
};
|
||||
}
|
||||
|
||||
DocumentFilter applyToFilter(final DocumentFilter filter) {
|
||||
//TODO: Check in profiling mode if this is inefficient enough to cause stutters...
|
||||
switch (ruleType) {
|
||||
case titleRule:
|
||||
return filter.copyWith(queryText: value, queryType: QueryType.title);
|
||||
case documentTypeRule:
|
||||
return filter.copyWith(
|
||||
documentType: value == null
|
||||
? const DocumentTypeQuery.notAssigned()
|
||||
: DocumentTypeQuery.fromId(int.parse(value!)),
|
||||
);
|
||||
case correspondentRule:
|
||||
return filter.copyWith(
|
||||
correspondent: value == null
|
||||
? const CorrespondentQuery.notAssigned()
|
||||
: CorrespondentQuery.fromId(int.parse(value!)),
|
||||
);
|
||||
case storagePathRule:
|
||||
return filter.copyWith(
|
||||
storagePath: value == null
|
||||
? const StoragePathQuery.notAssigned()
|
||||
: StoragePathQuery.fromId(int.parse(value!)),
|
||||
);
|
||||
case tagRule:
|
||||
return filter.copyWith(
|
||||
tags: value == null
|
||||
? const TagsQuery.notAssigned()
|
||||
: TagsQuery.fromIds([...filter.tags.ids, int.parse(value!)]),
|
||||
);
|
||||
case createdBeforeRule:
|
||||
return filter.copyWith(createdDateBefore: value == null ? null : DateTime.parse(value!));
|
||||
case createdAfterRule:
|
||||
return filter.copyWith(createdDateAfter: value == null ? null : DateTime.parse(value!));
|
||||
case addedBeforeRule:
|
||||
return filter.copyWith(addedDateBefore: value == null ? null : DateTime.parse(value!));
|
||||
case addedAfterRule:
|
||||
return filter.copyWith(addedDateAfter: value == null ? null : DateTime.parse(value!));
|
||||
case titleAndContentRule:
|
||||
return filter.copyWith(queryText: value, queryType: QueryType.titleAndContent);
|
||||
case extendedRule:
|
||||
return filter.copyWith(queryText: value, queryType: QueryType.extended);
|
||||
//TODO: Add currently unused rules
|
||||
default:
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Converts a [DocumentFilter] to a list of [FilterRule]s.
|
||||
///
|
||||
static List<FilterRule> fromFilter(final DocumentFilter filter) {
|
||||
List<FilterRule> filterRules = [];
|
||||
if (filter.correspondent.onlyNotAssigned) {
|
||||
filterRules.add(FilterRule(correspondentRule, null));
|
||||
}
|
||||
if (filter.correspondent.isSet) {
|
||||
filterRules.add(FilterRule(correspondentRule, filter.correspondent.id!.toString()));
|
||||
}
|
||||
if (filter.documentType.onlyNotAssigned) {
|
||||
filterRules.add(FilterRule(documentTypeRule, null));
|
||||
}
|
||||
if (filter.documentType.isSet) {
|
||||
filterRules.add(FilterRule(documentTypeRule, filter.documentType.id!.toString()));
|
||||
}
|
||||
if (filter.storagePath.onlyNotAssigned) {
|
||||
filterRules.add(FilterRule(storagePathRule, null));
|
||||
}
|
||||
if (filter.storagePath.isSet) {
|
||||
filterRules.add(FilterRule(storagePathRule, filter.storagePath.id!.toString()));
|
||||
}
|
||||
if (filter.tags.onlyNotAssigned) {
|
||||
filterRules.add(FilterRule(tagRule, null));
|
||||
}
|
||||
if (filter.tags.isSet) {
|
||||
filterRules.addAll(filter.tags.ids.map((id) => FilterRule(tagRule, id.toString())));
|
||||
}
|
||||
|
||||
if (filter.queryText != null) {
|
||||
switch (filter.queryType) {
|
||||
case QueryType.title:
|
||||
filterRules.add(FilterRule(titleRule, filter.queryText!));
|
||||
break;
|
||||
case QueryType.titleAndContent:
|
||||
filterRules.add(FilterRule(titleAndContentRule, filter.queryText!));
|
||||
break;
|
||||
case QueryType.extended:
|
||||
filterRules.add(FilterRule(extendedRule, filter.queryText!));
|
||||
break;
|
||||
case QueryType.asn:
|
||||
filterRules.add(FilterRule(asnRule, filter.queryText!));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (filter.createdDateAfter != null) {
|
||||
filterRules.add(FilterRule(createdAfterRule, dateFormat.format(filter.createdDateAfter!)));
|
||||
}
|
||||
if (filter.createdDateBefore != null) {
|
||||
filterRules.add(FilterRule(createdBeforeRule, dateFormat.format(filter.createdDateBefore!)));
|
||||
}
|
||||
if (filter.addedDateAfter != null) {
|
||||
filterRules.add(FilterRule(addedAfterRule, dateFormat.format(filter.addedDateAfter!)));
|
||||
}
|
||||
if (filter.addedDateBefore != null) {
|
||||
filterRules.add(FilterRule(addedBeforeRule, dateFormat.format(filter.addedDateBefore!)));
|
||||
}
|
||||
return filterRules;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [ruleType, value];
|
||||
}
|
||||
84
lib/features/documents/model/paged_search_result.dart
Normal file
84
lib/features/documents/model/paged_search_result.dart
Normal file
@@ -0,0 +1,84 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_paperless_mobile/core/type/json.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/document.model.dart';
|
||||
|
||||
const pageRegex = r".*page=(\d+).*";
|
||||
|
||||
class PagedSearchResult<T> extends Equatable {
|
||||
/// Total number of available items
|
||||
final int count;
|
||||
|
||||
/// Link to next page
|
||||
final String? next;
|
||||
|
||||
/// Link to previous page
|
||||
final String? previous;
|
||||
|
||||
/// Actual items
|
||||
final List<T> results;
|
||||
|
||||
int get pageKey {
|
||||
if (next != null) {
|
||||
final matches = RegExp(pageRegex).allMatches(next!);
|
||||
final group = matches.first.group(1)!;
|
||||
final nextPageKey = int.parse(group);
|
||||
return nextPageKey - 1;
|
||||
}
|
||||
if (previous != null) {
|
||||
// This is only executed if it's the last page or there is no data.
|
||||
final matches = RegExp(pageRegex).allMatches(previous!);
|
||||
if (matches.isEmpty) {
|
||||
//In case there is a match but a page is not explicitly set, the page is 1 per default. Therefore, if the previous page is 1, this page is 1+1=2
|
||||
return 2;
|
||||
}
|
||||
final group = matches.first.group(1)!;
|
||||
final previousPageKey = int.parse(group);
|
||||
return previousPageKey + 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
const PagedSearchResult({
|
||||
required this.count,
|
||||
required this.next,
|
||||
required this.previous,
|
||||
required this.results,
|
||||
});
|
||||
|
||||
factory PagedSearchResult.fromJson(Map<dynamic, dynamic> json, T Function(JSON) fromJson) {
|
||||
return PagedSearchResult(
|
||||
count: json['count'],
|
||||
next: json['next'],
|
||||
previous: json['previous'],
|
||||
results: List<JSON>.from(json['results']).map<T>(fromJson).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
PagedSearchResult copyWith({
|
||||
int? count,
|
||||
String? next,
|
||||
String? previous,
|
||||
List<DocumentModel>? results,
|
||||
}) {
|
||||
return PagedSearchResult(
|
||||
count: count ?? this.count,
|
||||
next: next ?? this.next,
|
||||
previous: previous ?? this.previous,
|
||||
results: results ?? this.results,
|
||||
);
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the number of pages based on the given [pageSize]. The last page
|
||||
/// might not exhaust its capacity.
|
||||
///
|
||||
int inferPageCount({required int pageSize}) {
|
||||
if (pageSize == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (count / pageSize).round() + 1;
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [count, next, previous, results];
|
||||
}
|
||||
10
lib/features/documents/model/query_parameters/asn_query.dart
Normal file
10
lib/features/documents/model/query_parameters/asn_query.dart
Normal file
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class AsnQuery extends IdQueryParameter {
|
||||
const AsnQuery.fromId(super.id) : super.fromId();
|
||||
const AsnQuery.unset() : super.unset();
|
||||
const AsnQuery.notAssigned() : super.notAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'archive_serial_number';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class CorrespondentQuery extends IdQueryParameter {
|
||||
const CorrespondentQuery.fromId(super.id) : super.fromId();
|
||||
const CorrespondentQuery.unset() : super.unset();
|
||||
const CorrespondentQuery.notAssigned() : super.notAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'correspondent';
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class DocumentTypeQuery extends IdQueryParameter {
|
||||
const DocumentTypeQuery.fromId(super.id) : super.fromId();
|
||||
const DocumentTypeQuery.unset() : super.unset();
|
||||
const DocumentTypeQuery.notAssigned() : super.notAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'document_type';
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
abstract class IdQueryParameter extends Equatable {
|
||||
final bool _onlyNotAssigned;
|
||||
final int? _id;
|
||||
|
||||
const IdQueryParameter.notAssigned()
|
||||
: _onlyNotAssigned = true,
|
||||
_id = null;
|
||||
|
||||
const IdQueryParameter.fromId(int? id)
|
||||
: _onlyNotAssigned = false,
|
||||
_id = id;
|
||||
|
||||
const IdQueryParameter.unset() : this.fromId(null);
|
||||
|
||||
bool get isUnset => _id == null && _onlyNotAssigned == false;
|
||||
|
||||
bool get isSet => _id != null && _onlyNotAssigned == false;
|
||||
|
||||
bool get onlyNotAssigned => _onlyNotAssigned;
|
||||
|
||||
int? get id => _id;
|
||||
|
||||
@protected
|
||||
String get queryParameterKey;
|
||||
|
||||
String toQueryParameter() {
|
||||
if (onlyNotAssigned) {
|
||||
return "&${queryParameterKey}__isnull=1";
|
||||
}
|
||||
|
||||
return isUnset ? "" : "&${queryParameterKey}__id=$id";
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [_onlyNotAssigned, _id];
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class IdsQueryParameter with EquatableMixin {
|
||||
final List<int> _ids;
|
||||
final bool onlyNotAssigned;
|
||||
|
||||
const IdsQueryParameter.fromIds(List<int> ids)
|
||||
: onlyNotAssigned = false,
|
||||
_ids = ids;
|
||||
|
||||
const IdsQueryParameter.notAssigned()
|
||||
: onlyNotAssigned = true,
|
||||
_ids = const [];
|
||||
|
||||
const IdsQueryParameter.unset()
|
||||
: onlyNotAssigned = false,
|
||||
_ids = const [];
|
||||
|
||||
bool get isUnset => _ids.isEmpty && onlyNotAssigned == false;
|
||||
|
||||
bool get isSet => _ids.isNotEmpty && onlyNotAssigned == false;
|
||||
|
||||
List<int> get ids => _ids;
|
||||
|
||||
String toQueryParameter();
|
||||
|
||||
@override
|
||||
List<Object?> get props => [onlyNotAssigned, _ids];
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
enum QueryType {
|
||||
title('title__icontains'),
|
||||
titleAndContent('title_content'),
|
||||
extended('query'),
|
||||
asn('asn');
|
||||
|
||||
final String queryParam;
|
||||
const QueryType(this.queryParam);
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
enum SortField {
|
||||
archiveSerialNumber("archive_serial_number"),
|
||||
correspondentName("correspondent__name"),
|
||||
title("title"),
|
||||
documentType("documentType"),
|
||||
created("created"),
|
||||
added("added"),
|
||||
modified("modified");
|
||||
|
||||
final String queryString;
|
||||
|
||||
const SortField(this.queryString);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return name.toLowerCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
enum SortOrder {
|
||||
ascending(""),
|
||||
descending("-");
|
||||
|
||||
final String queryString;
|
||||
const SortOrder(this.queryString);
|
||||
|
||||
SortOrder toggle() {
|
||||
return this == ascending ? descending : ascending;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/id_query_parameter.dart';
|
||||
|
||||
class StoragePathQuery extends IdQueryParameter {
|
||||
const StoragePathQuery.fromId(super.id) : super.fromId();
|
||||
const StoragePathQuery.unset() : super.unset();
|
||||
const StoragePathQuery.notAssigned() : super.notAssigned();
|
||||
|
||||
@override
|
||||
String get queryParameterKey => 'storage_path';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/ids_query_parameter.dart';
|
||||
|
||||
class TagsQuery extends IdsQueryParameter {
|
||||
const TagsQuery.fromIds(super.ids) : super.fromIds();
|
||||
const TagsQuery.unset() : super.unset();
|
||||
const TagsQuery.notAssigned() : super.notAssigned();
|
||||
|
||||
@override
|
||||
String toQueryParameter() {
|
||||
if (onlyNotAssigned) {
|
||||
return '&is_tagged=false';
|
||||
}
|
||||
return isUnset ? "" : '&tags__id__all=${ids.join(',')}';
|
||||
}
|
||||
}
|
||||
88
lib/features/documents/model/saved_view.model.dart
Normal file
88
lib/features/documents/model/saved_view.model.dart
Normal file
@@ -0,0 +1,88 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter_paperless_mobile/core/type/json.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/document_filter.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/filter_rule.model.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/sort_field.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/query_parameters/sort_order.dart';
|
||||
|
||||
class SavedView with EquatableMixin {
|
||||
final int? id;
|
||||
final String name;
|
||||
|
||||
final bool showOnDashboard;
|
||||
final bool showInSidebar;
|
||||
|
||||
final SortField sortField;
|
||||
final bool sortReverse;
|
||||
final List<FilterRule> filterRules;
|
||||
|
||||
SavedView({
|
||||
this.id,
|
||||
required this.name,
|
||||
required this.showOnDashboard,
|
||||
required this.showInSidebar,
|
||||
required this.sortField,
|
||||
required this.sortReverse,
|
||||
required this.filterRules,
|
||||
}) {
|
||||
filterRules.sort(
|
||||
(a, b) => (a.ruleType.compareTo(b.ruleType) != 0
|
||||
? a.ruleType.compareTo(b.ruleType)
|
||||
: a.value?.compareTo(b.value ?? "") ?? -1),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props =>
|
||||
[name, showOnDashboard, showInSidebar, sortField, sortReverse, filterRules];
|
||||
|
||||
SavedView.fromJson(JSON json)
|
||||
: this(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
showOnDashboard: json['show_on_dashboard'],
|
||||
showInSidebar: json['show_in_sidebar'],
|
||||
sortField:
|
||||
SortField.values.where((order) => order.queryString == json['sort_field']).first,
|
||||
sortReverse: json['sort_reverse'],
|
||||
filterRules:
|
||||
json['filter_rules'].cast<JSON>().map<FilterRule>(FilterRule.fromJson).toList(),
|
||||
);
|
||||
|
||||
DocumentFilter toDocumentFilter() {
|
||||
return filterRules.fold(
|
||||
DocumentFilter(
|
||||
sortOrder: sortReverse ? SortOrder.ascending : SortOrder.descending,
|
||||
sortField: sortField,
|
||||
),
|
||||
(filter, filterRule) => filterRule.applyToFilter(filter),
|
||||
);
|
||||
}
|
||||
|
||||
SavedView.fromDocumentFilter(
|
||||
DocumentFilter filter, {
|
||||
required String name,
|
||||
required bool showInSidebar,
|
||||
required bool showOnDashboard,
|
||||
}) : this(
|
||||
id: null,
|
||||
name: name,
|
||||
filterRules: FilterRule.fromFilter(filter),
|
||||
sortField: filter.sortField,
|
||||
showInSidebar: showInSidebar,
|
||||
showOnDashboard: showOnDashboard,
|
||||
sortReverse: filter.sortOrder == SortOrder.ascending,
|
||||
);
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'show_on_dashboard': showOnDashboard,
|
||||
'show_in_sidebar': showInSidebar,
|
||||
'sort_reverse': sortReverse,
|
||||
'sort_field': sortField.queryString,
|
||||
'filter_rules': filterRules.map((rule) => rule.toJson()).toList(),
|
||||
};
|
||||
}
|
||||
}
|
||||
59
lib/features/documents/model/similar_document.model.dart
Normal file
59
lib/features/documents/model/similar_document.model.dart
Normal file
@@ -0,0 +1,59 @@
|
||||
import 'package:flutter_paperless_mobile/core/type/json.dart';
|
||||
import 'package:flutter_paperless_mobile/features/documents/model/document.model.dart';
|
||||
|
||||
class SimilarDocumentModel extends DocumentModel {
|
||||
final SearchHit searchHit;
|
||||
|
||||
const SimilarDocumentModel({
|
||||
required super.id,
|
||||
required super.title,
|
||||
required super.documentType,
|
||||
required super.correspondent,
|
||||
required super.created,
|
||||
required super.modified,
|
||||
required super.added,
|
||||
required super.originalFileName,
|
||||
required this.searchHit,
|
||||
super.archiveSerialNumber,
|
||||
super.archivedFileName,
|
||||
super.content,
|
||||
super.storagePath,
|
||||
super.tags,
|
||||
});
|
||||
|
||||
@override
|
||||
JSON toJson() {
|
||||
final json = super.toJson();
|
||||
json['__search_hit__'] = searchHit.toJson();
|
||||
return json;
|
||||
}
|
||||
|
||||
SimilarDocumentModel.fromJson(JSON json)
|
||||
: searchHit = SearchHit.fromJson(json),
|
||||
super.fromJson(json);
|
||||
}
|
||||
|
||||
class SearchHit {
|
||||
final double? score;
|
||||
final String? highlights;
|
||||
final int? rank;
|
||||
|
||||
SearchHit({
|
||||
this.score,
|
||||
required this.highlights,
|
||||
required this.rank,
|
||||
});
|
||||
|
||||
JSON toJson() {
|
||||
return {
|
||||
'score': score,
|
||||
'highlights': highlights,
|
||||
'rank': rank,
|
||||
};
|
||||
}
|
||||
|
||||
SearchHit.fromJson(JSON json)
|
||||
: score = json['score'],
|
||||
highlights = json['highlights'],
|
||||
rank = json['rank'];
|
||||
}
|
||||
Reference in New Issue
Block a user