mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-07 07:35:59 +00:00
Remove person suggestion if it doesn't match with organization
This commit is contained in:
parent
eb308cfa85
commit
d99ce76d89
@ -40,6 +40,11 @@ case class MetaProposalList private (proposals: List[MetaProposal]) {
|
|||||||
def change(f: MetaProposal => MetaProposal): MetaProposalList =
|
def change(f: MetaProposal => MetaProposal): MetaProposalList =
|
||||||
new MetaProposalList(proposals.map(f))
|
new MetaProposalList(proposals.map(f))
|
||||||
|
|
||||||
|
def replace(mp: MetaProposal): MetaProposalList = {
|
||||||
|
val next = proposals.filter(_.proposalType != mp.proposalType)
|
||||||
|
MetaProposalList(mp :: next)
|
||||||
|
}
|
||||||
|
|
||||||
def filter(f: MetaProposal => Boolean): MetaProposalList =
|
def filter(f: MetaProposal => Boolean): MetaProposalList =
|
||||||
new MetaProposalList(proposals.filter(f))
|
new MetaProposalList(proposals.filter(f))
|
||||||
|
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
package docspell.joex.process
|
||||||
|
|
||||||
|
import cats.data.NonEmptyList
|
||||||
|
import cats.data.OptionT
|
||||||
|
import cats.effect.Sync
|
||||||
|
import cats.implicits._
|
||||||
|
|
||||||
|
import docspell.common._
|
||||||
|
import docspell.joex.scheduler.Task
|
||||||
|
|
||||||
|
/** After candidates have been determined, the set is reduced by doing
|
||||||
|
* some cross checks. For example: if a organization is suggested as
|
||||||
|
* correspondent, the correspondent person must be linked to that
|
||||||
|
* organization. So this *removes all* person candidates that are not
|
||||||
|
* linked to the first organization candidate (which will be linked
|
||||||
|
* to the item).
|
||||||
|
*/
|
||||||
|
object CrossCheckProposals {
|
||||||
|
|
||||||
|
def apply[F[_]: Sync](data: ItemData): Task[F, ProcessItemArgs, ItemData] =
|
||||||
|
Task { ctx =>
|
||||||
|
val proposals = data.finalProposals
|
||||||
|
val corrOrg = proposals.find(MetaProposalType.CorrOrg)
|
||||||
|
(for {
|
||||||
|
orgRef <- OptionT.fromOption[F](corrOrg)
|
||||||
|
persRefs <- OptionT.liftF(EvalProposals.findOrganizationRelation(data, ctx))
|
||||||
|
clProps <- OptionT.liftF(
|
||||||
|
personOrgCheck[F](ctx.logger, data.classifyProposals, persRefs)(orgRef)
|
||||||
|
)
|
||||||
|
atProps <- OptionT.liftF {
|
||||||
|
data.metas.traverse(ra =>
|
||||||
|
personOrgCheck[F](ctx.logger, ra.proposals, persRefs)(orgRef).map(nl =>
|
||||||
|
ra.copy(proposals = nl)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} yield data.copy(classifyProposals = clProps, metas = atProps)).getOrElse(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
def personOrgCheck[F[_]: Sync](
|
||||||
|
logger: Logger[F],
|
||||||
|
mpl: MetaProposalList,
|
||||||
|
persRefs: Map[Ident, PersonRef]
|
||||||
|
)(
|
||||||
|
corrOrg: MetaProposal
|
||||||
|
): F[MetaProposalList] = {
|
||||||
|
val orgId = corrOrg.values.head.ref.id
|
||||||
|
mpl.find(MetaProposalType.CorrPerson) match {
|
||||||
|
case Some(ppl) =>
|
||||||
|
val list = ppl.values.filter(c =>
|
||||||
|
persRefs.get(c.ref.id).exists(_.organization == Some(orgId))
|
||||||
|
)
|
||||||
|
|
||||||
|
if (ppl.values.toList == list) mpl.pure[F]
|
||||||
|
else
|
||||||
|
logger.debug(
|
||||||
|
"Removing person suggestion, because it doesn't match organization"
|
||||||
|
) *> NonEmptyList
|
||||||
|
.fromList(list)
|
||||||
|
.map(nel => mpl.replace(MetaProposal(ppl.proposalType, nel)))
|
||||||
|
.getOrElse(mpl.filter(_.proposalType != ppl.proposalType))
|
||||||
|
.pure[F]
|
||||||
|
|
||||||
|
case None =>
|
||||||
|
mpl.pure[F]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,9 @@ object EvalProposals {
|
|||||||
ctx: Context[F, _]
|
ctx: Context[F, _]
|
||||||
): F[Map[Ident, PersonRef]] = {
|
): F[Map[Ident, PersonRef]] = {
|
||||||
val corrPersIds = data.metas
|
val corrPersIds = data.metas
|
||||||
.flatMap(_.proposals.find(MetaProposalType.CorrPerson))
|
.map(_.proposals)
|
||||||
|
.appended(data.classifyProposals)
|
||||||
|
.flatMap(_.find(MetaProposalType.CorrPerson))
|
||||||
.flatMap(_.values.toList.map(_.ref.id))
|
.flatMap(_.values.toList.map(_.ref.id))
|
||||||
.toSet
|
.toSet
|
||||||
ctx.store
|
ctx.store
|
||||||
|
@ -33,6 +33,17 @@ case class ItemData(
|
|||||||
classifyTags: List[String]
|
classifyTags: List[String]
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
/** sort by weight; order of equal weights is not important, just
|
||||||
|
* choose one others are then suggestions
|
||||||
|
* doc-date is only set when given explicitely, not from "guessing"
|
||||||
|
*/
|
||||||
|
def finalProposals: MetaProposalList =
|
||||||
|
MetaProposalList
|
||||||
|
.flatten(metas.map(_.proposals))
|
||||||
|
.filter(_.proposalType != MetaProposalType.DocDate)
|
||||||
|
.sortByWeights
|
||||||
|
.fillEmptyFrom(classifyProposals)
|
||||||
|
|
||||||
def findMeta(attachId: Ident): Option[RAttachmentMeta] =
|
def findMeta(attachId: Ident): Option[RAttachmentMeta] =
|
||||||
metas.find(_.id == attachId)
|
metas.find(_.id == attachId)
|
||||||
|
|
||||||
|
@ -17,14 +17,7 @@ object LinkProposal {
|
|||||||
.map(_ => data)
|
.map(_ => data)
|
||||||
else
|
else
|
||||||
Task { ctx =>
|
Task { ctx =>
|
||||||
// sort by weight; order of equal weights is not important, just
|
val proposals = data.finalProposals
|
||||||
// choose one others are then suggestions
|
|
||||||
// doc-date is only set when given explicitely, not from "guessing"
|
|
||||||
val proposals = MetaProposalList
|
|
||||||
.flatten(data.metas.map(_.proposals))
|
|
||||||
.filter(_.proposalType != MetaProposalType.DocDate)
|
|
||||||
.sortByWeights
|
|
||||||
.fillEmptyFrom(data.classifyProposals)
|
|
||||||
|
|
||||||
ctx.logger.info(s"Starting linking proposals") *>
|
ctx.logger.info(s"Starting linking proposals") *>
|
||||||
MetaProposalType.all
|
MetaProposalType.all
|
||||||
|
@ -43,6 +43,7 @@ object ProcessItem {
|
|||||||
TextAnalysis[F](cfg.textAnalysis, analyser, regexNer)(item)
|
TextAnalysis[F](cfg.textAnalysis, analyser, regexNer)(item)
|
||||||
.flatMap(FindProposal[F](cfg.textAnalysis))
|
.flatMap(FindProposal[F](cfg.textAnalysis))
|
||||||
.flatMap(EvalProposals[F])
|
.flatMap(EvalProposals[F])
|
||||||
|
.flatMap(CrossCheckProposals[F])
|
||||||
.flatMap(SaveProposals[F])
|
.flatMap(SaveProposals[F])
|
||||||
|
|
||||||
private def processAttachments0[F[_]: ConcurrentEffect: ContextShift](
|
private def processAttachments0[F[_]: ConcurrentEffect: ContextShift](
|
||||||
|
Loading…
x
Reference in New Issue
Block a user