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