@@ -126,6 +126,66 @@ 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 )
140+ report (message = msg , category = category , format = "html" )
141+
142+
143+ def report_with_list (summary , data , columns , row_format , links = None , total = None , limit = 100 , category = "Other" ):
144+ """Append the upgrade report with a new entry that displays a list of records.
145+
146+ The entry consists of a category (title) and a summary (body).
147+ 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.
148+
149+ :param str category: Title of a report entry.
150+ :param str summary: Description of a report entry.
151+ :param List[Tuple] data: Any data in the form of a list of tuples, e.g. the output of cr.fetchall().
152+ :param Tuple[str] columns: Arbitrary names for each column in "data". All columns must be named and
153+ the order of these names must be the same as in "data".
154+ :param Dict[str, Tuple[str, str, str]] links: Optional model/record links, e.g.:
155+ {
156+ "partner_link": ("res.partner", "id", "name"),
157+ "company_link": ("res.company", "company_id", "company_name"),
158+ }
159+ :param str row_format: The way a row in a list is formatted, using named placeholders, e.g.:
160+ "Partner {partner_link} that lives in {city} works at company {company_link}."
161+ :param int limit: The maximum number of records that are going to be displayed in the report.
162+ :param int total: If the original list was limited prior to calling this method, the original, total number of
163+ records can be provided with this parameter.
164+ """
165+
166+ def row_to_html (row ):
167+ row_dict = dict (zip (columns , row ))
168+ row_dict .update (
169+ {
170+ link : get_anchor_link_to_record (rec_model , row_dict [id_col ], row_dict [name_col ])
171+ for link , (rec_model , id_col , name_col ) in links .items ()
172+ }
173+ )
174+ return "<li>{}</li>" .format (row_format .format (** row_dict ))
175+
176+ if not data :
177+ row_to_html (columns )
178+ return report_with_summary (summary = summary , details = "No records to display." , category = category )
179+
180+ total = total or len (data )
181+ disclaimer = "The total number of affected records is {}." .format (total )
182+ if total > limit :
183+ disclaimer += " This list is limited to {} records." .format (limit )
184+
185+ rows = "<ul>\n " + "\n " .join ([row_to_html (row ) for row in data [:limit ]]) + "\n </ul>"
186+ return report_with_summary (summary , "<i>{}</i>{}" .format (disclaimer , rows ), category )
187+
188+
129189def announce_release_note (cr ):
130190 filepath = os .path .join (os .path .dirname (__file__ ), "release-note.xml" )
131191 with open (filepath , "rb" ) as fp :
0 commit comments