mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-07 07:35:59 +00:00
Store tags guessed with classifier in database
This commit is contained in:
parent
9d83cb7fe4
commit
27c24c128d
@ -108,7 +108,8 @@ object CreateItem {
|
|||||||
fm.map(a => a.id -> a.fileId).toMap,
|
fm.map(a => a.id -> a.fileId).toMap,
|
||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil,
|
Nil,
|
||||||
None
|
MetaProposalList.empty,
|
||||||
|
Nil
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +169,8 @@ object CreateItem {
|
|||||||
origMap,
|
origMap,
|
||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil,
|
Nil,
|
||||||
None
|
MetaProposalList.empty,
|
||||||
|
Nil
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import java.time.ZoneId
|
|||||||
import cats.effect.Sync
|
import cats.effect.Sync
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import cats.{Applicative, FlatMap}
|
import cats.{Applicative, FlatMap}
|
||||||
|
|
||||||
import docspell.analysis.contact._
|
import docspell.analysis.contact._
|
||||||
import docspell.common.MetaProposal.Candidate
|
import docspell.common.MetaProposal.Candidate
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
@ -30,11 +31,8 @@ object FindProposal {
|
|||||||
processAttachment(cfg, rm, data.findDates(rm), ctx)
|
processAttachment(cfg, rm, data.findDates(rm), ctx)
|
||||||
.map(ml => rm.copy(proposals = ml))
|
.map(ml => rm.copy(proposals = ml))
|
||||||
)
|
)
|
||||||
clp <- data.classifyProposals match {
|
clp <- lookupClassifierProposals(ctx, data.classifyProposals)
|
||||||
case Some(cmp) => lookupClassifierProposals(ctx, cmp)
|
} yield data.copy(metas = rmv, classifyProposals = clp)
|
||||||
case None => MetaProposalList.empty.pure[F]
|
|
||||||
}
|
|
||||||
} yield data.copy(metas = rmv, classifyProposals = clp.some)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def lookupClassifierProposals[F[_]: Sync](
|
def lookupClassifierProposals[F[_]: Sync](
|
||||||
|
@ -29,7 +29,8 @@ case class ItemData(
|
|||||||
// a list of tags (names or ids) attached to the item if they exist
|
// a list of tags (names or ids) attached to the item if they exist
|
||||||
tags: List[String],
|
tags: List[String],
|
||||||
// proposals obtained from the classifier
|
// proposals obtained from the classifier
|
||||||
classifyProposals: Option[MetaProposalList]
|
classifyProposals: MetaProposalList,
|
||||||
|
classifyTags: List[String]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def findMeta(attachId: Ident): Option[RAttachmentMeta] =
|
def findMeta(attachId: Ident): Option[RAttachmentMeta] =
|
||||||
|
@ -24,7 +24,7 @@ object LinkProposal {
|
|||||||
.flatten(data.metas.map(_.proposals))
|
.flatten(data.metas.map(_.proposals))
|
||||||
.filter(_.proposalType != MetaProposalType.DocDate)
|
.filter(_.proposalType != MetaProposalType.DocDate)
|
||||||
.sortByWeights
|
.sortByWeights
|
||||||
.fillEmptyFrom(data.classifyProposals.getOrElse(MetaProposalList.empty))
|
.fillEmptyFrom(data.classifyProposals)
|
||||||
|
|
||||||
ctx.logger.info(s"Starting linking proposals") *>
|
ctx.logger.info(s"Starting linking proposals") *>
|
||||||
MetaProposalType.all
|
MetaProposalType.all
|
||||||
|
@ -66,7 +66,8 @@ object ReProcessItem {
|
|||||||
asrcMap.view.mapValues(_.fileId).toMap,
|
asrcMap.view.mapValues(_.fileId).toMap,
|
||||||
MetaProposalList.empty,
|
MetaProposalList.empty,
|
||||||
Nil,
|
Nil,
|
||||||
None
|
MetaProposalList.empty,
|
||||||
|
Nil
|
||||||
)).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}"))
|
||||||
)
|
)
|
||||||
|
@ -2,16 +2,18 @@ package docspell.joex.process
|
|||||||
|
|
||||||
import cats.effect.Sync
|
import cats.effect.Sync
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.joex.scheduler.Task
|
import docspell.joex.scheduler.{Context, Task}
|
||||||
import docspell.store.AddResult
|
import docspell.store.AddResult
|
||||||
import docspell.store.records._
|
import docspell.store.records._
|
||||||
|
|
||||||
/** Saves the proposals in the database
|
/** Saves the proposals in the database
|
||||||
*/
|
*/
|
||||||
object SaveProposals {
|
object SaveProposals {
|
||||||
|
type Args = ProcessItemArgs
|
||||||
|
|
||||||
def apply[F[_]: Sync](data: ItemData): Task[F, ProcessItemArgs, ItemData] =
|
def apply[F[_]: Sync](data: ItemData): Task[F, Args, ItemData] =
|
||||||
Task { ctx =>
|
Task { ctx =>
|
||||||
for {
|
for {
|
||||||
_ <- ctx.logger.info("Storing proposals")
|
_ <- ctx.logger.info("Storing proposals")
|
||||||
@ -21,28 +23,32 @@ object SaveProposals {
|
|||||||
s"Storing attachment proposals: ${rm.proposals}"
|
s"Storing attachment proposals: ${rm.proposals}"
|
||||||
) *> ctx.store.transact(RAttachmentMeta.updateProposals(rm.id, rm.proposals))
|
) *> ctx.store.transact(RAttachmentMeta.updateProposals(rm.id, rm.proposals))
|
||||||
)
|
)
|
||||||
_ <- data.classifyProposals match {
|
_ <-
|
||||||
case Some(clp) =>
|
if (data.classifyProposals.isEmpty && data.classifyTags.isEmpty) 0.pure[F]
|
||||||
val itemId = data.item.id
|
else saveItemProposal(ctx, data)
|
||||||
ctx.logger.debug(s"Storing classifier proposals: $clp") *>
|
|
||||||
ctx.store
|
|
||||||
.add(
|
|
||||||
RItemProposal.createNew(itemId, clp),
|
|
||||||
RItemProposal.exists(itemId)
|
|
||||||
)
|
|
||||||
.flatMap({
|
|
||||||
case AddResult.EntityExists(_) =>
|
|
||||||
ctx.store.transact(RItemProposal.updateProposals(itemId, clp))
|
|
||||||
case AddResult.Failure(ex) =>
|
|
||||||
ctx.logger
|
|
||||||
.warn(s"Could not store classifier proposals: ${ex.getMessage}") *>
|
|
||||||
0.pure[F]
|
|
||||||
case AddResult.Success =>
|
|
||||||
1.pure[F]
|
|
||||||
})
|
|
||||||
case None =>
|
|
||||||
0.pure[F]
|
|
||||||
}
|
|
||||||
} yield data
|
} yield data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def saveItemProposal[F[_]: Sync](ctx: Context[F, Args], data: ItemData): F[Unit] = {
|
||||||
|
def upsert(v: RItemProposal): F[Int] =
|
||||||
|
ctx.store.add(RItemProposal.insert(v), RItemProposal.exists(v.itemId)).flatMap {
|
||||||
|
case AddResult.Success => 1.pure[F]
|
||||||
|
case AddResult.EntityExists(_) =>
|
||||||
|
ctx.store.transact(RItemProposal.update(v))
|
||||||
|
case AddResult.Failure(ex) =>
|
||||||
|
ctx.logger.warn(s"Could not store item proposals: ${ex.getMessage}") *> 0
|
||||||
|
.pure[F]
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
_ <- ctx.logger.debug(s"Storing classifier proposals: ${data.classifyProposals}")
|
||||||
|
tags <- ctx.store.transact(
|
||||||
|
RTag.findAllByNameOrId(data.classifyTags, ctx.args.meta.collective)
|
||||||
|
)
|
||||||
|
tagRefs = tags.map(t => IdRef(t.tagId, t.name))
|
||||||
|
now <- Timestamp.current[F]
|
||||||
|
value = RItemProposal(data.item.id, data.classifyProposals, tagRefs.toList, now)
|
||||||
|
_ <- upsert(value)
|
||||||
|
} yield ()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,8 @@ object SetGivenData {
|
|||||||
Task { ctx =>
|
Task { ctx =>
|
||||||
val itemId = data.item.id
|
val itemId = data.item.id
|
||||||
val collective = ctx.args.meta.collective
|
val collective = ctx.args.meta.collective
|
||||||
val tags = (ctx.args.meta.tags.getOrElse(Nil) ++ data.tags).distinct
|
val tags =
|
||||||
|
(ctx.args.meta.tags.getOrElse(Nil) ++ data.tags ++ data.classifyTags).distinct
|
||||||
for {
|
for {
|
||||||
_ <- ctx.logger.info(s"Set tags from given data: ${tags}")
|
_ <- ctx.logger.info(s"Set tags from given data: ${tags}")
|
||||||
e <- ops.linkTags(itemId, tags, collective).attempt
|
e <- ops.linkTags(itemId, tags, collective).attempt
|
||||||
|
@ -54,9 +54,9 @@ object TextAnalysis {
|
|||||||
.copy(
|
.copy(
|
||||||
metas = v.map(_._1),
|
metas = v.map(_._1),
|
||||||
dateLabels = v.map(_._2),
|
dateLabels = v.map(_._2),
|
||||||
classifyProposals = classProposals.some
|
classifyProposals = classProposals,
|
||||||
|
classifyTags = tag
|
||||||
)
|
)
|
||||||
.appendTags(tag)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def annotateAttachment[F[_]: Sync](
|
def annotateAttachment[F[_]: Sync](
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package docspell.store.records
|
package docspell.store.records
|
||||||
|
|
||||||
import cats.data.NonEmptyList
|
import cats.data.NonEmptyList
|
||||||
//import cats.implicits._
|
|
||||||
|
|
||||||
import docspell.common._
|
import docspell.common._
|
||||||
import docspell.store.qb.DSL._
|
import docspell.store.qb.DSL._
|
||||||
@ -39,22 +38,23 @@ object RItemProposal {
|
|||||||
fr"${v.itemId},${v.classifyProposals},${v.classifyTags},${v.created}"
|
fr"${v.itemId},${v.classifyProposals},${v.classifyTags},${v.created}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update(v: RItemProposal): ConnectionIO[Int] =
|
||||||
|
DML.update(
|
||||||
|
T,
|
||||||
|
T.itemId === v.itemId,
|
||||||
|
DML.set(
|
||||||
|
T.classifyProposals.setTo(v.classifyProposals),
|
||||||
|
T.classifyTags.setTo(v.classifyTags)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
def deleteByItem(itemId: Ident): ConnectionIO[Int] =
|
def deleteByItem(itemId: Ident): ConnectionIO[Int] =
|
||||||
DML.delete(T, T.itemId === itemId)
|
DML.delete(T, T.itemId === itemId)
|
||||||
|
|
||||||
def createNew(itemId: Ident, proposals: MetaProposalList): ConnectionIO[Int] =
|
|
||||||
for {
|
|
||||||
now <- Timestamp.current[ConnectionIO]
|
|
||||||
value = RItemProposal(itemId, proposals, Nil, now)
|
|
||||||
n <- insert(value)
|
|
||||||
} yield n
|
|
||||||
|
|
||||||
def exists(itemId: Ident): ConnectionIO[Boolean] =
|
def exists(itemId: Ident): ConnectionIO[Boolean] =
|
||||||
Select(select(countAll), from(T), T.itemId === itemId).build
|
Select(select(countAll), from(T), T.itemId === itemId).build
|
||||||
.query[Int]
|
.query[Int]
|
||||||
.unique
|
.unique
|
||||||
.map(_ > 0)
|
.map(_ > 0)
|
||||||
|
|
||||||
def updateProposals(itemId: Ident, proposals: MetaProposalList): ConnectionIO[Int] =
|
|
||||||
DML.update(T, T.itemId === itemId, DML.set(T.classifyProposals.setTo(proposals)))
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user