[IMP][FIX] report_xlsx: handle duplicate name exceptions

We want to avoid duplicated sheet names exceptions the same following the same
philosophy that Odoo implements overriding the main library to avoid the 31
characters limit triming the strings before sending them to the library.

In some cases, there's not much control over this as the reports send automated
data and the potential exception is hidden underneath making it hard to debug
the original issue. Even so, different names can become the same one as their
strings are trimmed to those 31 character limit.

This way, once we come across with a duplicated, we set that final 3 characters
with a sequence that we evaluate on the fly. So for instance:

- 'Sheet name' will be 'Sheet name~01'
- The next 'Sheet name' will try to rename to 'Sheet name~01' as well and then
  that will give us 'Sheet name~02'.
- And the next 'Sheet name' will try to rename to 'Sheet name~01' and then to
  'Sheet name~02' and finally it will be able to 'Sheet name~03'.
- An so on as many times as duplicated sheet names come to the workbook up to
  100 for each sheet name. We set such limit as we don't want to truncate the
  strings too much and keeping in mind that this issue don't usually ocurrs.

TT31938
This commit is contained in:
david
2021-09-17 17:30:47 +02:00
parent 29d20090a3
commit 9061daa173

View File

@@ -2,7 +2,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from io import BytesIO
import re
from odoo import models
import logging
@@ -10,6 +10,63 @@ _logger = logging.getLogger(__name__)
try:
import xlsxwriter
class PatchedXlsxWorkbook(xlsxwriter.Workbook):
def _check_sheetname(self, sheetname, is_chartsheet=False):
"""We want to avoid duplicated sheet names exceptions the same
following the same philosophy that Odoo implements overriding the
main library to avoid the 31 characters limit triming the strings
before sending them to the library.
In some cases, there's not much control over this as the reports
send automated data and the potential exception is hidden underneath
making it hard to debug the original issue. Even so, different names
can become the same one as their strings are trimmed to those 31
character limit.
This way, once we come across with a duplicated, we set that final 3
characters with a sequence that we evaluate on the fly. So for
instance:
- 'Sheet name' will be 'Sheet name~01'
- The next 'Sheet name' will try to rename to 'Sheet name~01' as
well and then that will give us 'Sheet name~02'.
- And the next 'Sheet name' will try to rename to 'Sheet name~01'
and then to 'Sheet name~02' and finally it will be able to
'Sheet name~03'.
- An so on as many times as duplicated sheet names come to the
workbook up to 100 for each sheet name. We set such limit as we
don't want to truncate the strings too much and keeping in mind
that this issue don't usually ocurrs.
"""
try:
return super()._check_sheetname(
sheetname, is_chartsheet=is_chartsheet
)
except xlsxwriter.exceptions.DuplicateWorksheetName:
pattern = re.compile(r"~[0-9]{2}$")
duplicated_secuence = (
re.search(pattern, sheetname) and int(sheetname[-2:]) or 0
)
# Only up to 100 duplicates
deduplicated_secuence = "~{:02d}".format(
duplicated_secuence + 1
)
if duplicated_secuence > 99:
raise xlsxwriter.exceptions.DuplicateWorksheetName
if duplicated_secuence:
sheetname = re.sub(
pattern, deduplicated_secuence, sheetname
)
elif len(sheetname) <= 28:
sheetname += deduplicated_secuence
else:
sheetname = sheetname[:28] + deduplicated_secuence
# Refeed the method until we get an unduplicated name
return self._check_sheetname(sheetname, is_chartsheet=is_chartsheet)
xlsxwriter.Workbook = PatchedXlsxWorkbook
except ImportError:
_logger.debug('Can not import xlsxwriter`.')