WIP - Add system notifications for document upload progress/status

This commit is contained in:
Anton Stubenbord
2023-01-09 01:35:47 +01:00
parent 3c6c4e63d7
commit 8cf3020335
34 changed files with 615 additions and 44 deletions

View File

@@ -22,3 +22,5 @@ export 'paperless_server_information_model.dart';
export 'paperless_server_statistics_model.dart';
export 'saved_view_model.dart';
export 'similar_document_model.dart';
export 'task/task.dart';
export 'task/task_status.dart';

View File

@@ -0,0 +1,52 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:paperless_api/src/request_utils.dart';
import 'task_status.dart';
part 'task.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class Task extends Equatable {
final int id;
final String? taskId;
final String? taskFileName;
final DateTime dateCreated;
final DateTime? dateDone;
final String? type;
final TaskStatus? status;
final String? result;
final bool acknowledged;
@JsonKey(fromJson: tryParseNullable)
final int? relatedDocumentId;
const Task({
required this.id,
this.taskId,
this.taskFileName,
required this.dateCreated,
this.dateDone,
this.type,
this.status,
this.acknowledged = false,
this.relatedDocumentId,
this.result,
});
factory Task.fromJson(Map<String, dynamic> json) => _$TaskFromJson(json);
Map<String, dynamic> toJson() => _$TaskToJson(this);
@override
List<Object?> get props => [
id,
taskId,
taskFileName,
dateCreated,
dateDone,
type,
status,
result,
acknowledged,
relatedDocumentId,
];
}

View File

@@ -0,0 +1,43 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'task.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Task _$TaskFromJson(Map<String, dynamic> json) => Task(
id: json['id'] as int,
taskId: json['task_id'] as String?,
taskFileName: json['task_file_name'] as String?,
dateCreated: DateTime.parse(json['date_created'] as String),
dateDone: json['date_done'] == null
? null
: DateTime.parse(json['date_done'] as String),
type: json['type'] as String?,
status: $enumDecodeNullable(_$TaskStatusEnumMap, json['status']),
acknowledged: json['acknowledged'] as bool? ?? false,
relatedDocumentId:
tryParseNullable(json['related_document_id'] as String?),
result: json['result'] as String?,
);
Map<String, dynamic> _$TaskToJson(Task instance) => <String, dynamic>{
'id': instance.id,
'task_id': instance.taskId,
'task_file_name': instance.taskFileName,
'date_created': instance.dateCreated.toIso8601String(),
'date_done': instance.dateDone?.toIso8601String(),
'type': instance.type,
'status': _$TaskStatusEnumMap[instance.status],
'result': instance.result,
'acknowledged': instance.acknowledged,
'related_document_id': instance.relatedDocumentId,
};
const _$TaskStatusEnumMap = {
TaskStatus.started: 'STARTED',
TaskStatus.pending: 'PENDING',
TaskStatus.failure: 'FAILURE',
TaskStatus.success: 'SUCCESS',
};

View File

@@ -0,0 +1,13 @@
import 'package:json_annotation/json_annotation.dart';
@JsonEnum(valueField: 'value')
enum TaskStatus {
started("STARTED"),
pending("PENDING"),
failure("FAILURE"),
success("SUCCESS");
final String value;
const TaskStatus(this.value);
}

View File

