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.

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