Files
odoo_source/addons/attachment_indexation/models/ir_attachment.py
T
Stanislas Sobieski 82e8c74f1e [FIX] base: avoid indexing attachment on copy
When sending a mass mailing with a pdf attachment, the ir.attachment
is copied in the mail.composer for each email. Spending time and
resources in the indexing of the pdf. On copy that value is already in
the vals. This is especially true when attachment_indexation is
installed, processing the pdf documents can be costly with pdfminer.

It could also be done by overwriting the copu from ir_attachment but it
could happen a misuse of mass create of ir.attachment with the same file
has the same issue

closes odoo/odoo#100374

X-original-commit: 3d59cc849ed1040c08c4d1488866ba78c80990c8
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-09-17 00:51:32 +02:00

143 lines
4.9 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import io
import logging
import xml.dom.minidom
import zipfile
from odoo import api, models
from odoo.tools.lru import LRU
_logger = logging.getLogger(__name__)
try:
from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.pdfpage import PDFPage
except ImportError:
PDFResourceManager = PDFPageInterpreter = TextConverter = PDFPage = None
_logger.warning("Attachment indexation of PDF documents is unavailable because the 'pdfminer' Python library cannot be found on the system. "
"You may install it from https://pypi.org/project/pdfminer.six/ (e.g. `pip3 install pdfminer.six`)")
FTYPES = ['docx', 'pptx', 'xlsx', 'opendoc', 'pdf']
index_content_cache = LRU(1)
def textToString(element):
buff = u""
for node in element.childNodes:
if node.nodeType == xml.dom.Node.TEXT_NODE:
buff += node.nodeValue
elif node.nodeType == xml.dom.Node.ELEMENT_NODE:
buff += textToString(node)
return buff
class IrAttachment(models.Model):
_inherit = 'ir.attachment'
def _index_docx(self, bin_data):
'''Index Microsoft .docx documents'''
buf = u""
f = io.BytesIO(bin_data)
if zipfile.is_zipfile(f):
try:
zf = zipfile.ZipFile(f)
content = xml.dom.minidom.parseString(zf.read("word/document.xml"))
for val in ["w:p", "w:h", "text:list"]:
for element in content.getElementsByTagName(val):
buf += textToString(element) + "\n"
except Exception:
pass
return buf
def _index_pptx(self, bin_data):
'''Index Microsoft .pptx documents'''
buf = u""
f = io.BytesIO(bin_data)
if zipfile.is_zipfile(f):
try:
zf = zipfile.ZipFile(f)
zf_filelist = [x for x in zf.namelist() if x.startswith('ppt/slides/slide')]
for i in range(1, len(zf_filelist) + 1):
content = xml.dom.minidom.parseString(zf.read('ppt/slides/slide%s.xml' % i))
for val in ["a:t"]:
for element in content.getElementsByTagName(val):
buf += textToString(element) + "\n"
except Exception:
pass
return buf
def _index_xlsx(self, bin_data):
'''Index Microsoft .xlsx documents'''
buf = u""
f = io.BytesIO(bin_data)
if zipfile.is_zipfile(f):
try:
zf = zipfile.ZipFile(f)
content = xml.dom.minidom.parseString(zf.read("xl/sharedStrings.xml"))
for val in ["t"]:
for element in content.getElementsByTagName(val):
buf += textToString(element) + "\n"
except Exception:
pass
return buf
def _index_opendoc(self, bin_data):
'''Index OpenDocument documents (.odt, .ods...)'''
buf = u""
f = io.BytesIO(bin_data)
if zipfile.is_zipfile(f):
try:
zf = zipfile.ZipFile(f)
content = xml.dom.minidom.parseString(zf.read("content.xml"))
for val in ["text:p", "text:h", "text:list"]:
for element in content.getElementsByTagName(val):
buf += textToString(element) + "\n"
except Exception:
pass
return buf
def _index_pdf(self, bin_data):
'''Index PDF documents'''
if PDFResourceManager is None:
return
buf = u""
if bin_data.startswith(b'%PDF-'):
f = io.BytesIO(bin_data)
try:
resource_manager = PDFResourceManager()
with io.StringIO() as content, TextConverter(resource_manager, content) as device:
logging.getLogger("pdfminer").setLevel(logging.CRITICAL)
interpreter = PDFPageInterpreter(resource_manager, device)
for page in PDFPage.get_pages(f):
interpreter.process_page(page)
buf = content.getvalue()
except Exception:
pass
return buf
@api.model
def _index(self, bin_data, mimetype, checksum=None):
if checksum:
cached_content = index_content_cache.get(checksum)
if cached_content:
return cached_content
res = False
for ftype in FTYPES:
buf = getattr(self, '_index_%s' % ftype)(bin_data)
if buf:
res = buf.replace('\x00', '')
break
res = res or super(IrAttachment, self)._index(bin_data, mimetype, checksum=checksum)
if checksum:
index_content_cache[checksum] = res
return res