mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
First sketch for custom data threaded through item processing
Refs: #2334
This commit is contained in:
@ -15,10 +15,10 @@ import docspell.common.ProcessItemArgs.ProcessMeta
|
|||||||
import docspell.common.{CollectiveId, Ident, Language}
|
import docspell.common.{CollectiveId, Ident, Language}
|
||||||
import docspell.logging.Logger
|
import docspell.logging.Logger
|
||||||
|
|
||||||
import io.circe.Codec
|
|
||||||
import io.circe.generic.extras.Configuration
|
import io.circe.generic.extras.Configuration
|
||||||
import io.circe.generic.extras.semiauto.deriveConfiguredCodec
|
import io.circe.generic.extras.semiauto.deriveConfiguredCodec
|
||||||
import io.circe.generic.semiauto.deriveCodec
|
import io.circe.generic.semiauto.deriveCodec
|
||||||
|
import io.circe.{Codec, Json}
|
||||||
|
|
||||||
case class NewFile(metadata: Meta = Meta.empty, file: String) {
|
case class NewFile(metadata: Meta = Meta.empty, file: String) {
|
||||||
|
|
||||||
@ -41,7 +41,8 @@ object NewFile {
|
|||||||
case class Meta(
|
case class Meta(
|
||||||
language: Option[Language],
|
language: Option[Language],
|
||||||
skipDuplicate: Option[Boolean],
|
skipDuplicate: Option[Boolean],
|
||||||
attachmentsOnly: Option[Boolean]
|
attachmentsOnly: Option[Boolean],
|
||||||
|
customData: Option[Json]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def toProcessMeta(
|
def toProcessMeta(
|
||||||
@ -62,12 +63,13 @@ object NewFile {
|
|||||||
fileFilter = None,
|
fileFilter = None,
|
||||||
tags = None,
|
tags = None,
|
||||||
reprocess = false,
|
reprocess = false,
|
||||||
attachmentsOnly = attachmentsOnly
|
attachmentsOnly = attachmentsOnly,
|
||||||
|
customData = customData
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Meta {
|
object Meta {
|
||||||
val empty = Meta(None, None, None)
|
val empty = Meta(None, None, None, None)
|
||||||
implicit val jsonCodec: Codec[Meta] = deriveCodec
|
implicit val jsonCodec: Codec[Meta] = deriveCodec
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ import docspell.common._
|
|||||||
import docspell.logging.Logger
|
import docspell.logging.Logger
|
||||||
|
|
||||||
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
import io.circe.generic.semiauto.{deriveDecoder, deriveEncoder}
|
||||||
import io.circe.{Decoder, Encoder}
|
import io.circe.{Decoder, Encoder, Json}
|
||||||
|
|
||||||
case class NewItem(metadata: Option[Meta], files: List[String]) {
|
case class NewItem(metadata: Option[Meta], files: List[String]) {
|
||||||
|
|
||||||
@ -25,7 +25,7 @@ case class NewItem(metadata: Option[Meta], files: List[String]) {
|
|||||||
sourceAbbrev: String
|
sourceAbbrev: String
|
||||||
): ProcessItemArgs.ProcessMeta =
|
): ProcessItemArgs.ProcessMeta =
|
||||||
metadata
|
metadata
|
||||||
.getOrElse(Meta(None, None, None, None, None, None, None))
|
.getOrElse(Meta.empty)
|
||||||
.toProcessArgs(cid, collLang, sourceAbbrev)
|
.toProcessArgs(cid, collLang, sourceAbbrev)
|
||||||
|
|
||||||
def resolveFiles[F[_]: Files: Monad](
|
def resolveFiles[F[_]: Files: Monad](
|
||||||
@ -58,7 +58,8 @@ object NewItem {
|
|||||||
source: Option[String],
|
source: Option[String],
|
||||||
skipDuplicate: Option[Boolean],
|
skipDuplicate: Option[Boolean],
|
||||||
tags: Option[List[String]],
|
tags: Option[List[String]],
|
||||||
attachmentsOnly: Option[Boolean]
|
attachmentsOnly: Option[Boolean],
|
||||||
|
customData: Option[Json]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def toProcessArgs(
|
def toProcessArgs(
|
||||||
@ -78,11 +79,14 @@ object NewItem {
|
|||||||
fileFilter = None,
|
fileFilter = None,
|
||||||
tags = tags,
|
tags = tags,
|
||||||
reprocess = false,
|
reprocess = false,
|
||||||
attachmentsOnly = attachmentsOnly
|
attachmentsOnly = attachmentsOnly,
|
||||||
|
customData = customData
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Meta {
|
object Meta {
|
||||||
|
val empty: Meta = Meta(None, None, None, None, None, None, None, None)
|
||||||
|
|
||||||
implicit val jsonEncoder: Encoder[Meta] = deriveEncoder
|
implicit val jsonEncoder: Encoder[Meta] = deriveEncoder
|
||||||
implicit val jsonDecoder: Decoder[Meta] = deriveDecoder
|
implicit val jsonDecoder: Decoder[Meta] = deriveDecoder
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ import docspell.scheduler.{Job, JobStore}
|
|||||||
import docspell.store.Store
|
import docspell.store.Store
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
|
|
||||||
|
import io.circe.Json
|
||||||
|
|
||||||
trait OUpload[F[_]] {
|
trait OUpload[F[_]] {
|
||||||
|
|
||||||
def submit(
|
def submit(
|
||||||
@ -69,7 +71,8 @@ object OUpload {
|
|||||||
tags: List[String],
|
tags: List[String],
|
||||||
language: Option[Language],
|
language: Option[Language],
|
||||||
attachmentsOnly: Option[Boolean],
|
attachmentsOnly: Option[Boolean],
|
||||||
flattenArchives: Option[Boolean]
|
flattenArchives: Option[Boolean],
|
||||||
|
customData: Option[Json]
|
||||||
)
|
)
|
||||||
|
|
||||||
case class UploadData[F[_]](
|
case class UploadData[F[_]](
|
||||||
@ -157,7 +160,8 @@ object OUpload {
|
|||||||
data.meta.fileFilter.some,
|
data.meta.fileFilter.some,
|
||||||
data.meta.tags.some,
|
data.meta.tags.some,
|
||||||
false,
|
false,
|
||||||
data.meta.attachmentsOnly
|
data.meta.attachmentsOnly,
|
||||||
|
data.meta.customData
|
||||||
)
|
)
|
||||||
args = ProcessItemArgs(meta, files.toList)
|
args = ProcessItemArgs(meta, files.toList)
|
||||||
jobs <- right(
|
jobs <- right(
|
||||||
|
@ -54,7 +54,8 @@ object ProcessItemArgs {
|
|||||||
fileFilter: Option[Glob],
|
fileFilter: Option[Glob],
|
||||||
tags: Option[List[String]],
|
tags: Option[List[String]],
|
||||||
reprocess: Boolean,
|
reprocess: Boolean,
|
||||||
attachmentsOnly: Option[Boolean]
|
attachmentsOnly: Option[Boolean],
|
||||||
|
customData: Option[Json]
|
||||||
)
|
)
|
||||||
|
|
||||||
object ProcessMeta {
|
object ProcessMeta {
|
||||||
|
@ -75,6 +75,7 @@ object ItemAddonTask extends AddonTaskExtension {
|
|||||||
givenMeta = proposals,
|
givenMeta = proposals,
|
||||||
tags = tags.map(_.name).toList,
|
tags = tags.map(_.name).toList,
|
||||||
classifyProposals = MetaProposalList.empty,
|
classifyProposals = MetaProposalList.empty,
|
||||||
classifyTags = Nil
|
classifyTags = Nil,
|
||||||
|
customData = None // can't retain this information from a final item. TODO
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,8 @@ object CreateItem {
|
|||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil,
|
Nil,
|
||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil
|
Nil,
|
||||||
|
ctx.args.meta.customData
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +176,8 @@ object CreateItem {
|
|||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil,
|
Nil,
|
||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil
|
Nil,
|
||||||
|
ctx.args.meta.customData
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,8 @@ case class ItemData(
|
|||||||
tags: List[String],
|
tags: List[String],
|
||||||
// proposals obtained from the classifier
|
// proposals obtained from the classifier
|
||||||
classifyProposals: MetaProposalList,
|
classifyProposals: MetaProposalList,
|
||||||
classifyTags: List[String]
|
classifyTags: List[String],
|
||||||
|
customData: Option[Json]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/** sort by weight; order of equal weights is not important, just choose one others are
|
/** sort by weight; order of equal weights is not important, just choose one others are
|
||||||
@ -121,6 +122,7 @@ object ItemData {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.asJson,
|
.asJson,
|
||||||
|
"customData" -> data.customData.asJson,
|
||||||
"tags" -> data.tags.asJson,
|
"tags" -> data.tags.asJson,
|
||||||
"assumedTags" -> data.classifyTags.asJson,
|
"assumedTags" -> data.classifyTags.asJson,
|
||||||
"assumedCorrOrg" -> data.finalProposals
|
"assumedCorrOrg" -> data.finalProposals
|
||||||
|
@ -101,7 +101,8 @@ object ReProcessItem {
|
|||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil,
|
Nil,
|
||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil
|
Nil,
|
||||||
|
None // cannot retain customData from an already existing item
|
||||||
)).getOrElseF(
|
)).getOrElseF(
|
||||||
Sync[F].raiseError(new Exception(s"Item not found: ${ctx.args.itemId.id}"))
|
Sync[F].raiseError(new Exception(s"Item not found: ${ctx.args.itemId.id}"))
|
||||||
)
|
)
|
||||||
@ -134,7 +135,8 @@ object ReProcessItem {
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
true,
|
true,
|
||||||
None // attachOnly (not used when reprocessing attachments)
|
None, // attachOnly (not used when reprocessing attachments)
|
||||||
|
None // cannot retain customData from an already existing item
|
||||||
),
|
),
|
||||||
Nil
|
Nil
|
||||||
).pure[F]
|
).pure[F]
|
||||||
|
@ -328,6 +328,7 @@ object ScanMailboxTask {
|
|||||||
args.tags.getOrElse(Nil),
|
args.tags.getOrElse(Nil),
|
||||||
args.language,
|
args.language,
|
||||||
args.attachmentsOnly,
|
args.attachmentsOnly,
|
||||||
|
None,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
data = OUpload.UploadData(
|
data = OUpload.UploadData(
|
||||||
|
@ -8250,6 +8250,14 @@ components:
|
|||||||
attachments of the e-mail are imported and the e-mail body
|
attachments of the e-mail are imported and the e-mail body
|
||||||
is discarded. E-mails that don't have any attachments are
|
is discarded. E-mails that don't have any attachments are
|
||||||
skipped.
|
skipped.
|
||||||
|
customData:
|
||||||
|
type: string
|
||||||
|
format: json
|
||||||
|
default: null
|
||||||
|
description: |
|
||||||
|
Custom user data that gets threaded through the processing. Docspell
|
||||||
|
ignores it completely, but will pass it to the outcome of processing
|
||||||
|
to be able to react on it in addons or other ways.
|
||||||
|
|
||||||
Collective:
|
Collective:
|
||||||
description: |
|
description: |
|
||||||
|
@ -32,6 +32,7 @@ import docspell.store.queries.{
|
|||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
import docspell.store.{AddResult, UpdateResult}
|
import docspell.store.{AddResult, UpdateResult}
|
||||||
|
|
||||||
|
import io.circe.Json
|
||||||
import org.http4s.headers.`Content-Type`
|
import org.http4s.headers.`Content-Type`
|
||||||
import org.http4s.multipart.Multipart
|
import org.http4s.multipart.Multipart
|
||||||
import org.log4s.Logger
|
import org.log4s.Logger
|
||||||
@ -315,7 +316,8 @@ trait Conversions {
|
|||||||
m.tags.map(_.items).getOrElse(Nil),
|
m.tags.map(_.items).getOrElse(Nil),
|
||||||
m.language,
|
m.language,
|
||||||
m.attachmentsOnly,
|
m.attachmentsOnly,
|
||||||
m.flattenArchives
|
m.flattenArchives,
|
||||||
|
m.customData.map(Json.fromString) // TODO fix openapi spec
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -333,6 +335,7 @@ trait Conversions {
|
|||||||
Nil,
|
Nil,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -348,7 +348,8 @@ object MigrateCollectiveIdTaskArgs extends TransactorSupport {
|
|||||||
fileFilter = oldArgs.meta.fileFilter,
|
fileFilter = oldArgs.meta.fileFilter,
|
||||||
tags = oldArgs.meta.tags,
|
tags = oldArgs.meta.tags,
|
||||||
reprocess = oldArgs.meta.reprocess,
|
reprocess = oldArgs.meta.reprocess,
|
||||||
attachmentsOnly = oldArgs.meta.attachmentsOnly
|
attachmentsOnly = oldArgs.meta.attachmentsOnly,
|
||||||
|
customData = None
|
||||||
),
|
),
|
||||||
oldArgs.files.map(f =>
|
oldArgs.files.map(f =>
|
||||||
ProcessItemArgs
|
ProcessItemArgs
|
||||||
|
Reference in New Issue
Block a user