[MERGE] OPW 279130 - translate errors raised inside product_id_change (sale/purchase...
[odoo/odoo.git] / addons / document / content_index.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #    
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
19 #
20 ##############################################################################
21 import logging
22 import os
23 import tempfile
24 from subprocess import Popen, PIPE
25
26 class NhException(Exception):
27     pass
28
29
30 class indexer(object):
31     """ An indexer knows how to parse the content of some file.
32     
33         Typically, one indexer should be instantiated per file
34         type.
35         Override this class to add more functionality. Note that
36         you should only override the Content or the File methods
37         that give an optimal result. """
38         
39     def _getMimeTypes(self):
40         """ Return supported mimetypes """
41         return []
42     
43     def _getExtensions(self):
44         return []
45     
46     def _getDefMime(self, ext):
47         """ Return a mimetype for this document type, ideally the
48             closest to the extension ext. """
49         mts = self._getMimeTypes();
50         if len (mts):
51             return mts[0]
52         return None
53
54     def indexContent(self, content, filename=None, realfile = None):
55         """ Use either content or the real file, to index.
56             Some parsers will work better with the actual
57             content, others parse a file easier. Try the
58             optimal.
59         """
60         res = ''
61         try:
62             if content != None:
63                 return self._doIndexContent(content)
64         except NhException:
65             pass
66         
67         if realfile != None:
68             try:
69                 return self._doIndexFile(realfile)
70             except NhException:
71                 pass
72             
73             fp = open(realfile,'rb')
74             try:
75                 content2 = fp.read()
76             finally:    
77                 fp.close()
78             
79             # The not-handled exception may be raised here
80             return self._doIndexContent(content2)
81             
82             
83         # last try, with a tmp file
84         if content:
85             try:
86                 fname,ext = filename and os.path.splitext(filename) or ('','')
87                 fd, rfname = tempfile.mkstemp(suffix=ext)
88                 os.write(fd, content)
89                 os.close(fd)
90                 res = self._doIndexFile(rfname)
91                 os.unlink(rfname)
92                 return res
93             except NhException:
94                 pass
95
96         raise NhException('No appropriate method to index file')
97     
98     def _doIndexContent(self,content):
99         raise NhException("Content not handled here")
100
101     def _doIndexFile(self,fpath):
102         raise NhException("Content not handled here")
103
104     def __repr__(self):
105         return "<indexer %s.%s>" %(self.__module__, self.__class__.__name__)
106         
107
108 def mime_match(mime, mdict):
109     if mdict.has_key(mime):
110         return (mime, mdict[mime])
111     if '/' in mime:
112         mpat = mime.split('/')[0]+'/*'
113         if mdict.has_key(mpat):
114             return (mime, mdict[mpat])
115     
116     return (None, None)
117
118 class contentIndex(object):
119     __logger = logging.getLogger('addons.document.content_index')
120     def __init__(self):
121         self.mimes = {}
122         self.exts = {}
123     
124     def register(self, obj):
125         f = False
126         for mime in obj._getMimeTypes():
127             self.mimes[mime] = obj
128             f = True
129             
130         for ext in obj._getExtensions():
131             self.exts[ext] = obj
132             f = True
133             
134         if f:
135             self.__logger.debug('Register content indexer: %r', obj)
136         if not f:
137             raise Exception("Your indexer should at least suport a mimetype or extension")
138     
139     def doIndex(self, content, filename=None, content_type=None, realfname = None, debug=False):
140         fobj = None
141         fname = None
142         mime = None
143         if content_type and self.mimes.has_key(content_type):
144             mime = content_type
145             fobj = self.mimes[content_type]
146         elif filename:
147             bname,ext = os.path.splitext(filename)
148             if self.exts.has_key(ext):
149                 fobj = self.exts[ext]
150                 mime = fobj._getDefMime(ext)
151         
152         if content_type and not fobj:
153             mime,fobj = mime_match(content_type, self.mimes)
154         
155         if not fobj:
156             try:
157                 if realfname :
158                     fname = realfname
159                 else:
160                     try:
161                         bname,ext = os.path.splitext(filename or 'test.tmp')
162                     except Exception:
163                         bname, ext = filename, 'tmp'
164                     fd, fname = tempfile.mkstemp(suffix=ext)
165                     os.write(fd, content)
166                     os.close(fd)
167             
168                 pop = Popen(['file','-b','--mime',fname], shell=False, stdout=PIPE)
169                 (result, _) = pop.communicate()
170                 
171                 mime2 = result.split(';')[0]
172                 self.__logger.debug('File gave us: %s', mime2)
173                 # Note that the temporary file still exists now.
174                 mime,fobj = mime_match(mime2, self.mimes)
175                 if not mime:
176                     mime = mime2
177             except Exception:
178                 self.__logger.exception('Cannot determine mime type')
179         
180         try:
181             if fobj:
182                 res = (mime, fobj.indexContent(content,filename,fname or realfname) )
183             else:
184                 self.__logger.debug("Have no object, return (%s, None)", mime)
185                 res = (mime, None )
186         except Exception:
187             self.__logger.exception("Could not index file %s (%s)",
188                                     filename, fname or realfname)
189             res = None
190         
191         # If we created a tmp file, unlink it now
192         if not realfname and fname:
193             try:
194                 os.unlink(fname)
195             except Exception:
196                 self.__logger.exception("Could not unlink %s", fname)
197         
198         return res
199
200 cntIndex = contentIndex()
201
202 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: