Skip to content

Commit b07cdb9

Browse files
committed
refactor: split mail exporter for offline testing
Extract prepare/publish helpers, injectable mail clock, and non-interactive compile path used by offline tests. Signed-off-by: Alan Peixinho <alan.peixinho@profusion.mobi>
1 parent 7aefe7f commit b07cdb9

1 file changed

Lines changed: 121 additions & 68 deletions

File tree

regzbot/export_mail.py

Lines changed: 121 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from collections import Counter
88
import datetime
9+
from email import generator as email_generator
910
from email.message import EmailMessage
1011
import email.utils
1112
import tempfile
@@ -17,6 +18,17 @@
1718
logger = regzbot.logger
1819

1920

21+
def _mail_now():
22+
mail_now = regzbot._TESTING.get("mail_now")
23+
if mail_now is not None:
24+
return mail_now
25+
return regzbot.timendate_now()
26+
27+
28+
def _mail_days_delta(past):
29+
return (_mail_now() - datetime.datetime.fromtimestamp(past, datetime.timezone.utc)).days
30+
31+
2032
class RegLinkMailReport(regzbot.RegLink):
2133
def __init__(self, *args):
2234
super().__init__(*args)
@@ -31,7 +43,7 @@ def mailreport(self):
3143
):
3244
monitored = "; thread monitored."
3345
authored = "\n %s days ago, by %s%s" % (
34-
regzbot.days_delta(self.gmtime),
46+
_mail_days_delta(self.gmtime),
3547
self.author,
3648
monitored,
3749
)
@@ -95,17 +107,17 @@ def compile(self, lastreport_gmtime):
95107
statusline.append(", ")
96108

97109
statusline.append("; ")
98-
statusline.append(str(regzbot.days_delta(self.gmtime)))
110+
statusline.append(str(_mail_days_delta(self.gmtime)))
99111
statusline.append(" days ago; ")
100112
statusline.append(str(len(self._actievents)))
101113
statusline.append(" activities")
102114
if len(self._actievents) > 0:
103115
statusline.append(", latest ")
104-
statusline.append(str(regzbot.days_delta(self._actievents[-1].gmtime)))
116+
statusline.append(str(_mail_days_delta(self._actievents[-1].gmtime)))
105117
statusline.append(" days ago")
106118

107119
if self.poked:
108-
statusline.append("; poked %s days ago" % regzbot.days_delta(self.poked.gmtime))
120+
statusline.append("; poked %s days ago" % _mail_days_delta(self.poked.gmtime))
109121
statusline.append(".")
110122
report.append("".join(statusline))
111123

@@ -166,7 +178,7 @@ def add_latestpatch(self, report):
166178
report.append("* %s" % actievent.subject)
167179
report.append(" %s" % actievent.url())
168180
report.append(
169-
" %s days ago, by %s" % (regzbot.days_delta(actievent.gmtime), actievent.author)
181+
" %s days ago, by %s" % (_mail_days_delta(actievent.gmtime), actievent.author)
170182
)
171183

