mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 02:18:26 +00:00
Remove obsolete code
This commit is contained in:
@ -170,13 +170,6 @@ object JoexAppImpl extends MailAddressCodec {
|
|||||||
ReProcessItem.onCancel[F]
|
ReProcessItem.onCancel[F]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.withTask(
|
|
||||||
JobTask.json(
|
|
||||||
NotifyDueItemsArgs.taskName,
|
|
||||||
NotifyDueItemsTask[F](cfg.sendMail, javaEmil),
|
|
||||||
NotifyDueItemsTask.onCancel[F]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.withTask(
|
.withTask(
|
||||||
JobTask.json(
|
JobTask.json(
|
||||||
ScanMailboxArgs.taskName,
|
ScanMailboxArgs.taskName,
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Eike K. & Contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
package docspell.joex.notify
|
|
||||||
|
|
||||||
import docspell.common._
|
|
||||||
import docspell.joex.notify.YamuscaConverter._
|
|
||||||
import docspell.store.queries.ListItem
|
|
||||||
|
|
||||||
import yamusca.implicits._
|
|
||||||
import yamusca.imports._
|
|
||||||
|
|
||||||
/** The context for rendering the e-mail template. */
|
|
||||||
case class MailContext(
|
|
||||||
items: List[MailContext.ItemData],
|
|
||||||
more: Boolean,
|
|
||||||
account: AccountId,
|
|
||||||
username: String,
|
|
||||||
itemUri: Option[LenientUri]
|
|
||||||
)
|
|
||||||
|
|
||||||
object MailContext {
|
|
||||||
|
|
||||||
def from(
|
|
||||||
items: Vector[ListItem],
|
|
||||||
max: Int,
|
|
||||||
account: AccountId,
|
|
||||||
itemBaseUri: Option[LenientUri],
|
|
||||||
now: Timestamp
|
|
||||||
): MailContext =
|
|
||||||
MailContext(
|
|
||||||
items.take(max - 1).map(ItemData(now)).toList.sortBy(_.dueDate),
|
|
||||||
items.sizeCompare(max) >= 0,
|
|
||||||
account,
|
|
||||||
account.user.id.capitalize,
|
|
||||||
itemBaseUri
|
|
||||||
)
|
|
||||||
|
|
||||||
case class ItemData(
|
|
||||||
id: Ident,
|
|
||||||
name: String,
|
|
||||||
date: Timestamp,
|
|
||||||
dueDate: Option[Timestamp],
|
|
||||||
source: String,
|
|
||||||
overDue: Boolean,
|
|
||||||
dueIn: Option[String],
|
|
||||||
corrOrg: Option[String]
|
|
||||||
)
|
|
||||||
|
|
||||||
object ItemData {
|
|
||||||
|
|
||||||
def apply(now: Timestamp)(i: ListItem): ItemData = {
|
|
||||||
val dueIn = i.dueDate.map(dt => Timestamp.daysBetween(now, dt))
|
|
||||||
val dueInLabel = dueIn.map {
|
|
||||||
case 0 => "**today**"
|
|
||||||
case 1 => "**tomorrow**"
|
|
||||||
case -1 => s"**yesterday**"
|
|
||||||
case n if n > 0 => s"in $n days"
|
|
||||||
case n => s"${n * -1} days ago"
|
|
||||||
}
|
|
||||||
ItemData(
|
|
||||||
i.id,
|
|
||||||
i.name,
|
|
||||||
i.date,
|
|
||||||
i.dueDate,
|
|
||||||
i.source,
|
|
||||||
dueIn.exists(_ < 0),
|
|
||||||
dueInLabel,
|
|
||||||
i.corrOrg.map(_.name)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
implicit def yamusca: ValueConverter[ItemData] =
|
|
||||||
ValueConverter.deriveConverter[ItemData]
|
|
||||||
}
|
|
||||||
|
|
||||||
implicit val yamusca: ValueConverter[MailContext] =
|
|
||||||
ValueConverter.deriveConverter[MailContext]
|
|
||||||
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Eike K. & Contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
package docspell.joex.notify
|
|
||||||
|
|
||||||
import yamusca.implicits._
|
|
||||||
|
|
||||||
object MailTemplate {
|
|
||||||
|
|
||||||
val text = mustache"""
|
|
||||||
Hello {{{ username }}},
|
|
||||||
|
|
||||||
this is Docspell informing you about your next due items coming up.
|
|
||||||
|
|
||||||
{{#itemUri}}
|
|
||||||
{{#items}}
|
|
||||||
- {{#overDue}}**(OVERDUE)** {{/overDue}}[{{name}}]({{itemUri}}/{{id}}),
|
|
||||||
{{#overDue}}was {{/overDue}}due {{dueIn}} on *{{dueDate}}*; {{#corrOrg}}from {{corrOrg}}{{/corrOrg}}
|
|
||||||
received on {{date}} via {{source}}
|
|
||||||
{{/items}}
|
|
||||||
{{/itemUri}}
|
|
||||||
{{^itemUri}}
|
|
||||||
{{#items}}
|
|
||||||
- {{#overDue}}**(OVERDUE)** {{/overDue}}*{{name}}*,
|
|
||||||
{{#overDue}}was {{/overDue}}due {{dueIn}} on *{{dueDate}}*; {{#corrOrg}}from {{corrOrg}}{{/corrOrg}}
|
|
||||||
received on {{date}} via {{source}}
|
|
||||||
{{/items}}
|
|
||||||
{{/itemUri}}
|
|
||||||
{{#more}}
|
|
||||||
- … more have been left out for brevity
|
|
||||||
{{/more}}
|
|
||||||
|
|
||||||
|
|
||||||
Sincerely yours,
|
|
||||||
|
|
||||||
Docspell
|
|
||||||
"""
|
|
||||||
|
|
||||||
def render(mc: MailContext): String =
|
|
||||||
mc.render(text)
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2020 Eike K. & Contributors
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
||||||
*/
|
|
||||||
|
|
||||||
package docspell.joex.notify
|
|
||||||
|
|
||||||
import cats.data.{NonEmptyList => Nel, OptionT}
|
|
||||||
import cats.effect._
|
|
||||||
import cats.implicits._
|
|
||||||
|
|
||||||
import docspell.backend.ops.OItemSearch.{Batch, ListItem, Query}
|
|
||||||
import docspell.common._
|
|
||||||
import docspell.joex.mail.EmilHeader
|
|
||||||
import docspell.joex.scheduler.{Context, Task}
|
|
||||||
import docspell.query.Date
|
|
||||||
import docspell.query.ItemQuery._
|
|
||||||
import docspell.query.ItemQueryDsl._
|
|
||||||
import docspell.store.queries.QItem
|
|
||||||
import docspell.store.records._
|
|
||||||
|
|
||||||
import emil._
|
|
||||||
import emil.builder._
|
|
||||||
import emil.javamail.syntax._
|
|
||||||
import emil.markdown._
|
|
||||||
|
|
||||||
object NotifyDueItemsTask {
|
|
||||||
val maxItems: Int = 7
|
|
||||||
type Args = NotifyDueItemsArgs
|
|
||||||
|
|
||||||
def apply[F[_]: Sync](cfg: MailSendConfig, emil: Emil[F]): Task[F, Args, Unit] =
|
|
||||||
Task { ctx =>
|
|
||||||
for {
|
|
||||||
_ <- ctx.logger.info("Getting mail configuration")
|
|
||||||
mailCfg <- getMailSettings(ctx)
|
|
||||||
_ <- ctx.logger.info(
|
|
||||||
s"Searching for items due in ${ctx.args.remindDays} days…."
|
|
||||||
)
|
|
||||||
_ <- createMail(cfg, mailCfg, ctx)
|
|
||||||
.semiflatMap { mail =>
|
|
||||||
for {
|
|
||||||
_ <- ctx.logger.info(s"Sending notification mail to ${ctx.args.recipients}")
|
|
||||||
res <- emil(mailCfg.toMailConfig).send(mail).map(_.head)
|
|
||||||
_ <- ctx.logger.info(s"Sent mail with id: $res")
|
|
||||||
} yield ()
|
|
||||||
}
|
|
||||||
.getOrElseF(ctx.logger.info("No items found"))
|
|
||||||
} yield ()
|
|
||||||
}
|
|
||||||
|
|
||||||
def onCancel[F[_]]: Task[F, NotifyDueItemsArgs, Unit] =
|
|
||||||
Task.log(_.warn("Cancelling notify-due-items task"))
|
|
||||||
|
|
||||||
def getMailSettings[F[_]: Sync](ctx: Context[F, Args]): F[RUserEmail] =
|
|
||||||
ctx.store
|
|
||||||
.transact(RUserEmail.getByName(ctx.args.account, ctx.args.smtpConnection))
|
|
||||||
.flatMap {
|
|
||||||
case Some(c) => c.pure[F]
|
|
||||||
case None =>
|
|
||||||
Sync[F].raiseError(
|
|
||||||
new Exception(
|
|
||||||
s"No smtp configuration found for: ${ctx.args.smtpConnection.id}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
def createMail[F[_]: Sync](
|
|
||||||
sendCfg: MailSendConfig,
|
|
||||||
cfg: RUserEmail,
|
|
||||||
ctx: Context[F, Args]
|
|
||||||
): OptionT[F, Mail[F]] =
|
|
||||||
for {
|
|
||||||
items <- OptionT.liftF(findItems(ctx)).filter(_.nonEmpty)
|
|
||||||
mail <- OptionT.liftF(makeMail(sendCfg, cfg, ctx.args, items))
|
|
||||||
} yield mail
|
|
||||||
|
|
||||||
def findItems[F[_]: Sync](ctx: Context[F, Args]): F[Vector[ListItem]] =
|
|
||||||
for {
|
|
||||||
now <- Timestamp.current[F]
|
|
||||||
rightDate = Date((now + Duration.days(ctx.args.remindDays.toLong)).toMillis)
|
|
||||||
q =
|
|
||||||
Query
|
|
||||||
.all(ctx.args.account)
|
|
||||||
.withOrder(orderAsc = _.dueDate)
|
|
||||||
.withFix(_.copy(query = Expr.ValidItemStates.some))
|
|
||||||
.withCond(_ =>
|
|
||||||
Query.QueryExpr(
|
|
||||||
Attr.DueDate <= rightDate &&?
|
|
||||||
ctx.args.daysBack.map(back =>
|
|
||||||
Attr.DueDate >= Date((now - Duration.days(back.toLong)).toMillis)
|
|
||||||
) &&?
|
|
||||||
Nel
|
|
||||||
.fromList(ctx.args.tagsInclude)
|
|
||||||
.map(ids => Q.tagIdsEq(ids.map(_.id))) &&?
|
|
||||||
Nel
|
|
||||||
.fromList(ctx.args.tagsExclude)
|
|
||||||
.map(ids => Q.tagIdsIn(ids.map(_.id)).negate)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
res <-
|
|
||||||
ctx.store
|
|
||||||
.transact(
|
|
||||||
QItem
|
|
||||||
.findItems(q, now.toUtcDate, 0, Batch.limit(maxItems))
|
|
||||||
.take(maxItems.toLong)
|
|
||||||
)
|
|
||||||
.compile
|
|
||||||
.toVector
|
|
||||||
} yield res
|
|
||||||
|
|
||||||
def makeMail[F[_]: Sync](
|
|
||||||
sendCfg: MailSendConfig,
|
|
||||||
cfg: RUserEmail,
|
|
||||||
args: Args,
|
|
||||||
items: Vector[ListItem]
|
|
||||||
): F[Mail[F]] =
|
|
||||||
Timestamp.current[F].map { now =>
|
|
||||||
val templateCtx =
|
|
||||||
MailContext.from(items, maxItems.toInt, args.account, args.itemDetailUrl, now)
|
|
||||||
val md = MailTemplate.render(templateCtx)
|
|
||||||
val recp = args.recipients
|
|
||||||
.map(MailAddress.parse)
|
|
||||||
.map {
|
|
||||||
case Right(ma) => ma
|
|
||||||
case Left(err) =>
|
|
||||||
throw new Exception(s"Unable to parse recipient address: $err")
|
|
||||||
}
|
|
||||||
MailBuilder.build(
|
|
||||||
From(cfg.mailFrom),
|
|
||||||
Tos(recp),
|
|
||||||
XMailer.emil,
|
|
||||||
Subject("[Docspell] Next due items"),
|
|
||||||
EmilHeader.listId(sendCfg.listId),
|
|
||||||
MarkdownBody[F](md).withConfig(
|
|
||||||
MarkdownConfig("body { font-size: 10pt; font-family: sans-serif; }")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,6 +12,13 @@ import emil.MailAddress
|
|||||||
import io.circe.generic.semiauto
|
import io.circe.generic.semiauto
|
||||||
import io.circe.{Decoder, Encoder}
|
import io.circe.{Decoder, Encoder}
|
||||||
|
|
||||||
|
/** Arguments to the notification task.
|
||||||
|
*
|
||||||
|
* This tasks queries items with a due date and informs the user via mail.
|
||||||
|
*
|
||||||
|
* If the structure changes, there must be some database migration to update or remove
|
||||||
|
* the json data of the corresponding task.
|
||||||
|
*/
|
||||||
final case class PeriodicDueItemsArgs(
|
final case class PeriodicDueItemsArgs(
|
||||||
account: AccountId,
|
account: AccountId,
|
||||||
channel: ChannelOrRef,
|
channel: ChannelOrRef,
|
||||||
|
@ -17,6 +17,9 @@ import io.circe.generic.semiauto._
|
|||||||
*
|
*
|
||||||
* If the structure changes, there must be some database migration to update or remove
|
* If the structure changes, there must be some database migration to update or remove
|
||||||
* the json data of the corresponding task.
|
* the json data of the corresponding task.
|
||||||
|
*
|
||||||
|
* @deprecated note: This has been removed and copied to this place to be able to
|
||||||
|
* migrate away from this structure. Replaced by PeriodicDueItemsArgs
|
||||||
*/
|
*/
|
||||||
case class NotifyDueItemsArgs(
|
case class NotifyDueItemsArgs(
|
||||||
account: AccountId,
|
account: AccountId,
|
Reference in New Issue
Block a user