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.

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