mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-04-05 19:09:32 +00:00
Merge pull request #1260 from Moerfi666/recursive_mail_scan
Add a recursive mail folder scan option to ScanMailboxTask
This commit is contained in:
commit
212a47fff5
@ -23,6 +23,8 @@ case class ScanMailboxArgs(
|
|||||||
account: AccountId,
|
account: AccountId,
|
||||||
// the configured imap connection
|
// the configured imap connection
|
||||||
imapConnection: Ident,
|
imapConnection: Ident,
|
||||||
|
// scan folders recursively
|
||||||
|
scanRecursively: Option[Boolean],
|
||||||
// what folders to search
|
// what folders to search
|
||||||
folders: List[String],
|
folders: List[String],
|
||||||
// only select mails received since then
|
// only select mails received since then
|
||||||
|
@ -24,6 +24,7 @@ import docspell.store.records._
|
|||||||
|
|
||||||
import _root_.io.circe.syntax._
|
import _root_.io.circe.syntax._
|
||||||
import emil.SearchQuery.{All, ReceivedDate}
|
import emil.SearchQuery.{All, ReceivedDate}
|
||||||
|
import emil.SearchResult.searchResultMonoid
|
||||||
import emil.javamail.syntax._
|
import emil.javamail.syntax._
|
||||||
import emil.{MimeType => _, _}
|
import emil.{MimeType => _, _}
|
||||||
|
|
||||||
@ -98,7 +99,12 @@ object ScanMailboxTask {
|
|||||||
if (acc.noneLeft(name)) acc.pure[F]
|
if (acc.noneLeft(name)) acc.pure[F]
|
||||||
else
|
else
|
||||||
mailer
|
mailer
|
||||||
.run(impl.handleFolder(theEmil.access, upload)(name))
|
.run(
|
||||||
|
impl.handleFolder(theEmil.access, upload)(
|
||||||
|
name,
|
||||||
|
ctx.args.scanRecursively.getOrElse(false)
|
||||||
|
)
|
||||||
|
)
|
||||||
.map(_ ++ acc)
|
.map(_ ++ acc)
|
||||||
|
|
||||||
Stream
|
Stream
|
||||||
@ -155,12 +161,15 @@ object ScanMailboxTask {
|
|||||||
MailOp(_ => f(ctx.logger))
|
MailOp(_ => f(ctx.logger))
|
||||||
|
|
||||||
def handleFolder[C](a: Access[F, C], upload: OUpload[F])(
|
def handleFolder[C](a: Access[F, C], upload: OUpload[F])(
|
||||||
name: String
|
name: String,
|
||||||
|
scanRecursively: Boolean
|
||||||
): MailOp[F, C, ScanResult] =
|
): MailOp[F, C, ScanResult] =
|
||||||
for {
|
for {
|
||||||
_ <- Kleisli.liftF(ctx.logger.info(s"Processing folder $name"))
|
_ <- Kleisli.liftF(ctx.logger.info(s"Processing folder $name"))
|
||||||
folder <- requireFolder(a)(name)
|
folder <- requireFolder(a)(name)
|
||||||
search <- searchMails(a)(folder)
|
search <-
|
||||||
|
if (scanRecursively) searchMailsRecursively(a)(folder)
|
||||||
|
else searchMails(a)(folder)
|
||||||
items = search.mails.map(MailHeaderItem(_))
|
items = search.mails.map(MailHeaderItem(_))
|
||||||
headers <- Kleisli.liftF(
|
headers <- Kleisli.liftF(
|
||||||
filterSubjects(items).flatMap(filterMessageIds)
|
filterSubjects(items).flatMap(filterMessageIds)
|
||||||
@ -175,6 +184,15 @@ object ScanMailboxTask {
|
|||||||
.map(_.toRight(new Exception(s"Folder '$name' not found")))
|
.map(_.toRight(new Exception(s"Folder '$name' not found")))
|
||||||
.mapF(_.rethrow)
|
.mapF(_.rethrow)
|
||||||
|
|
||||||
|
def searchMailsRecursively[C](
|
||||||
|
a: Access[F, C]
|
||||||
|
)(folder: MailFolder): MailOp[F, C, SearchResult[MailHeader]] =
|
||||||
|
for {
|
||||||
|
subFolders <- a.listFoldersRecursive(Some(folder))
|
||||||
|
foldersToSearch = Vector(folder) ++ subFolders
|
||||||
|
search <- foldersToSearch.traverse(searchMails(a))
|
||||||
|
} yield searchResultMonoid.combineAll(search)
|
||||||
|
|
||||||
def searchMails[C](
|
def searchMails[C](
|
||||||
a: Access[F, C]
|
a: Access[F, C]
|
||||||
)(folder: MailFolder): MailOp[F, C, SearchResult[MailHeader]] = {
|
)(folder: MailFolder): MailOp[F, C, SearchResult[MailHeader]] = {
|
||||||
|
@ -6407,6 +6407,7 @@ components:
|
|||||||
required:
|
required:
|
||||||
- id
|
- id
|
||||||
- enabled
|
- enabled
|
||||||
|
- scanRecursively
|
||||||
- imapConnection
|
- imapConnection
|
||||||
- schedule
|
- schedule
|
||||||
- folders
|
- folders
|
||||||
@ -6426,6 +6427,10 @@ components:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
type: string
|
type: string
|
||||||
|
scanRecursively:
|
||||||
|
type: boolean
|
||||||
|
description: |
|
||||||
|
Scan folders recursively for new mails.
|
||||||
schedule:
|
schedule:
|
||||||
type: string
|
type: string
|
||||||
format: calevent
|
format: calevent
|
||||||
|
@ -115,6 +115,7 @@ object ScanMailboxRoutes {
|
|||||||
ScanMailboxArgs(
|
ScanMailboxArgs(
|
||||||
user,
|
user,
|
||||||
settings.imapConnection,
|
settings.imapConnection,
|
||||||
|
Option(settings.scanRecursively),
|
||||||
settings.folders,
|
settings.folders,
|
||||||
settings.receivedSinceHours.map(_.toLong).map(Duration.hours),
|
settings.receivedSinceHours.map(_.toLong).map(Duration.hours),
|
||||||
settings.targetFolder,
|
settings.targetFolder,
|
||||||
@ -150,6 +151,7 @@ object ScanMailboxRoutes {
|
|||||||
task.summary,
|
task.summary,
|
||||||
conn.getOrElse(Ident.unsafe("")),
|
conn.getOrElse(Ident.unsafe("")),
|
||||||
task.args.folders,
|
task.args.folders,
|
||||||
|
task.args.scanRecursively.getOrElse(false),
|
||||||
task.timer,
|
task.timer,
|
||||||
task.args.receivedSince.map(_.hours.toInt),
|
task.args.receivedSince.map(_.hours.toInt),
|
||||||
task.args.targetFolder,
|
task.args.targetFolder,
|
||||||
|
@ -68,6 +68,7 @@ type alias Model =
|
|||||||
, targetFolder : Maybe String
|
, targetFolder : Maybe String
|
||||||
, foldersModel : Comp.StringListInput.Model
|
, foldersModel : Comp.StringListInput.Model
|
||||||
, folders : List String
|
, folders : List String
|
||||||
|
, scanRecursively : Bool
|
||||||
, direction : Maybe Direction
|
, direction : Maybe Direction
|
||||||
, schedule : Maybe CalEvent
|
, schedule : Maybe CalEvent
|
||||||
, scheduleModel : Comp.CalEventInput.Model
|
, scheduleModel : Comp.CalEventInput.Model
|
||||||
@ -156,6 +157,7 @@ type Msg
|
|||||||
| ReceivedHoursMsg Comp.IntField.Msg
|
| ReceivedHoursMsg Comp.IntField.Msg
|
||||||
| SetTargetFolder String
|
| SetTargetFolder String
|
||||||
| FoldersMsg Comp.StringListInput.Msg
|
| FoldersMsg Comp.StringListInput.Msg
|
||||||
|
| ToggleScanRecursively
|
||||||
| DirectionMsg (Maybe Direction)
|
| DirectionMsg (Maybe Direction)
|
||||||
| YesNoDeleteMsg Comp.YesNoDimmer.Msg
|
| YesNoDeleteMsg Comp.YesNoDimmer.Msg
|
||||||
| GetFolderResp (Result Http.Error FolderList)
|
| GetFolderResp (Result Http.Error FolderList)
|
||||||
@ -200,6 +202,7 @@ initWith flags s =
|
|||||||
, receivedHours = s.receivedSinceHours
|
, receivedHours = s.receivedSinceHours
|
||||||
, targetFolder = s.targetFolder
|
, targetFolder = s.targetFolder
|
||||||
, folders = s.folders
|
, folders = s.folders
|
||||||
|
, scanRecursively = s.scanRecursively
|
||||||
, schedule = Just newSchedule
|
, schedule = Just newSchedule
|
||||||
, direction = Maybe.andThen Data.Direction.fromString s.direction
|
, direction = Maybe.andThen Data.Direction.fromString s.direction
|
||||||
, scheduleModel = sm
|
, scheduleModel = sm
|
||||||
@ -246,6 +249,7 @@ init flags =
|
|||||||
, receivedHoursModel = Comp.IntField.init (Just 1) Nothing True
|
, receivedHoursModel = Comp.IntField.init (Just 1) Nothing True
|
||||||
, foldersModel = Comp.StringListInput.init
|
, foldersModel = Comp.StringListInput.init
|
||||||
, folders = []
|
, folders = []
|
||||||
|
, scanRecursively = False
|
||||||
, targetFolder = Nothing
|
, targetFolder = Nothing
|
||||||
, direction = Nothing
|
, direction = Nothing
|
||||||
, schedule = Just initialSchedule
|
, schedule = Just initialSchedule
|
||||||
@ -316,6 +320,7 @@ makeSettings model =
|
|||||||
, deleteMail = model.deleteMail
|
, deleteMail = model.deleteMail
|
||||||
, targetFolder = model.targetFolder
|
, targetFolder = model.targetFolder
|
||||||
, folders = folders
|
, folders = folders
|
||||||
|
, scanRecursively = model.scanRecursively
|
||||||
, direction = Maybe.map Data.Direction.asString model.direction
|
, direction = Maybe.map Data.Direction.asString model.direction
|
||||||
, schedule = Data.CalEvent.makeEvent timer
|
, schedule = Data.CalEvent.makeEvent timer
|
||||||
, itemFolder = model.itemFolderId
|
, itemFolder = model.itemFolderId
|
||||||
@ -497,6 +502,12 @@ update flags tz msg model =
|
|||||||
, Cmd.none
|
, Cmd.none
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ToggleScanRecursively ->
|
||||||
|
( { model | scanRecursively = not model.scanRecursively }
|
||||||
|
, NoAction
|
||||||
|
, Cmd.none
|
||||||
|
)
|
||||||
|
|
||||||
DirectionMsg md ->
|
DirectionMsg md ->
|
||||||
( { model | direction = md }
|
( { model | direction = md }
|
||||||
, NoAction
|
, NoAction
|
||||||
@ -961,6 +972,17 @@ viewGeneral2 texts settings model =
|
|||||||
viewProcessing2 : Texts -> Model -> List (Html Msg)
|
viewProcessing2 : Texts -> Model -> List (Html Msg)
|
||||||
viewProcessing2 texts model =
|
viewProcessing2 texts model =
|
||||||
[ div [ class "mb-4" ]
|
[ div [ class "mb-4" ]
|
||||||
|
[ MB.viewItem <|
|
||||||
|
MB.Checkbox
|
||||||
|
{ id = "scanmail-scan-recursively"
|
||||||
|
, value = model.scanRecursively
|
||||||
|
, label = texts.scanRecursivelyLabel
|
||||||
|
, tagger = \_ -> ToggleScanRecursively
|
||||||
|
}
|
||||||
|
, span [ class "opacity-50 text-sm mt-1" ]
|
||||||
|
[ Markdown.toHtml [] texts.scanRecursivelyInfo ]
|
||||||
|
]
|
||||||
|
, div [ class "mb-4" ]
|
||||||
[ label [ class S.inputLabel ]
|
[ label [ class S.inputLabel ]
|
||||||
[ text texts.folders
|
[ text texts.folders
|
||||||
, B.inputRequired
|
, B.inputRequired
|
||||||
|
@ -68,6 +68,8 @@ type alias Texts =
|
|||||||
, tagsInfo : String
|
, tagsInfo : String
|
||||||
, documentLanguage : String
|
, documentLanguage : String
|
||||||
, documentLanguageInfo : String
|
, documentLanguageInfo : String
|
||||||
|
, scanRecursivelyInfo : String
|
||||||
|
, scanRecursivelyLabel : String
|
||||||
, schedule : String
|
, schedule : String
|
||||||
, scheduleClickForHelp : String
|
, scheduleClickForHelp : String
|
||||||
, scheduleInfo : String
|
, scheduleInfo : String
|
||||||
@ -149,6 +151,8 @@ gb tz =
|
|||||||
, documentLanguageInfo =
|
, documentLanguageInfo =
|
||||||
"Used for text extraction and text analysis. The "
|
"Used for text extraction and text analysis. The "
|
||||||
++ "collective's default language is used, if not specified here."
|
++ "collective's default language is used, if not specified here."
|
||||||
|
, scanRecursivelyInfo = "Scan the sub-folders of the given folders for emails too."
|
||||||
|
, scanRecursivelyLabel = "Scan folders recursively"
|
||||||
, schedule = "Schedule"
|
, schedule = "Schedule"
|
||||||
, scheduleClickForHelp = "Click here for help"
|
, scheduleClickForHelp = "Click here for help"
|
||||||
, scheduleInfo =
|
, scheduleInfo =
|
||||||
@ -229,6 +233,8 @@ kann hier ein Wert für alle festgelegt werden. Bei 'Automatisch' wird auf den S
|
|||||||
, documentLanguageInfo =
|
, documentLanguageInfo =
|
||||||
"Wird für Texterkennung und -analyse verwendet. Die Standardsprache des Kollektivs "
|
"Wird für Texterkennung und -analyse verwendet. Die Standardsprache des Kollektivs "
|
||||||
++ "wird verwendet, falls hier nicht angegeben."
|
++ "wird verwendet, falls hier nicht angegeben."
|
||||||
|
, scanRecursivelyInfo = "Auch die Unterordner der gegebenen Ordner nach E-Mails durchsuchen."
|
||||||
|
, scanRecursivelyLabel = "Ordner rekursiv scannen"
|
||||||
, schedule = "Zeitplan"
|
, schedule = "Zeitplan"
|
||||||
, scheduleClickForHelp = "Klicke für Hilfe"
|
, scheduleClickForHelp = "Klicke für Hilfe"
|
||||||
, scheduleInfo =
|
, scheduleInfo =
|
||||||
@ -311,6 +317,8 @@ fr tz =
|
|||||||
, documentLanguageInfo =
|
, documentLanguageInfo =
|
||||||
"Utilisé pour l'extraction et l'analyse du texte. La langue"
|
"Utilisé pour l'extraction et l'analyse du texte. La langue"
|
||||||
++ "par défaut du groupe est utilisée, si non spécifié"
|
++ "par défaut du groupe est utilisée, si non spécifié"
|
||||||
|
, scanRecursivelyInfo = "Rechercher également les mails dans les sous-dossiers des dossiers spécifiés."
|
||||||
|
, scanRecursivelyLabel = "Analyse récursive des dossiers"
|
||||||
, schedule = "Programmation"
|
, schedule = "Programmation"
|
||||||
, scheduleClickForHelp = "Cliquer pour l'aide"
|
, scheduleClickForHelp = "Cliquer pour l'aide"
|
||||||
, scheduleInfo =
|
, scheduleInfo =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user