@@ -126,6 +126,64 @@ def add_to_migration_reports(message, category="Other", format="text"):
126126 _logger .warning ("Upgrade report is growing suspiciously long: %s characters so far." , migration_reports_length )
127127
128128
129+ report = add_to_migration_reports
130+
131+
132+ def report_with_summary (summary , details , category = "Other" ):
133+ """Append the upgrade report with a new entry.
134+
135+ :param str summary: Description of a report entry.
136+ :param str details: Detailed description that is going to be folded by default.
137+ :param str category: Title of a report entry.
138+ """
139+ msg = "<summary>{}<details>{}</details></summary>" .format (summary , details ) if details else \
140+ "<summary>{}</summary>" .format (summary )
141+ report (message = msg , category = category , format = "html" )
142+
143+
144+ def report_with_list (summary , data , columns , row_format , links = None , total = None , limit = 100 , category = "Other" ):
145+ """Append the upgrade report with a new entry that displays a list of records.
146+
147+ The entry consists of a category (title) and a summary (body).
148+ The entry displays a list of records previously returned by an SQL query, or any list as long as it's passed as a Python List of Tuples.
149+
150+ :param str summary: description of a report entry.
151+ :param list(tuple) data: data to report, each entry would be a row in the report.
152+ It could be empty, in which case only the summary is rendered.
153+ :param tuple(str) columns: names for each column in "data", can be referenced in "row_format".
154+ :param str row_format: the way a row in a list is formatted, using named placeholders, e.g.:
155+ "Partner {partner_link} that lives in {city} works at company {company_link}."
156+ :param dict(str, tuple(str, str, str)) links: optional model/record links spec,
157+ the keys can be referenced in "row_format".
158+ :param int total: if the original list was limited prior to calling this method, the original, total number of
159+ records, can be provided with this parameter.
160+ :param int limit: the maximum number of records that are going to be displayed in the report.
161+ :param str category: title of a report entry.
162+ """
163+
164+ def row_to_html (row ):
165+ row_dict = dict (zip (columns , row ))
166+ row_dict .update (
167+ {
168+ link : get_anchor_link_to_record (rec_model , row_dict [id_col ], row_dict [name_col ])
169+ for link , (rec_model , id_col , name_col ) in links .items ()
170+ }
171+ )
172+ return "<li>{}</li>" .format (row_format .format (** row_dict ))
173+
174+ if not data :
175+ row_to_html (columns ) # Validate the format is correct, including links
176+ return report_with_summary (summary = summary , details = "" , category = category )
177+
178+ total = total or len (data )
179+ disclaimer = "The total number of affected records is {}." .format (total )
180+ if total > limit :
181+ disclaimer += " This list is limited to {} records." .format (limit )
182+
183+ rows = "<ul>\n " + "\n " .join ([row_to_html (row ) for row in data [:limit ]]) + "\n </ul>"
184+ return report_with_summary (summary , "<i>{}</i>{}" .format (disclaimer , rows ), category )
185+
186+
129187def announce_release_note (cr ):
130188 filepath = os .path .join (os .path .dirname (__file__ ), "release-note.xml" )
131189 with open (filepath , "rb" ) as fp :
0 commit comments