66
77from collections import Counter
88import datetime
9+ from email import generator as email_generator
910from email .message import EmailMessage
1011import email .utils
1112import tempfile
1718logger = 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+
2032class 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