✨feat(mail_reader): add moving analyzed mails
add moving analyzed mails feature, upgrade reports by adding date of generation
This commit is contained in:
@@ -12,6 +12,7 @@ from logwatcher.mail_reader import (
|
||||
connect_to_mailbox,
|
||||
extract_logs_from_mails,
|
||||
fetch_log_messages,
|
||||
move_analyzed_mails,
|
||||
)
|
||||
from logwatcher.models import LogEntry
|
||||
from logwatcher.parser import parse_file, parse_lines
|
||||
@@ -90,6 +91,7 @@ def from_mail(
|
||||
write_log_report(
|
||||
SourceType.MAIL, relevant, irrelevant, nb_files, output_dir=output_dir
|
||||
)
|
||||
move_analyzed_mails(account, messages)
|
||||
logger.info("============ logwatcher mail ended ============")
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from exchangelib import (
|
||||
Configuration,
|
||||
Credentials,
|
||||
FileAttachment,
|
||||
Folder,
|
||||
Message,
|
||||
Version,
|
||||
)
|
||||
@@ -17,7 +18,8 @@ from exchangelib.errors import UnauthorizedError
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
MAILBOX_DIR = "Logs"
|
||||
LOG_DIR = "Logs"
|
||||
ANALYZED_LOG_DIR = "Analyzed"
|
||||
LOG_IN_ATTACHMENT_PATTERN = "Le compte-rendu contient plus de 100 lignes."
|
||||
|
||||
|
||||
@@ -138,7 +140,7 @@ def fetch_log_messages(account: Account) -> list[Message]:
|
||||
Returns:
|
||||
queryset of log-related mails
|
||||
"""
|
||||
logs_folder = account.msg_folder_root / MAILBOX_DIR
|
||||
logs_folder = account.msg_folder_root / LOG_DIR
|
||||
return list(logs_folder.all())
|
||||
|
||||
|
||||
@@ -178,3 +180,57 @@ def extract_logs_from_mails(messages: list[Message]) -> list[str]:
|
||||
content = _get_mail_body(message)
|
||||
logs.extend(extract_log_lines(content))
|
||||
return logs
|
||||
|
||||
|
||||
def _get_or_create_analyzed_folder(account: Account) -> Folder:
|
||||
"""
|
||||
Return the 'Analyzed' folder, creating it if it doesn't exist.
|
||||
|
||||
Args:
|
||||
account: The mailbox account owning the Logs folder.
|
||||
|
||||
Returns:
|
||||
The existing or newly created 'Analyzed' folder.
|
||||
|
||||
"""
|
||||
log_folder = account.msg_folder_root / LOG_DIR
|
||||
matches = list(log_folder.glob(ANALYZED_LOG_DIR))
|
||||
|
||||
if matches:
|
||||
logger.info(f"\tFolder '{ANALYZED_LOG_DIR}' found.")
|
||||
return matches[0]
|
||||
|
||||
logger.info(f"\tCreating '{ANALYZED_LOG_DIR}' folder.")
|
||||
analyzed_folder = Folder(parent=log_folder, name=ANALYZED_LOG_DIR)
|
||||
analyzed_folder.save()
|
||||
return analyzed_folder
|
||||
|
||||
|
||||
def move_analyzed_mails(account: Account, messages: list[Message]) -> None:
|
||||
"""
|
||||
Move processed mails from the Logs folder to the Analyzed subfolder.
|
||||
|
||||
Args:
|
||||
account: The mailbox account owning the Logs folder.
|
||||
messages: The processed mails to move.
|
||||
|
||||
Note:
|
||||
Partial failure is possible: if a move fails mid-batch, some mails
|
||||
may remain in Logs and be retried on the next run (at-least-once).
|
||||
"""
|
||||
if not messages:
|
||||
logger.info("No mails to move.")
|
||||
return
|
||||
|
||||
logger.info(
|
||||
f"Moving {len(messages)} mails from '{LOG_DIR}' directory to '{ANALYZED_LOG_DIR}'"
|
||||
)
|
||||
|
||||
analyzed_folder = _get_or_create_analyzed_folder(account)
|
||||
|
||||
ids = [(m.id, m.changekey) for m in messages]
|
||||
account.bulk_move(ids=ids, to_folder=analyzed_folder)
|
||||
|
||||
logger.info(
|
||||
f"Mails were successfully moved from '{LOG_DIR}' to '{ANALYZED_LOG_DIR}'."
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from string import Template
|
||||
|
||||
from logwatcher.config import DATETIME_FORMAT, RESULT_PATH, SourceType
|
||||
from logwatcher.config import DATETIME_FORMAT, FRENCH_TIMEZONE, RESULT_PATH, SourceType
|
||||
from logwatcher.models import LogEntry
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -12,6 +12,7 @@ logger = logging.getLogger(__name__)
|
||||
BASE_TEMPLATE = Template("""
|
||||
RAPPORT D'ANALYSE DE LOGS
|
||||
=========================
|
||||
Généré le\t : $today
|
||||
Période\t : $start_date -> $end_date
|
||||
$source_type(s) lu(s)\t : $nb_files
|
||||
Nombre total d'erreur(s)\t: $nb_errors
|
||||
@@ -126,10 +127,12 @@ def build_reports(
|
||||
logger.info("\t\t\tn2 and other reports were generated.")
|
||||
logger.info("\t\tbuilding reports job completed.")
|
||||
|
||||
today = datetime.now(FRENCH_TIMEZONE).strftime(DATETIME_FORMAT)
|
||||
source_value = source_type.value.capitalize()
|
||||
|
||||
return {
|
||||
"n2": BASE_TEMPLATE.substitute(
|
||||
today=today,
|
||||
source_type=source_value,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
@@ -138,6 +141,7 @@ def build_reports(
|
||||
sub_content=relevant_report,
|
||||
),
|
||||
"other": BASE_TEMPLATE.substitute(
|
||||
today=today,
|
||||
source_type=source_value,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
@@ -146,6 +150,7 @@ def build_reports(
|
||||
sub_content=irrelevant_report,
|
||||
),
|
||||
"all": BASE_TEMPLATE.substitute(
|
||||
today=today,
|
||||
source_type=source_value,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
|
||||
Reference in New Issue
Block a user