310 lines
13 KiB
C#
310 lines
13 KiB
C#
|
|
using Newtonsoft.Json;
|
|||
|
|
using paperless_ngx_export.Models;
|
|||
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Net.Http.Headers;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Text.Json.Nodes;
|
|||
|
|
|
|||
|
|
namespace paperless_ngx_export
|
|||
|
|
{
|
|||
|
|
public static class api
|
|||
|
|
{
|
|||
|
|
public static bool isInitialized { get; private set; } = false;
|
|||
|
|
static string paperless_api_base;
|
|||
|
|
static string token;
|
|||
|
|
|
|||
|
|
public static void init(string token, string paperless_api_base)
|
|||
|
|
{
|
|||
|
|
api.paperless_api_base = paperless_api_base.Trim();
|
|||
|
|
setToken(token.Trim());
|
|||
|
|
|
|||
|
|
isInitialized = !string.IsNullOrWhiteSpace(api.paperless_api_base) && !string.IsNullOrEmpty(api.token);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static void setToken(string token)
|
|||
|
|
{
|
|||
|
|
api.token = token.Trim().Trim('/');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static HttpRequestMessage getHttpRequestMessage(string query, int page = -1)
|
|||
|
|
{
|
|||
|
|
if (page > 0)
|
|||
|
|
{
|
|||
|
|
var pagedQuery = query.Contains('?') ? $"{query}&page={page}" : $"{query}?page={page}";
|
|||
|
|
return getHttpRequestMessage(pagedQuery, HttpMethod.Get);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
return getHttpRequestMessage(query, HttpMethod.Get);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static HttpRequestMessage getHttpRequestMessage(string query, HttpMethod httpMethod)
|
|||
|
|
{
|
|||
|
|
HttpRequestMessage message = new HttpRequestMessage(httpMethod, $"{api.paperless_api_base}{query}");
|
|||
|
|
//message.Headers.Authorization = getAuthenticationHeaderValue();
|
|||
|
|
message.Headers.Add("Authorization", $"Token {api.token}");
|
|||
|
|
|
|||
|
|
return message;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static AuthenticationHeaderValue getAuthenticationHeaderValue()
|
|||
|
|
{
|
|||
|
|
var header = new AuthenticationHeaderValue("Token", api.token);
|
|||
|
|
return header;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<IEnumerable<Tag>> getTagsAsync()
|
|||
|
|
{
|
|||
|
|
var query = "tags/";
|
|||
|
|
var tags = new List<Tag>();
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
using (HttpClient client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var count = -1;
|
|||
|
|
var page = 1;
|
|||
|
|
while (count != 0)
|
|||
|
|
{
|
|||
|
|
count = 0;
|
|||
|
|
var response = await client.SendAsync(getHttpRequestMessage(query, page));
|
|||
|
|
if (response != null && response.IsSuccessStatusCode)
|
|||
|
|
{
|
|||
|
|
var responseString = await response.Content.ReadAsStringAsync();
|
|||
|
|
var root = JsonConvert.DeserializeObject<RootObject<Tag>>(responseString);
|
|||
|
|
if (root.Results.Any())
|
|||
|
|
{
|
|||
|
|
count = root.Results.Count();
|
|||
|
|
tags.AddRange(root.Results);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
page++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return tags;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<IEnumerable<Document>> getCurrentDocumentsAsync(bool includeNonExpirableDocuments = true)
|
|||
|
|
{
|
|||
|
|
var query = "documents/";
|
|||
|
|
var excludedTagIds = (await getTagsAsync()).Where(x => (includeNonExpirableDocuments && x.name == "No Expiration") || x.name == "Action:Renewed" || x.name == "HR:Inactive").Select(x => x.Id).ToList();
|
|||
|
|
excludedTagIds.ForEach(id => query += query.Contains('?') ? $"&tags__id__none={id}" : $"?tags__id__none={id}");
|
|||
|
|
|
|||
|
|
var documents = new List<Document>();
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
using (HttpClient client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var count = -1;
|
|||
|
|
var page = 1;
|
|||
|
|
while (count != 0)
|
|||
|
|
{
|
|||
|
|
count = 0;
|
|||
|
|
var response = await client.SendAsync(getHttpRequestMessage(query, page));
|
|||
|
|
if (response != null && response.IsSuccessStatusCode)
|
|||
|
|
{
|
|||
|
|
var responseString = await response.Content.ReadAsStringAsync();
|
|||
|
|
var root = JsonConvert.DeserializeObject<RootObject<Document>>(responseString);
|
|||
|
|
if (root.Results.Any())
|
|||
|
|
{
|
|||
|
|
count = root.Results.Count();
|
|||
|
|
foreach (var document in root.Results)
|
|||
|
|
{
|
|||
|
|
bool isValid = true;
|
|||
|
|
foreach (var t in document.tags)
|
|||
|
|
if (excludedTagIds.Contains(t))
|
|||
|
|
isValid = false;
|
|||
|
|
|
|||
|
|
if(isValid)
|
|||
|
|
documents.Add(document);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
page++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return documents.OrderBy(d=>d.correspondent).ToList();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<IEnumerable<Document>> getAllDocumentsAsync()
|
|||
|
|
{
|
|||
|
|
var query = "documents/";
|
|||
|
|
var documents = new List<Document>();
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
using (HttpClient client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var count = -1;
|
|||
|
|
var page = 1;
|
|||
|
|
while (count != 0)
|
|||
|
|
{
|
|||
|
|
count = 0;
|
|||
|
|
var response = await client.SendAsync(getHttpRequestMessage(query, page));
|
|||
|
|
if (response != null && response.IsSuccessStatusCode)
|
|||
|
|
{
|
|||
|
|
var responseString = await response.Content.ReadAsStringAsync();
|
|||
|
|
var root = JsonConvert.DeserializeObject<RootObject<Document>>(responseString);
|
|||
|
|
if (root.Results.Any())
|
|||
|
|
{
|
|||
|
|
count = root.Results.Count();
|
|||
|
|
documents.AddRange(root.Results);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
page++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return documents;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<IEnumerable<Correspondent>> getCorrespondentsAsync()
|
|||
|
|
{
|
|||
|
|
var query = "correspondents/";
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
var correspondents = new List<Correspondent>();
|
|||
|
|
using (HttpClient client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var count = -1;
|
|||
|
|
var page = 1;
|
|||
|
|
while (count != 0)
|
|||
|
|
{
|
|||
|
|
count = 0;
|
|||
|
|
var response = await client.SendAsync(getHttpRequestMessage(query, page));
|
|||
|
|
if (response != null && response.IsSuccessStatusCode)
|
|||
|
|
{
|
|||
|
|
var responseString = await response.Content.ReadAsStringAsync();
|
|||
|
|
var root = JsonConvert.DeserializeObject<RootObject<Correspondent>>(responseString);
|
|||
|
|
if (root.Results.Any())
|
|||
|
|
{
|
|||
|
|
count = root.Results.Count();
|
|||
|
|
|
|||
|
|
correspondents.AddRange(root.Results);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
page++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return correspondents;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<IEnumerable<CustomField>> getCustomFieldsAsync()
|
|||
|
|
{
|
|||
|
|
var query = "custom_fields/";
|
|||
|
|
var fields = new List<CustomField>();
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
using (HttpClient client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var count = -1;
|
|||
|
|
var page = 1;
|
|||
|
|
while (count != 0)
|
|||
|
|
{
|
|||
|
|
count = 0;
|
|||
|
|
var response = await client.SendAsync(getHttpRequestMessage(query, page));
|
|||
|
|
if (response != null && response.IsSuccessStatusCode)
|
|||
|
|
{
|
|||
|
|
var responseString = await response.Content.ReadAsStringAsync();
|
|||
|
|
var root = JsonConvert.DeserializeObject<RootObject<CustomField>>(responseString);
|
|||
|
|
if (root.Results.Any())
|
|||
|
|
{
|
|||
|
|
count = root.Results.Count();
|
|||
|
|
fields.AddRange(root.Results);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
page++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return fields;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<IEnumerable<DocumentType>> getDocumentTypesAsync()
|
|||
|
|
{
|
|||
|
|
var query = "document_types/";
|
|||
|
|
var doctypes = new List<DocumentType>();
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
using (HttpClient client = new HttpClient())
|
|||
|
|
{
|
|||
|
|
var count = -1;
|
|||
|
|
var page = 1;
|
|||
|
|
while (count != 0)
|
|||
|
|
{
|
|||
|
|
count = 0;
|
|||
|
|
var response = await client.SendAsync(getHttpRequestMessage(query, page));
|
|||
|
|
if (response != null && response.IsSuccessStatusCode)
|
|||
|
|
{
|
|||
|
|
var responseString = await response.Content.ReadAsStringAsync();
|
|||
|
|
var root = JsonConvert.DeserializeObject<RootObject<DocumentType>>(responseString);
|
|||
|
|
if (root.Results.Any())
|
|||
|
|
{
|
|||
|
|
count = root.Results.Count();
|
|||
|
|
doctypes.AddRange(root.Results);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
page++;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return doctypes;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<string> getExpirationDatesCalDAVAsync(bool onlyCurrentDocuments = true)
|
|||
|
|
{
|
|||
|
|
string caldav = @"BEGIN:VCALENDAR
|
|||
|
|
VERSION:2.0
|
|||
|
|
PRODID:-//CAMCASS//Expiring Documents Calendar//EN
|
|||
|
|
X-WR-CALNAME:Expiring Documents
|
|||
|
|
X-WR-CALDESC:Onboarded staff's document expiration dates
|
|||
|
|
X-PUBLISHED-TTL:PT12H";
|
|||
|
|
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
|
|||
|
|
var documents = (await getMergedDocumentMetadataAsync(onlyCurrentDocuments, false)).OrderBy(x=>x.Correspondent).ToList();
|
|||
|
|
documents.Where(d => d.CustomFields.Any(f => f.Name.StartsWith("Expiration", StringComparison.OrdinalIgnoreCase)))
|
|||
|
|
.ToList()
|
|||
|
|
.ForEach(doc =>
|
|||
|
|
{
|
|||
|
|
var calDAVEvent = getCalDAVEvent(doc, doc.CustomFields.First(f => f.Name.StartsWith("expir", StringComparison.OrdinalIgnoreCase)).DateValue);
|
|||
|
|
caldav += @$"
|
|||
|
|
{calDAVEvent}";
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
caldav += @"
|
|||
|
|
END:VCALENDAR";
|
|||
|
|
return caldav;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private static string getCalDAVEvent(DocumentParsed document, DateTime date)
|
|||
|
|
{
|
|||
|
|
var nextDay = date.AddDays(1);
|
|||
|
|
var DTSTAMP = $"{DateTime.UtcNow.Year:0000}{DateTime.UtcNow.Month:00}{DateTime.UtcNow.Day:00}T{DateTime.UtcNow.Hour:00}{DateTime.UtcNow.Minute:00}00Z";
|
|||
|
|
return $@"BEGIN:VEVENT
|
|||
|
|
UID:{Guid.NewGuid().ToString().Replace("-","")}
|
|||
|
|
DTSTAMP:{DTSTAMP}
|
|||
|
|
DTSTART;VALUE=DATE:{date.Year:0000}{date.Month:00}{date.Day:00}
|
|||
|
|
DTEND;VALUE=DATE:{nextDay.Year:0000}{nextDay.Month:00}{nextDay.Day:00}
|
|||
|
|
SUMMARY:{document.DocumentType} - {document.Correspondent}
|
|||
|
|
END:VEVENT";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static async Task<IEnumerable<DocumentParsed>> getMergedDocumentMetadataAsync(bool onlyCurrentDocuments = true, bool includeNonExpirableDocuments=true)
|
|||
|
|
{
|
|||
|
|
if (!isInitialized) { throw new NotSupportedException("API base URL or token not initialized"); }
|
|||
|
|
|
|||
|
|
var docs = onlyCurrentDocuments ? await getCurrentDocumentsAsync(includeNonExpirableDocuments) : await getAllDocumentsAsync();
|
|||
|
|
var tags = await getTagsAsync();
|
|||
|
|
var correspondents = await getCorrespondentsAsync();
|
|||
|
|
var customFields = await getCustomFieldsAsync();
|
|||
|
|
var documenttypes = await getDocumentTypesAsync();
|
|||
|
|
|
|||
|
|
DocumentParsed._tags = tags.ToArray();
|
|||
|
|
DocumentParsed._correspondents = correspondents.ToArray();
|
|||
|
|
DocumentParsed._customFields = customFields.ToArray();
|
|||
|
|
DocumentParsed._documentTypes = documenttypes.ToArray();
|
|||
|
|
|
|||
|
|
|
|||
|
|
var documents = new List<DocumentParsed>();
|
|||
|
|
docs.ToList().ForEach(document => documents.Add(DocumentParsed.FromDocument(document)));
|
|||
|
|
|
|||
|
|
return documents;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|