@@ -8,7 +8,9 @@ import 'package:paperless_api/src/models/paged_search_result.dart';
import 'package:paperless_api/src/models/similar_document_model.dart';
abstract class PaperlessDocumentsApi {
Future<void> create(
/// Uploads a document using a form data request and from server version 1.11.3
/// returns the celery task id which can be used to track the status of the document.
Future<String?> create(
Uint8List documentBytes, {
required String filename,
required String title,
@@ -27,7 +29,9 @@ abstract class PaperlessDocumentsApi {
Future<Uint8List> getPreview(int docId);
String getThumbnailUrl(int docId);
Future<DocumentModel> waitForConsumptionFinished(
String filename, String title);
String filename,
String title,
);
Future<Uint8List> download(DocumentModel document);
Future<List<String>> autocomplete(String query, [int limit = 10]);

View File

@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
@@ -6,6 +7,7 @@ import 'package:paperless_api/paperless_api.dart';
import 'package:paperless_api/src/constants.dart';
import 'package:paperless_api/src/converters/document_model_json_converter.dart';
import 'package:paperless_api/src/converters/similar_document_model_json_converter.dart';
import 'package:paperless_api/src/request_utils.dart';
class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
final Dio client;
@@ -13,7 +15,7 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
PaperlessDocumentsApiImpl(this.client);
@override
Future<void> create(
Future<String?> create(
Uint8List documentBytes, {
required String filename,
required String title,
@@ -46,8 +48,12 @@ class PaperlessDocumentsApiImpl implements PaperlessDocumentsApi {
try {
final response =
await client.post('/api/documents/post_document/', data: formData);
if (response.statusCode != 200) {
if (response.statusCode == 200) {
if (response.data is String && response.data != "OK") {
return response.data;
}
return null;
} else {
throw PaperlessServerException(
ErrorCode.documentUploadFailed,
httpStatusCode: response.statusCode,

View File

@@ -9,3 +9,5 @@ export 'saved_views_api/paperless_saved_views_api.dart';
export 'saved_views_api/paperless_saved_views_api_impl.dart';
export 'server_stats_api/paperless_server_stats_api.dart';
export 'server_stats_api/paperless_server_stats_api_impl.dart';
export 'tasks_api/paperless_tasks_api.dart';
export 'tasks_api/paperless_tasks_api_impl.dart';

View File

@@ -0,0 +1,7 @@
import 'package:paperless_api/src/models/task/task.dart';
abstract class PaperlessTasksApi {
Future<Task?> find({int? id, String? taskId});
Future<Iterable<Task>> findAll([Iterable<int>? ids]);
Stream<Task> listenForTaskChanges(String taskId);
}

View File

@@ -0,0 +1,53 @@
import 'package:dio/dio.dart';
import 'package:paperless_api/src/models/task/task.dart';
import 'package:paperless_api/src/models/task/task_status.dart';
import 'paperless_tasks_api.dart';
class PaperlessTasksApiImpl implements PaperlessTasksApi {
final Dio client;
const PaperlessTasksApiImpl(this.client);
@override
Future<Task?> find({int? id, String? taskId}) async {
assert(id != null || taskId != null);
String url = "/api/tasks/";
if (taskId != null) {
url += "?task_id=$taskId";
} else {
url += "$id/";
}
final response = await client.get(url);
if (response.statusCode == 200) {
return Task.fromJson(response.data);
}
return null;
}
@override
Future<Iterable<Task>> findAll([Iterable<int>? ids]) async {
final response = await client.get("/api/tasks/");
if (response.statusCode == 200) {
return (response.data as List).map((e) => Task.fromJson(e));
}
return [];
}
@override
Stream<Task> listenForTaskChanges(String taskId) async* {
bool isSuccess = false;
while (!isSuccess) {
final task = await find(taskId: taskId);
if (task == null) {
throw Exception("Task with taskId $taskId does not exist.");
}
yield task;
if (task.status == TaskStatus.success) {
isSuccess = true;
}
await Future.delayed(const Duration(seconds: 1));
}
}
}

View File

@@ -81,3 +81,14 @@ class _CollectionFromJsonSerializationParams<T> {
_CollectionFromJsonSerializationParams(this.fromJson, this.list);
}
int getExtendedVersionNumber(String version) {
List versionCells = version.split('.');
versionCells = versionCells.map((i) => int.parse(i)).toList();
return versionCells[0] * 100000 + versionCells[1] * 1000 + versionCells[2];
}
int? tryParseNullable(String? source, {int? radix}) {
if (source == null) return null;
return int.tryParse(source, radix: radix);
}