172184
break
@@ -235,33 +247,63 @@ def __init__(
235247
self.reporttext = reporttext
236248

237249
@classmethod
238-
def __create_mail(cls, content, treename):
250+
def listed(cls, lastreport_gmtime):
251+
regressionslist = list()
252+
for regression in RegressionMailReport.get_all(only_unsolved=True):
253+
# ignore some
254+
if regression._actievents:
255+
last_activity = regression._actievents[-1].gmtime
256+
else:
257+
last_activity = regression._histevents[-1].gmtime
258+
regressionslist.append(
259+
cls(
260+
regression._actim_report.entry,
261+
regression.gmtime,
262+
regression.gmtime_filed,
263+
last_activity,
264+
regression.treename,
265+
regression.versionline,
266+
regression.backburner,
267+
regression.identified,
268+
regression.mailreport(lastreport_gmtime),
269+
)
270+
)
271+
272+
regressionslist.sort(key=lambda x: x.gmtime_activity, reverse=True)
273+
return regressionslist
274+
275+
@classmethod
276+
def __create_mail(cls, content, treename, *, fixed_message_id=False):
239277
msg = EmailMessage()
240278
msg["To"] = (
241279
"LKML <linux-kernel@vger.kernel.org>, Linus Torvalds <torvalds@linux-foundation.org>, Linux regressions mailing list <regressions@lists.linux.dev>"
242280
)
243281
msg["Subject"] = "%s for %s [%s]" % (
244282
regzbot.REPORT_SUBJECT_PREFIX,
245283
treename,
246-
datetime.date.today(),
284+
_mail_now().date(),
247285
)
248-
msg["Date"] = email.utils.localtime()
249-
msg["Message-ID"] = email.utils.make_msgid(domain="leemhuis.info")
286+
msg["Date"] = email.utils.formatdate(timeval=_mail_now().timestamp(), localtime=True)
287+
if fixed_message_id:
288+
msg["Message-ID"] = "<regzbot-testing-mailreport@example.com>"
289+
else:
290+
msg["Message-ID"] = email.utils.make_msgid(domain="leemhuis.info")
250291
msg.set_content(content, cte="quoted-printable")
251292
return msg
252293

253294
@classmethod
254-
def pagecreate(cls, categories, treename, lastreport_msgid):
295+
def pagecreate(cls, categories, treename, lastreport_msgid, *, interactive=True):
255296
def repintro(report, number_issues, treename):
256297
intro = list()
257298

258-
print("Enter/Paste your intro for %s and hit Ctrl-D to save it." % treename)
259-
while True:
260-
try:
261-
line = input()
262-
except EOFError:
263-
break
264-
intro.append(line)
299+
if interactive:
300+
print("Enter/Paste your intro for %s and hit Ctrl-D to save it." % treename)
301+
while True:
302+
try:
303+
line = input()
304+
except EOFError:
305+
break
306+
intro.append(line)
265307
if report:
266308
intro.append("\n---\n")
267309

@@ -450,11 +492,7 @@ def categorize(cls, regressionlist, lastreport_gmtime):
450492
}
451493

452494
for regression in regressionlist:
453-
filed_days = (
454-
datetime.datetime.now(datetime.timezone.utc)
455-
- datetime.datetime.fromtimestamp(regression.gmtime_filed, datetime.timezone.utc)
456-
).days
457-
last_activity_days = regzbot.days_delta(regression.gmtime_activity)
495+
last_activity_days = _mail_days_delta(regression.gmtime_activity)
458496

459497
if regression.backburner:
460498
if lastreport_gmtime > regression.gmtime_activity:
@@ -512,80 +550,95 @@ def categorize(cls, regressionlist, lastreport_gmtime):
512550
return categories
513551

514552
@classmethod
515-
def compile(cls):
516-
logger.debug("[reportmail] generating")
553+
def prepare_reports(cls, categories, lastreport_msgid, *, interactive=True):
554+
reports = dict()
555+
for treename in categories.keys():
556+
reports[treename] = cls.pagecreate(
557+
categories[treename], treename, lastreport_msgid, interactive=interactive
558+
)
559+
return reports
560+
561+
@classmethod
562+
def lastreport(cls):
563+
if regzbot.is_running_citesting("offline"):
564+
lastreport_gmtime = regzbot._TESTING["mail_lastreport_gmtime"]
565+
lastreport_msgid = regzbot._TESTING.get("mail_lastreport_msgid")
566+
else:
567+
lastreport_msgid = regzbot.RegzbotState.get("lastreport_mainline_msgid")
568+
lastreport_gmtime = regzbot.RegzbotState.get("lastreport_mainline_gmtime")
517569

518-
lastreport_msgid = regzbot.RegzbotState.get("lastreport_mainline_msgid")
519-
lastreport_gmtime = regzbot.RegzbotState.get("lastreport_mainline_gmtime")
520570
if lastreport_gmtime:
521571
lastreport_gmtime = int(lastreport_gmtime)
522572
else:
523-
lastreport_gmtime = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
573+
lastreport_gmtime = int(_mail_now().timestamp())
524574

575+
return lastreport_gmtime, lastreport_msgid
576+
577+
@classmethod
578+
def prepare(cls, *, interactive=True):
579+
lastreport_gmtime, lastreport_msgid = cls.lastreport()
525580
logger.debug("[reportmail] lastreport was %s" % lastreport_gmtime)
526581

