@@ -126,6 +126,76 @@ 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+ .. example::
151+
152+ .. code-block:: python
153+
154+ util.report_with_list(summary="The following records were altered.",
155+ data=cr.fetchall(),
156+ columns=("id", "name", "city", "comment", "company_id", "company_name")
157+ row_format="Partner with id {partner_link} works at company {company_link} in {city}, ({comment})",
158+ links={"company_link": ("res.company", "company_id", "company_name"), "partner_link": ("res.partner", "id", "name")},
159+ category="Accounting"
160+ )
161+
162+ :param str summary: description of a report entry.
163+ :param list(tuple) data: data to report, each entry would be a row in the report.
164+ It could be empty, in which case only the summary is rendered.
165+ :param tuple(str) columns: names for each column in "data", can be referenced in "row_format".
166+ :param str row_format: the way a row in a list is formatted, using named placeholders, e.g.:
167+ "Partner {partner_link} that lives in {city} works at company {company_link}."
168+ :param dict(str, tuple(str, str, str)) links: optional model/record links spec,
169+ the keys can be referenced in "row_format".
170+ :param int total: if the original list was limited prior to calling this method, the original, total number of
171+ records, can be provided with this parameter.
172+ :param int limit: the maximum number of records that are going to be displayed in the report.
173+ :param str category: title of a report entry.
174+ """
175+
176+ def row_to_html (row ):
177+ row_dict = dict (zip (columns , row ))
178+ row_dict .update (
179+ {
180+ link : get_anchor_link_to_record (rec_model , row_dict [id_col ], row_dict [name_col ])
181+ for link , (rec_model , id_col , name_col ) in links .items ()
182+ }
183+ )
184+ return "<li>{}</li>" .format (row_format .format (** row_dict ))
185+
186+ if not data :
187+ row_to_html (columns ) # Validate the format is correct, including links
188+ return report_with_summary (summary = summary , details = "" , category = category )
189+
190+ total = total or len (data )
191+ disclaimer = "The total number of affected records is {}." .format (total )
192+ if total > limit :
193+ disclaimer += " This list is limited to {} records." .format (limit )
194+
195+ rows = "<ul>\n " + "\n " .join ([row_to_html (row ) for row in data [:limit ]]) + "\n </ul>"
196+ return report_with_summary (summary , "<i>{}</i>{}" .format (disclaimer , rows ), category )
197+
198+
129199def announce_release_note (cr ):
130200 filepath = os .path .join (os .path .dirname (__file__ ), "release-note.xml" )
131201 with open (filepath , "rb" ) as fp :
0 commit comments