@@ -106,7 +106,7 @@ def html_escape(text):
106106}
107107
108108
109- def add_to_migration_reports (message , category = "Other" , format = "text" ):
109+ def report (message , category = "Other" , format = "text" ):
110110 assert format in {"text" , "html" , "md" , "rst" }
111111 if format == "md" :
112112 message = md2html (dedent (message ))
@@ -126,6 +126,89 @@ 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+ add_to_migration_reports = report
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+ return msg
146+
147+
148+ def report_with_list (summary , data , columns , row_format , links = None , total = None , limit = 100 , category = "Other" ):
149+ """Append the upgrade report with a new entry that displays a list of records.
150+
151+ The entry consists of a category (title) and a summary (body).
152+ The entry displays a list of records previously returned by SQL query, or any list.
153+
154+ .. example::
155+
156+ .. code-block:: python
157+
158+ total = cr.rowcount
159+ data = cr.fetchmany(20)
160+ util.report_with_list(
161+ summary="The following records were altered.",
162+ data=data,
163+ columns=("id", "name", "city", "comment", "company_id", "company_name"),
164+ row_format="Partner with id {partner_link} works at company {company_link} in {city}, ({comment})",
165+ links={"company_link": ("res.company", "company_id", "company_name"), "partner_link": ("res.partner", "id", "name")},
166+ total=total,
167+ category="Accounting"
168+ )
169+
170+ :param str summary: description of a report entry.
171+ :param list(tuple) data: data to report, each entry would be a row in the report.
172+ It could be empty, in which case only the summary is rendered.
173+ :param tuple(str) columns: columns in `data`, can be referenced in `row_format`.
174+ :param str row_format: format for rows, can use any name from `columns` or `links`, e.g.:
175+ "Partner {partner_link} that lives in {city} works at company {company_link}."
176+ :param dict(str, tuple(str, str, str)) links: optional model/record links spec,
177+ the keys can be referenced in `row_format`.
178+ :param int total: optional, total number of records.
179+ Taken as `len(data)` when `None` is passed.
180+ Useful when `data` was limited by the caller.
181+ :param int limit: maximum number of records to list in the report.
182+ If `data` contains more records the `total` number would be
183+ included in the report as well.
184+ :param str category: title of a report entry.
185+ """
186+
187+ def row_to_html (row ):
188+ row_dict = dict (zip (columns , row ))
189+ if links :
190+ row_dict .update (
191+ {
192+ link : get_anchor_link_to_record (rec_model , row_dict [id_col ], row_dict [name_col ])
193+ for link , (rec_model , id_col , name_col ) in links .items ()
194+ }
195+ )
196+ return "<li>{}</li>" .format (row_format .format (** row_dict ))
197+
198+ if not data :
199+ row_to_html (columns ) # Validate the format is correct, including links
200+ return report_with_summary (summary = summary , details = "" , category = category )
201+
202+ limit = min (limit , len (data ))
203+ total = len (data ) if total is None else total
204+ disclaimer = "The total number of affected records is {}." .format (total )
205+ if total > limit :
206+ disclaimer += " This list is limited to {} records." .format (limit )
207+
208+ rows = "<ul>\n " + "\n " .join ([row_to_html (row ) for row in data [:limit ]]) + "\n </ul>"
209+ return report_with_summary (summary , "<i>{}</i>{}" .format (disclaimer , rows ), category )
210+
211+
129212def announce_release_note (cr ):
130213 filepath = os .path .join (os .path .dirname (__file__ ), "release-note.xml" )
131214 with open (filepath , "rb" ) as fp :
0 commit comments