527582
# gather everything we need
528-
regressionslist = list()
583+
categories = cls.categorize(cls.listed(lastreport_gmtime), lastreport_gmtime)
584+
reports = cls.prepare_reports(categories, lastreport_msgid, interactive=interactive)
585+
return reports, categories
529586

530-
for regression in RegressionMailReport.get_all(only_unsolved=True):
531-
# ignore some
532-
if regression._actievents:
533-
last_activity = regression._actievents[-1].gmtime
534-
else:
535-
last_activity = regression._histevents[-1].gmtime
536-
last_activity_days = regzbot.days_delta(last_activity)
537-
if regression._actievents:
538-
last_activity = regression._actievents[-1].gmtime
539-
else:
540-
last_activity = regression._histevents[-1].gmtime
541-
regressionslist.append(
542-
cls(
543-
regression._actim_report.entry,
544-
regression.gmtime,
545-
regression.gmtime_filed,
546-
last_activity,
547-
regression.treename,
548-
regression.versionline,
549-
regression.backburner,
550-
regression.identified,
551-
regression.mailreport(lastreport_gmtime),
552-
)
553-
)
554-
555-
regressionslist.sort(key=lambda x: x.gmtime_activity, reverse=True)
556-
categories = cls.categorize(regressionslist, lastreport_gmtime)
557-
558-
report_gmtime = int(datetime.datetime.now(datetime.timezone.utc).timestamp())
587+
@classmethod
588+
def publish(cls, reports, *, interactive=True):
589+
report_gmtime = int(_mail_now().timestamp())
590+
lastreport_msgid = None
559591
with tempfile.TemporaryDirectory() as tmpdirname:
560-
for counter, treename in enumerate(categories.keys()):
561-
report = cls.pagecreate(categories[treename], treename, lastreport_msgid)
562-
592+
counter = 0
593+
for treename, report in reports.items():
563594
if not report:
564595
logger.info("Nothing to report for %s" % treename)
565596
continue
566597

567598
filename = os.path.join(tmpdirname, "%s-regzbotreport-%s" % (counter, treename))
568-
msg = cls.__create_mail(report, treename)
599+
msg = cls.__create_mail(report, treename, fixed_message_id=not interactive)
569600
lastreport_msgid = msg["Message-ID"].strip("<>")
570-
print("#" * 120)
571-
print("\n%s\n" % filename)
572-
print("#" * 120)
573-
print(report)
601+
if interactive:
602+
print("#" * 120)
603+
print("\n%s\n" % filename)
604+
print("#" * 120)
605+
print(report)
574606
with open(filename, "w") as out:
575-
gen = email.generator.Generator(out)
607+
gen = email_generator.Generator(out)
576608
gen.flatten(msg)
609+
counter += 1
577610

578-
print("#" * 120)
611+
if counter == 0:
612+
return
613+
614+
if not interactive:
615+
return
579616

617+
print("#" * 120)
580618
print(
581619
"Review the reports in %s and sent them using \"git send-email --from='Regzbot (on behalf of Thorsten Leemhuis) <regressions@leemhuis.info>' --suppress-cc=self --to '' %s/*\""
582620
% (tmpdirname, tmpdirname)
583621
)
584622
answer = input("Enter c to confirm you sent the report, anything else to abort: ")
585623
if answer.lower() != "c":
586624
return
625+
587626
regzbot.RegzbotState.set("lastreport_mainline_gmtime", report_gmtime)
588627
regzbot.RegzbotState.set("lastreport_mainline_msgid", lastreport_msgid)
589-
lastreport_msgid = regzbot.RegzbotState.get("lastreport_mainline_msgid")
590628

591629
logger.debug("[report] generated")
630+
631+
@classmethod
632+
def compile(cls, *, interactive=True):
633+
logger.debug("[reportmail] generating")
634+
reports, _categories = cls.prepare(interactive=interactive)
635+
cls.publish(reports, interactive=interactive)
636+
637+
638+
def dumpall_mail():
639+
reports, categories = RegExportMailReport.prepare(interactive=False)
640+
if not any(category["entries"] for category in categories.get("mainline", {}).values()):
641+
return
642+
643+
yield reports.get("mainline", "")
644+
yield "\n"

0 commit comments

Comments
 (0)