Improve mail template

This commit is contained in:
Eike Kettner 2020-04-22 23:41:09 +02:00
parent ffc1cdee51
commit d52efdfcf0
4 changed files with 48 additions and 28 deletions

View File

@ -4,6 +4,7 @@ import java.time.{Instant, LocalDate, ZoneId}
import cats.effect.Sync import cats.effect.Sync
import io.circe.{Decoder, Encoder} import io.circe.{Decoder, Encoder}
import java.time.temporal.ChronoUnit
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZonedDateTime import java.time.ZonedDateTime
@ -59,6 +60,9 @@ object Timestamp {
def atUtc(ldt: LocalDateTime): Timestamp = def atUtc(ldt: LocalDateTime): Timestamp =
from(ldt.atZone(UTC)) from(ldt.atZone(UTC))
def daysBetween(ts0: Timestamp, ts1: Timestamp): Long =
ChronoUnit.DAYS.between(ts0.toUtcDate, ts1.toUtcDate)
implicit val encodeTimestamp: Encoder[Timestamp] = implicit val encodeTimestamp: Encoder[Timestamp] =
BaseJsonCodecs.encodeInstantEpoch.contramap(_.value) BaseJsonCodecs.encodeInstantEpoch.contramap(_.value)

View File

@ -11,6 +11,7 @@ case class MailContext(
items: List[MailContext.ItemData], items: List[MailContext.ItemData],
more: Boolean, more: Boolean,
account: AccountId, account: AccountId,
username: String,
itemUri: Option[LenientUri] itemUri: Option[LenientUri]
) )
@ -20,12 +21,14 @@ object MailContext {
items: Vector[QItem.ListItem], items: Vector[QItem.ListItem],
max: Int, max: Int,
account: AccountId, account: AccountId,
itemBaseUri: Option[LenientUri] itemBaseUri: Option[LenientUri],
now: Timestamp
): MailContext = ): MailContext =
MailContext( MailContext(
items.take(max - 1).map(ItemData.apply).toList.sortBy(_.dueDate), items.take(max - 1).map(ItemData(now)).toList.sortBy(_.dueDate),
items.sizeCompare(max) >= 0, items.sizeCompare(max) >= 0,
account, account,
account.user.id.capitalize,
itemBaseUri itemBaseUri
) )
@ -34,13 +37,22 @@ object MailContext {
name: String, name: String,
date: Timestamp, date: Timestamp,
dueDate: Option[Timestamp], dueDate: Option[Timestamp],
source: String source: String,
overDue: Boolean,
dueIn: Option[String]
) )
object ItemData { object ItemData {
def apply(i: QItem.ListItem): ItemData = def apply(now: Timestamp)(i: QItem.ListItem): ItemData = {
ItemData(i.id, i.name, i.date, i.dueDate, i.source) val dueIn = i.dueDate.map(dt => Timestamp.daysBetween(now, dt))
val dueInLabel = dueIn.map {
case 0 => "**today**"
case 1 => "**tomorrow**"
case n => s"in $n days"
}
ItemData(i.id, i.name, i.date, i.dueDate, i.source, dueIn.exists(_ < 0), dueInLabel)
}
implicit def yamusca: ValueConverter[ItemData] = implicit def yamusca: ValueConverter[ItemData] =
ValueConverter.deriveConverter[ItemData] ValueConverter.deriveConverter[ItemData]

View File

@ -5,24 +5,26 @@ import yamusca.implicits._
object MailTemplate { object MailTemplate {
val text = mustache""" val text = mustache"""
## Hello {{{ account.user }}}, ## Hello {{{ username }}},
this is Docspell informing you about due items coming up. this is Docspell informing you about your next due items coming up.
{{#itemUri}} {{#itemUri}}
{{#items}} {{#items}}
- [{{name}}]({{itemUri}}/{{id}}), due on *{{dueDate}}* - {{#overDue}}**(OVERDUE)** {{/overDue}}[{{name}}]({{itemUri}}/{{id}}),
due {{dueIn}} on *{{dueDate}}*
(received on {{date}} via {{source}}) (received on {{date}} via {{source}})
{{/items}} {{/items}}
{{/itemUri}} {{/itemUri}}
{{^itemUri}} {{^itemUri}}
{{#items}} {{#items}}
- *{{name}}*, due on *{{dueDate}}* - {{#overDue}}**(OVERDUE)** {{/overDue}}*{{name}}*,
due {{dueIn}} on *{{dueDate}}*
(received on {{date}} via {{source}}) (received on {{date}} via {{source}})
{{/items}} {{/items}}
{{/itemUri}} {{/itemUri}}
{{#more}} {{#more}}
- (There are more due items, left out for brevity) - more have been left out for brevity
{{/more}} {{/more}}

View File

@ -16,7 +16,7 @@ import docspell.joex.notify.MailContext
import docspell.joex.notify.MailTemplate import docspell.joex.notify.MailTemplate
object NotifyDueItemsTask { object NotifyDueItemsTask {
val maxItems: Long = 5 val maxItems: Long = 7
type Args = NotifyDueItemsArgs type Args = NotifyDueItemsArgs
def apply[F[_]: Sync](emil: Emil[F]): Task[F, Args, Unit] = def apply[F[_]: Sync](emil: Emil[F]): Task[F, Args, Unit] =
@ -83,21 +83,23 @@ object NotifyDueItemsTask {
cfg: RUserEmail, cfg: RUserEmail,
args: Args, args: Args,
items: Vector[QItem.ListItem] items: Vector[QItem.ListItem]
): F[Mail[F]] = Sync[F].delay { ): F[Mail[F]] =
val templateCtx = Timestamp.current[F].map { now =>
MailContext.from(items, maxItems.toInt, args.account, args.itemDetailUrl) val templateCtx =
val md = MailTemplate.render(templateCtx) MailContext.from(items, maxItems.toInt, args.account, args.itemDetailUrl, now)
val recp = args.recipients val md = MailTemplate.render(templateCtx)
.map(MailAddress.parse) val recp = args.recipients
.map { .map(MailAddress.parse)
case Right(ma) => ma .map {
case Left(err) => throw new Exception(s"Unable to parse recipient address: $err") case Right(ma) => ma
} case Left(err) =>
MailBuilder.build( throw new Exception(s"Unable to parse recipient address: $err")
From(cfg.mailFrom), }
Tos(recp), MailBuilder.build(
Subject("Next due items"), From(cfg.mailFrom),
MarkdownBody[F](md) Tos(recp),
) Subject("Next due items"),
} MarkdownBody[F](md)
)
}
} }