OCA reporting engine fork for dev and update.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

557 lines
29 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
5 years ago
6 years ago
6 years ago
6 years ago
  1. # Copyright 2009-2018 Noviat.
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from datetime import datetime, date
  4. import re
  5. from types import CodeType
  6. from xlsxwriter.utility import xl_rowcol_to_cell
  7. from odoo import fields, models, _
  8. from odoo.exceptions import UserError
  9. class ReportXlsxAbstract(models.AbstractModel):
  10. _inherit = 'report.report_xlsx.abstract'
  11. def generate_xlsx_report(self, workbook, data, objects):
  12. self._define_formats(workbook)
  13. for ws_params in self._get_ws_params(workbook, data, objects):
  14. ws_name = ws_params.get('ws_name')
  15. ws_name = self._check_ws_name(ws_name)
  16. ws = workbook.add_worksheet(ws_name)
  17. generate_ws_method = getattr(
  18. self, ws_params['generate_ws_method'])
  19. generate_ws_method(workbook, ws, ws_params, data, objects)
  20. def _check_ws_name(self, name, sanitize=True):
  21. pattern = re.compile(r'[/\\*\[\]:?]') # invalid characters: /\*[]:?
  22. max_chars = 31
  23. if sanitize:
  24. # we could drop these two lines since a similar
  25. # sanitize is done in tools.misc PatchedXlsxWorkbook
  26. name = pattern.sub('', name)
  27. name = name[:max_chars]
  28. else:
  29. if len(name) > max_chars:
  30. raise UserError(_(
  31. "Programming Error:\n\n"
  32. "Excel Sheet name '%s' should not exceed %s characters."
  33. ) % (name, max_chars))
  34. special_chars = pattern.findall(name)
  35. if special_chars:
  36. raise UserError(_(
  37. "Programming Error:\n\n"
  38. "Excel Sheet name '%s' contains unsupported special "
  39. "characters: '%s'."
  40. ) % (name, special_chars))
  41. return name
  42. def _get_ws_params(self, workbook, data, objects):
  43. """
  44. Return list of dictionaries with parameters for the
  45. worksheets.
  46. Keywords:
  47. - 'generate_ws_method': mandatory
  48. - 'ws_name': name of the worksheet
  49. - 'title': title of the worksheet
  50. - 'wanted_list': list of column names
  51. - 'col_specs': cf. XXX
  52. The 'generate_ws_method' must be present in your report
  53. and contain the logic to generate the content of the worksheet.
  54. """
  55. return []
  56. def _define_xls_headers(self, workbook):
  57. """
  58. Predefined worksheet headers/footers.
  59. """
  60. hf_params = {
  61. 'font_size': 8,
  62. 'font_style': 'I', # B: Bold, I: Italic, U: Underline
  63. }
  64. self.xls_headers = {
  65. 'standard': ''
  66. }
  67. report_date = fields.Datetime.context_timestamp(
  68. self.env.user, datetime.now()).strftime('%Y-%m-%d %H:%M')
  69. self.xls_footers = {
  70. 'standard': (
  71. '&L&%(font_size)s&%(font_style)s' + report_date +
  72. '&R&%(font_size)s&%(font_style)s&P / &N'
  73. ) % hf_params,
  74. }
  75. def _define_formats(self, workbook):
  76. """
  77. This section contains a number of pre-defined formats.
  78. It is recommended to use these in order to have a
  79. consistent look & feel between your XLSX reports.
  80. """
  81. self._define_xls_headers(workbook)
  82. border_grey = '#D3D3D3'
  83. border = {'border': True, 'border_color': border_grey}
  84. theader = dict(border, bold=True)
  85. bg_yellow = '#FFFFCC'
  86. bg_blue = '#CCFFFF'
  87. num_format = '#,##0.00'
  88. num_format_conditional = '{0};[Red]-{0};{0}'.format(num_format)
  89. pct_format = '#,##0.00%'
  90. pct_format_conditional = '{0};[Red]-{0};{0}'.format(pct_format)
  91. int_format = '#,##0'
  92. int_format_conditional = '{0};[Red]-{0};{0}'.format(int_format)
  93. date_format = 'YYYY-MM-DD'
  94. theader_yellow = dict(theader, bg_color=bg_yellow)
  95. theader_blue = dict(theader, bg_color=bg_blue)
  96. # format for worksheet title
  97. self.format_ws_title = workbook.add_format(
  98. {'bold': True, 'font_size': 14})
  99. # no border formats
  100. self.format_left = workbook.add_format({'align': 'left'})
  101. self.format_center = workbook.add_format({'align': 'center'})
  102. self.format_right = workbook.add_format({'align': 'right'})
  103. self.format_amount_left = workbook.add_format(
  104. {'align': 'left', 'num_format': num_format})
  105. self.format_amount_center = workbook.add_format(
  106. {'align': 'center', 'num_format': num_format})
  107. self.format_amount_right = workbook.add_format(
  108. {'align': 'right', 'num_format': num_format})
  109. self.format_amount_conditional_left = workbook.add_format(
  110. {'align': 'left', 'num_format': num_format_conditional})
  111. self.format_amount_conditional_center = workbook.add_format(
  112. {'align': 'center', 'num_format': num_format_conditional})
  113. self.format_amount_conditional_right = workbook.add_format(
  114. {'align': 'right', 'num_format': num_format_conditional})
  115. self.format_percent_left = workbook.add_format(
  116. {'align': 'left', 'num_format': pct_format})
  117. self.format_percent_center = workbook.add_format(
  118. {'align': 'center', 'num_format': pct_format})
  119. self.format_percent_right = workbook.add_format(
  120. {'align': 'right', 'num_format': pct_format})
  121. self.format_percent_conditional_left = workbook.add_format(
  122. {'align': 'left', 'num_format': pct_format_conditional})
  123. self.format_percent_conditional_center = workbook.add_format(
  124. {'align': 'center', 'num_format': pct_format_conditional})
  125. self.format_percent_conditional_right = workbook.add_format(
  126. {'align': 'right', 'num_format': pct_format_conditional})
  127. self.format_integer_left = workbook.add_format(
  128. {'align': 'left', 'num_format': int_format})
  129. self.format_integer_center = workbook.add_format(
  130. {'align': 'center', 'num_format': int_format})
  131. self.format_integer_right = workbook.add_format(
  132. {'align': 'right', 'num_format': int_format})
  133. self.format_integer_conditional_left = workbook.add_format(
  134. {'align': 'right', 'num_format': int_format_conditional})
  135. self.format_integer_conditional_center = workbook.add_format(
  136. {'align': 'center', 'num_format': int_format_conditional})
  137. self.format_integer_conditional_right = workbook.add_format(
  138. {'align': 'right', 'num_format': int_format_conditional})
  139. self.format_date_left = workbook.add_format(
  140. {'align': 'left', 'num_format': date_format})
  141. self.format_date_center = workbook.add_format(
  142. {'align': 'center', 'num_format': date_format})
  143. self.format_date_right = workbook.add_format(
  144. {'align': 'right', 'num_format': date_format})
  145. self.format_left_bold = workbook.add_format(
  146. {'align': 'left', 'bold': True})
  147. self.format_center_bold = workbook.add_format(
  148. {'align': 'center', 'bold': True})
  149. self.format_right_bold = workbook.add_format(
  150. {'align': 'right', 'bold': True})
  151. self.format_amount_left_bold = workbook.add_format(
  152. {'align': 'left', 'bold': True, 'num_format': num_format})
  153. self.format_amount_center_bold = workbook.add_format(
  154. {'align': 'center', 'bold': True, 'num_format': num_format})
  155. self.format_amount_right_bold = workbook.add_format(
  156. {'align': 'right', 'bold': True, 'num_format': num_format})
  157. self.format_amount_conditional_left_bold = workbook.add_format(
  158. {'align': 'left', 'bold': True,
  159. 'num_format': num_format_conditional})
  160. self.format_amount_conditional_center_bold = workbook.add_format(
  161. {'align': 'center', 'bold': True,
  162. 'num_format': num_format_conditional})
  163. self.format_amount_conditional_right_bold = workbook.add_format(
  164. {'align': 'right', 'bold': True,
  165. 'num_format': num_format_conditional})
  166. self.format_percent_left_bold = workbook.add_format(
  167. {'align': 'left', 'bold': True, 'num_format': pct_format})
  168. self.format_percent_center_bold = workbook.add_format(
  169. {'align': 'center', 'bold': True, 'num_format': pct_format})
  170. self.format_percent_right_bold = workbook.add_format(
  171. {'align': 'right', 'bold': True, 'num_format': pct_format})
  172. self.format_percent_conditional_left_bold = workbook.add_format(
  173. {'align': 'left', 'bold': True,
  174. 'num_format': pct_format_conditional})
  175. self.format_percent_conditional_center_bold = workbook.add_format(
  176. {'align': 'center', 'bold': True,
  177. 'num_format': pct_format_conditional})
  178. self.format_percent_conditional_right_bold = workbook.add_format(
  179. {'align': 'right', 'bold': True,
  180. 'num_format': pct_format_conditional})
  181. self.format_integer_left_bold = workbook.add_format(
  182. {'align': 'left', 'bold': True, 'num_format': int_format})
  183. self.format_integer_center_bold = workbook.add_format(
  184. {'align': 'center', 'bold': True, 'num_format': int_format})
  185. self.format_integer_right_bold = workbook.add_format(
  186. {'align': 'right', 'bold': True, 'num_format': int_format})
  187. self.format_integer_conditional_left_bold = workbook.add_format(
  188. {'align': 'left', 'bold': True,
  189. 'num_format': int_format_conditional})
  190. self.format_integer_conditional_center_bold = workbook.add_format(
  191. {'align': 'center', 'bold': True,
  192. 'num_format': int_format_conditional})
  193. self.format_integer_conditional_right_bold = workbook.add_format(
  194. {'align': 'right', 'bold': True,
  195. 'num_format': int_format_conditional})
  196. self.format_date_left_bold = workbook.add_format(
  197. {'align': 'left', 'bold': True, 'num_format': date_format})
  198. self.format_date_center_bold = workbook.add_format(
  199. {'align': 'center', 'bold': True, 'num_format': date_format})
  200. self.format_date_right_bold = workbook.add_format(
  201. {'align': 'right', 'bold': True, 'num_format': date_format})
  202. # formats for worksheet table column headers
  203. self.format_theader_yellow_left = workbook.add_format(theader_yellow)
  204. self.format_theader_yellow_center = workbook.add_format(
  205. dict(theader_yellow, align='center'))
  206. self.format_theader_yellow_right = workbook.add_format(
  207. dict(theader_yellow, align='right'))
  208. self.format_theader_yellow_amount_left = workbook.add_format(
  209. dict(theader_yellow, num_format=num_format, align='left'))
  210. self.format_theader_yellow_amount_center = workbook.add_format(
  211. dict(theader_yellow, num_format=num_format, align='center'))
  212. self.format_theader_yellow_amount_right = workbook.add_format(
  213. dict(theader_yellow, num_format=num_format, align='right'))
  214. self.format_theader_yellow_amount_conditional_left = workbook.\
  215. add_format(dict(theader_yellow, num_format=num_format_conditional,
  216. align='left'))
  217. self.format_theader_yellow_amount_conditional_center = workbook.\
  218. add_format(dict(theader_yellow, num_format=num_format_conditional,
  219. align='center'))
  220. self.format_theader_yellow_amount_conditional_right = workbook.\
  221. add_format(dict(theader_yellow, num_format=num_format_conditional,
  222. align='right'))
  223. self.format_theader_yellow_percent_left = workbook.add_format(
  224. dict(theader_yellow, num_format=pct_format, align='left'))
  225. self.format_theader_yellow_percent_center = workbook.add_format(
  226. dict(theader_yellow, num_format=pct_format, align='center'))
  227. self.format_theader_yellow_percent_right = workbook.add_format(
  228. dict(theader_yellow, num_format=pct_format, align='right'))
  229. self.format_theader_yellow_percent_conditional_left = workbook.\
  230. add_format(dict(theader_yellow, num_format=pct_format_conditional,
  231. align='left'))
  232. self.format_theader_yellow_percent_conditional_center = workbook.\
  233. add_format(dict(theader_yellow, num_format=pct_format_conditional,
  234. align='center'))
  235. self.format_theader_yellow_percent_conditional_right = workbook.\
  236. add_format(dict(theader_yellow, num_format=pct_format_conditional,
  237. align='right'))
  238. self.format_theader_yellow_integer_left = workbook.add_format(
  239. dict(theader_yellow, num_format=int_format, align='left'))
  240. self.format_theader_yellow_integer_center = workbook.add_format(
  241. dict(theader_yellow, num_format=int_format, align='center'))
  242. self.format_theader_yellow_integer_right = workbook.add_format(
  243. dict(theader_yellow, num_format=int_format, align='right'))
  244. self.format_theader_yellow_integer_conditional_left = workbook.\
  245. add_format(dict(theader_yellow, num_format=int_format_conditional,
  246. align='left'))
  247. self.format_theader_yellow_integer_conditional_center = workbook.\
  248. add_format(dict(theader_yellow, num_format=int_format_conditional,
  249. align='center'))
  250. self.format_theader_yellow_integer_conditional_right = workbook.\
  251. add_format(dict(theader_yellow, num_format=int_format_conditional,
  252. align='right'))
  253. self.format_theader_blue_left = workbook.add_format(theader_blue)
  254. self.format_theader_blue_center = workbook.add_format(
  255. dict(theader_blue, align='center'))
  256. self.format_theader_blue_right = workbook.add_format(
  257. dict(theader_blue, align='right'))
  258. self.format_theader_blue_amount_left = workbook.add_format(
  259. dict(theader_blue, num_format=num_format, align='left'))
  260. self.format_theader_blue_amount_center = workbook.add_format(
  261. dict(theader_blue, num_format=num_format, align='center'))
  262. self.format_theader_blue_amount_right = workbook.add_format(
  263. dict(theader_blue, num_format=num_format, align='right'))
  264. self.format_theader_blue_amount_conditional_left = workbook.\
  265. add_format(dict(theader_blue, num_format=num_format_conditional,
  266. align='left'))
  267. self.format_theader_blue_amount_conditional_center = workbook.\
  268. add_format(dict(theader_blue, num_format=num_format_conditional,
  269. align='center'))
  270. self.format_theader_blue_amount_conditional_right = workbook.\
  271. add_format(dict(theader_blue, num_format=num_format_conditional,
  272. align='right'))
  273. self.format_theader_blue_percent_left = workbook.add_format(
  274. dict(theader_blue, num_format=pct_format, align='left'))
  275. self.format_theader_blue_percent_center = workbook.add_format(
  276. dict(theader_blue, num_format=pct_format, align='center'))
  277. self.format_theader_blue_percent_right = workbook.add_format(
  278. dict(theader_blue, num_format=pct_format, align='right'))
  279. self.format_theader_blue_percent_conditional_left = workbook.\
  280. add_format(dict(theader_blue, num_format=pct_format_conditional,
  281. align='left'))
  282. self.format_theader_blue_percent_conditional_center = workbook.\
  283. add_format(dict(theader_blue, num_format=pct_format_conditional,
  284. align='center'))
  285. self.format_theader_blue_percent_conditional_right = workbook.\
  286. add_format(dict(theader_blue, num_format=pct_format_conditional,
  287. align='right'))
  288. self.format_theader_blue_integer_left = workbook.add_format(
  289. dict(theader_blue, num_format=int_format, align='left'))
  290. self.format_theader_blue_integer_center = workbook.add_format(
  291. dict(theader_blue, num_format=int_format, align='center'))
  292. self.format_theader_blue_integer_right = workbook.add_format(
  293. dict(theader_blue, num_format=int_format, align='right'))
  294. self.format_theader_blue_integer_conditional_left = workbook.\
  295. add_format(dict(theader_blue, num_format=int_format_conditional,
  296. align='left'))
  297. self.format_theader_blue_integer_conditional_center = workbook.\
  298. add_format(dict(theader_blue, num_format=int_format_conditional,
  299. align='center'))
  300. self.format_theader_blue_integer_conditional_right = workbook.\
  301. add_format(dict(theader_blue, num_format=int_format_conditional,
  302. align='right'))
  303. # formats for worksheet table cells
  304. self.format_tcell_left = workbook.add_format(
  305. dict(border, align='left'))
  306. self.format_tcell_center = workbook.add_format(
  307. dict(border, align='center'))
  308. self.format_tcell_right = workbook.add_format(
  309. dict(border, align='right'))
  310. self.format_tcell_amount_left = workbook.add_format(
  311. dict(border, num_format=num_format, align='left'))
  312. self.format_tcell_amount_center = workbook.add_format(
  313. dict(border, num_format=num_format, align='center'))
  314. self.format_tcell_amount_right = workbook.add_format(
  315. dict(border, num_format=num_format, align='right'))
  316. self.format_tcell_amount_conditional_left = workbook.add_format(
  317. dict(border, num_format=num_format_conditional, align='left'))
  318. self.format_tcell_amount_conditional_center = workbook.add_format(
  319. dict(border, num_format=num_format_conditional, align='center'))
  320. self.format_tcell_amount_conditional_right = workbook.add_format(
  321. dict(border, num_format=num_format_conditional, align='right'))
  322. self.format_tcell_percent_left = workbook.add_format(
  323. dict(border, num_format=pct_format, align='left'))
  324. self.format_tcell_percent_center = workbook.add_format(
  325. dict(border, num_format=pct_format, align='center'))
  326. self.format_tcell_percent_right = workbook.add_format(
  327. dict(border, num_format=pct_format, align='right'))
  328. self.format_tcell_percent_conditional_left = workbook.add_format(
  329. dict(border, num_format=pct_format_conditional, align='left'))
  330. self.format_tcell_percent_conditional_center = workbook.add_format(
  331. dict(border, num_format=pct_format_conditional, align='center'))
  332. self.format_tcell_percent_conditional_right = workbook.add_format(
  333. dict(border, num_format=pct_format_conditional, align='right'))
  334. self.format_tcell_integer_left = workbook.add_format(
  335. dict(border, num_format=int_format, align='left'))
  336. self.format_tcell_integer_center = workbook.add_format(
  337. dict(border, num_format=int_format, align='center'))
  338. self.format_tcell_integer_right = workbook.add_format(
  339. dict(border, num_format=int_format, align='right'))
  340. self.format_tcell_integer_conditional_left = workbook.add_format(
  341. dict(border, num_format=int_format_conditional, align='left'))
  342. self.format_tcell_integer_conditional_center = workbook.add_format(
  343. dict(border, num_format=int_format_conditional, align='center'))
  344. self.format_tcell_integer_conditional_right = workbook.add_format(
  345. dict(border, num_format=int_format_conditional, align='right'))
  346. self.format_tcell_date_left = workbook.add_format(
  347. dict(border, num_format=date_format, align='left'))
  348. self.format_tcell_date_center = workbook.add_format(
  349. dict(border, num_format=date_format, align='center'))
  350. self.format_tcell_date_right = workbook.add_format(
  351. dict(border, num_format=date_format, align='right'))
  352. self.format_tcell_left_bold = workbook.add_format(
  353. dict(border, align='left', bold=True))
  354. self.format_tcell_center_bold = workbook.add_format(
  355. dict(border, align='center', bold=True))
  356. self.format_tcell_right_bold = workbook.add_format(
  357. dict(border, align='right', bold=True))
  358. self.format_tcell_amount_left_bold = workbook.add_format(
  359. dict(border, num_format=num_format, align='left', bold=True))
  360. self.format_tcell_amount_center_bold = workbook.add_format(
  361. dict(border, num_format=num_format, align='center', bold=True))
  362. self.format_tcell_amount_right_bold = workbook.add_format(
  363. dict(border, num_format=num_format, align='right', bold=True))
  364. self.format_tcell_amount_conditional_left_bold = workbook.\
  365. add_format(dict(border, num_format=num_format_conditional,
  366. align='left', bold=True))
  367. self.format_tcell_amount_conditional_center_bold = workbook.\
  368. add_format(dict(border, num_format=num_format_conditional,
  369. align='center', bold=True))
  370. self.format_tcell_amount_conditional_right_bold = workbook.\
  371. add_format(dict(border, num_format=num_format_conditional,
  372. align='right', bold=True))
  373. self.format_tcell_percent_left_bold = workbook.add_format(
  374. dict(border, num_format=pct_format, align='left', bold=True))
  375. self.format_tcell_percent_center_bold = workbook.add_format(
  376. dict(border, num_format=pct_format, align='center', bold=True))
  377. self.format_tcell_percent_right_bold = workbook.add_format(
  378. dict(border, num_format=pct_format, align='right', bold=True))
  379. self.format_tcell_percent_conditional_left_bold = workbook.\
  380. add_format(dict(border, num_format=pct_format_conditional,
  381. align='left', bold=True))
  382. self.format_tcell_percent_conditional_center_bold = workbook.\
  383. add_format(dict(border, num_format=pct_format_conditional,
  384. align='center', bold=True))
  385. self.format_tcell_percent_conditional_right_bold = workbook.\
  386. add_format(dict(border, num_format=pct_format_conditional,
  387. align='right', bold=True))
  388. self.format_tcell_integer_left_bold = workbook.add_format(
  389. dict(border, num_format=int_format, align='left', bold=True))
  390. self.format_tcell_integer_center_bold = workbook.add_format(
  391. dict(border, num_format=int_format, align='center', bold=True))
  392. self.format_tcell_integer_right_bold = workbook.add_format(
  393. dict(border, num_format=int_format, align='right', bold=True))
  394. self.format_tcell_integer_conditional_left_bold = workbook.\
  395. add_format(dict(border, num_format=int_format_conditional,
  396. align='left', bold=True))
  397. self.format_tcell_integer_conditional_center_bold = workbook.\
  398. add_format(dict(border, num_format=int_format_conditional,
  399. align='center', bold=True))
  400. self.format_tcell_integer_conditional_right_bold = workbook.\
  401. add_format(dict(border, num_format=int_format_conditional,
  402. align='right', bold=True))
  403. self.format_tcell_date_left_bold = workbook.add_format(
  404. dict(border, num_format=date_format, align='left', bold=True))
  405. self.format_tcell_date_center_bold = workbook.add_format(
  406. dict(border, num_format=date_format, align='center', bold=True))
  407. self.format_tcell_date_right_bold = workbook.add_format(
  408. dict(border, num_format=date_format, align='right', bold=True))
  409. def _set_column_width(self, ws, ws_params):
  410. """
  411. Set width for all columns included in the 'wanted_list'.
  412. """
  413. col_specs = ws_params.get('col_specs')
  414. wl = ws_params.get('wanted_list') or []
  415. for pos, col in enumerate(wl):
  416. if col not in col_specs:
  417. raise UserError(_(
  418. "Programming Error:\n\n"
  419. "The '%s' column is not defined in the worksheet "
  420. "column specifications.") % col)
  421. ws.set_column(pos, pos, col_specs[col]['width'])
  422. def _write_ws_title(self, ws, row_pos, ws_params, merge_range=False):
  423. """
  424. Helper function to ensure consistent title formats
  425. troughout all worksheets.
  426. Requires 'title' keyword in ws_params.
  427. """
  428. title = ws_params.get('title')
  429. if not title:
  430. raise UserError(_(
  431. "Programming Error:\n\n"
  432. "The 'title' parameter is mandatory "
  433. "when calling the '_write_ws_title' method."))
  434. if merge_range:
  435. wl = ws_params.get('wanted_list')
  436. if wl and len(wl) > 1:
  437. ws.merge_range(
  438. row_pos, 0, row_pos, len(wl) - 1,
  439. title, self.format_ws_title)
  440. else:
  441. ws.write_string(row_pos, 0, title, self.format_ws_title)
  442. return row_pos + 2
  443. def _write_line(self, ws, row_pos, ws_params, col_specs_section=None,
  444. render_space=None, default_format=None):
  445. """
  446. Write a line with all columns included in the 'wanted_list'.
  447. Use the entry defined by the col_specs_section.
  448. An empty cell will be written if no col_specs_section entry
  449. for a column.
  450. """
  451. col_specs = ws_params.get('col_specs')
  452. wl = ws_params.get('wanted_list') or []
  453. pos = 0
  454. for col in wl:
  455. if col not in col_specs:
  456. raise UserError(_(
  457. "Programming Error:\n\n"
  458. "The '%s' column is not defined the worksheet "
  459. "column specifications.") % col)
  460. colspan = col_specs[col].get('colspan') or 1
  461. cell_spec = col_specs[col].get(col_specs_section) or {}
  462. if not cell_spec:
  463. cell_value = None
  464. cell_type = 'blank'
  465. cell_format = default_format
  466. else:
  467. cell_value = cell_spec.get('value')
  468. if isinstance(cell_value, CodeType):
  469. cell_value = self._eval(cell_value, render_space)
  470. cell_type = cell_spec.get('type')
  471. cell_format = cell_spec.get('format') or default_format
  472. if not cell_type:
  473. # test bool first since isinstance(val, int) returns
  474. # True when type(val) is bool
  475. if isinstance(cell_value, bool):
  476. cell_type = 'boolean'
  477. elif isinstance(cell_value, str):
  478. cell_type = 'string'
  479. elif isinstance(cell_value, (int, float)):
  480. cell_type = 'number'
  481. elif isinstance(cell_value, datetime):
  482. cell_type = 'datetime'
  483. elif isinstance(cell_value, date):
  484. cell_value = datetime.combine(
  485. cell_value, datetime.min.time())
  486. cell_type = 'datetime'
  487. else:
  488. if not cell_value:
  489. cell_type = 'blank'
  490. else:
  491. msg = _(
  492. "%s, _write_line : programming error "
  493. "detected while processing "
  494. "col_specs_section %s, column %s"
  495. ) % (__name__, col_specs_section, col)
  496. if cell_value:
  497. msg += _(", cellvalue %s")
  498. raise UserError(msg)
  499. colspan = cell_spec.get('colspan') or colspan
  500. args_pos = [row_pos, pos]
  501. args_data = [cell_value]
  502. if cell_format:
  503. if isinstance(cell_format, CodeType):
  504. cell_format = self._eval(cell_format, render_space)
  505. args_data.append(cell_format)
  506. if colspan > 1:
  507. args_pos += [row_pos, pos + colspan - 1]
  508. args = args_pos + args_data
  509. ws.merge_range(*args)
  510. else:
  511. ws_method = getattr(ws, 'write_%s' % cell_type)
  512. args = args_pos + args_data
  513. ws_method(*args)
  514. pos += colspan
  515. return row_pos + 1
  516. @staticmethod
  517. def _render(code):
  518. return compile(code, '<string>', 'eval')
  519. @staticmethod
  520. def _eval(val, render_space):
  521. if not render_space:
  522. render_space = {}
  523. if 'datetime' not in render_space:
  524. render_space['datetime'] = datetime
  525. # the use of eval is not a security thread as long as the
  526. # col_specs template is defined in a python module
  527. return eval(val, render_space) # pylint: disable=W0123,W8112
  528. @staticmethod
  529. def _rowcol_to_cell(row, col, row_abs=False, col_abs=False):
  530. return xl_rowcol_to_cell(row, col, row_abs=row_abs, col_abs=col_abs)