diff options
| author | Antony Lesuisse <al@openerp.com> | 2012-12-17 02:19:58 +0100 |
|---|---|---|
| committer | Antony Lesuisse <al@openerp.com> | 2012-12-17 02:19:58 +0100 |
| commit | 76ce1a297275718ce16044bad6d04a9d3f051e76 (patch) | |
| tree | 52c1bb6dfe5829dd31fda46f7ad4e7830dc399b0 /addons/document/document_storage.py | |
| parent | 0adc138bdc95de0f5d1e6e41d7ce5281fe5c1f33 (diff) | |
document partial refactoring and cleanups
bzr revid: al@openerp.com-20121217011958-70esu8d6q54fu0zd
Diffstat (limited to 'addons/document/document_storage.py')
| -rw-r--r-- | addons/document/document_storage.py | 278 |
1 files changed, 0 insertions, 278 deletions
diff --git a/addons/document/document_storage.py b/addons/document/document_storage.py deleted file mode 100644 index 9ad70497b2b..00000000000 --- a/addons/document/document_storage.py +++ /dev/null @@ -1,278 +0,0 @@ -# -*- encoding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# -# Copyright (C) P. Christeas, 2009, all rights reserved -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. -# -############################################################################## - -from osv import osv, fields -import os -import tools -import base64 -import errno -import logging -import shutil -from StringIO import StringIO -import psycopg2 -from tools.misc import ustr -from tools.translate import _ -from osv.orm import except_orm -import random -import string -import pooler -import nodes -from content_index import cntIndex -_logger = logging.getLogger(__name__) - -""" The algorithm of data storage - -We have to consider 3 cases of data /retrieval/: - Given (context,path) we need to access the file (aka. node). - given (directory, context), we need one of its children (for listings, views) - given (ir.attachment, context), we need its data and metadata (node). - -For data /storage/ we have the cases: - Have (ir.attachment, context), we modify the file (save, update, rename etc). - Have (directory, context), we create a file. - Have (path, context), we create or modify a file. - -Note that in all above cases, we don't explicitly choose the storage media, -but always require a context to be present. - -Note that a node will not always have a corresponding ir.attachment. Dynamic -nodes, for once, won't. Their metadata will be computed by the parent storage -media + directory. - -The algorithm says that in any of the above cases, our first goal is to locate -the node for any combination of search criteria. It would be wise NOT to -represent each node in the path (like node[/] + node[/dir1] + node[/dir1/dir2]) -but directly jump to the end node (like node[/dir1/dir2]) whenever possible. - -We also contain all the parenting loop code in one function. This is intentional, -because one day this will be optimized in the db (Pg 8.4). - -""" - -class nodefd_db(StringIO, nodes.node_descriptor): - """ A descriptor to db data - """ - def __init__(self, parent, ira_browse, mode): - nodes.node_descriptor.__init__(self, parent) - self._size = 0L - if mode.endswith('b'): - mode = mode[:-1] - - if mode in ('r', 'r+'): - data = ira_browse.datas - if data: - self._size = len(data) - StringIO.__init__(self, data) - elif mode in ('w', 'w+'): - StringIO.__init__(self, None) - # at write, we start at 0 (= overwrite), but have the original - # data available, in case of a seek() - elif mode == 'a': - StringIO.__init__(self, None) - else: - _logger.error("Incorrect mode %s is specified.", mode) - raise IOError(errno.EINVAL, "Invalid file mode.") - self.mode = mode - - def size(self): - return self._size - - def close(self): - # we now open a *separate* cursor, to update the data. - # FIXME: this may be improved, for concurrency handling - par = self._get_parent() - # uid = par.context.uid - cr = pooler.get_db(par.context.dbname).cursor() - try: - if self.mode in ('w', 'w+', 'r+'): - data = self.getvalue() - icont = '' - mime = '' - filename = par.path - if isinstance(filename, (tuple, list)): - filename = '/'.join(filename) - - try: - mime, icont = cntIndex.doIndex(data, filename=filename, - content_type=None, realfname=None) - except Exception: - _logger.debug('Cannot index file:', exc_info=True) - pass - - try: - icont_u = ustr(icont) - except UnicodeError: - icont_u = '' - - out = psycopg2.Binary(data) - cr.execute("UPDATE ir_attachment " \ - "SET db_datas = %s, file_size=%s, " \ - " index_content= %s, file_type=%s " \ - " WHERE id = %s", - (out, len(data), icont_u, mime, par.file_id)) - elif self.mode == 'a': - data = self.getvalue() - out = psycopg2.Binary(data) - cr.execute("UPDATE ir_attachment " \ - "SET db_datas = COALESCE(db_datas,'') || %s, " \ - " file_size = COALESCE(file_size, 0) + %s " \ - " WHERE id = %s", - (out, len(data), par.file_id)) - cr.commit() - except Exception: - _logger.exception('Cannot update db file #%d for close.', par.file_id) - raise - finally: - cr.close() - StringIO.close(self) - -class document_storage(osv.osv): - """ The primary object for data storage. - Each instance of this object is a storage media, in which our application - can store contents. The object here controls the behaviour of the storage - media. - The referring document.directory-ies will control the placement of data - into the storage. - - It is a bad idea to have multiple document.storage objects pointing to - the same tree of filesystem storage. - """ - _name = 'document.storage' - _description = 'Storage Media' - - _columns = { - 'name': fields.char('Name', size=64, required=True, select=1), - 'write_date': fields.datetime('Date Modified', readonly=True), - 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), - 'create_date': fields.datetime('Date Created', readonly=True), - 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), - 'user_id': fields.many2one('res.users', 'Owner'), - 'group_ids': fields.many2many('res.groups', 'document_storage_group_rel', 'item_id', 'group_id', 'Groups'), - 'dir_ids': fields.one2many('document.directory', 'parent_id', 'Directories'), - 'readonly': fields.boolean('Read Only', help="If set, media is for reading only"), - } - - _defaults = { - 'user_id': lambda self, cr, uid, ctx: uid, - } - - def get_data(self, cr, uid, id, file_node, context=None, fil_obj=None): - """ retrieve the contents of some file_node having storage_id = id - optionally, fil_obj could point to the browse object of the file - (ir.attachment) - """ - boo = self.browse(cr, uid, id, context=context) - if fil_obj: - ira = fil_obj - else: - ira = self.pool.get('ir.attachment').browse(cr, uid, file_node.file_id, context=context) - data = ira.datas - if data: - out = data.decode('base64') - else: - out = '' - return out - - def get_file(self, cr, uid, id, file_node, mode, context=None): - """ Return a file-like object for the contents of some node - """ - if context is None: - context = {} - boo = self.browse(cr, uid, id, context=context) - - ira = self.pool.get('ir.attachment').browse(cr, uid, file_node.file_id, context=context) - return nodefd_db(file_node, ira_browse=ira, mode=mode) - - def set_data(self, cr, uid, id, file_node, data, context=None, fil_obj=None): - """ store the data. - This function MUST be used from an ir.attachment. It wouldn't make sense - to store things persistently for other types (dynamic). - """ - boo = self.browse(cr, uid, id, context=context) - if fil_obj: - ira = fil_obj - else: - ira = self.pool.get('ir.attachment').browse(cr, uid, file_node.file_id, context=context) - - _logger.debug( "Store data for ir.attachment #%d." % ira.id) - store_fname = None - fname = None - filesize = len(data) - self.pool.get('ir.attachment').write(cr, uid, [file_node.file_id], {'datas': data.encode('base64')}, context=context) - # 2nd phase: store the metadata - try: - icont = '' - mime = ira.file_type - if not mime: - mime = "" - try: - mime, icont = cntIndex.doIndex(data, ira.datas_fname, ira.file_type or None, fname) - except Exception: - _logger.debug('Cannot index file.', exc_info=True) - pass - try: - icont_u = ustr(icont) - except UnicodeError: - icont_u = '' - # a hack: /assume/ that the calling write operation will not try - # to write the fname and size, and update them in the db concurrently. - # We cannot use a write() here, because we are already in one. - cr.execute('UPDATE ir_attachment SET file_size = %s, index_content = %s, file_type = %s WHERE id = %s', (filesize, icont_u, mime, file_node.file_id)) - file_node.content_length = filesize - file_node.content_type = mime - return True - except Exception, e : - self._logger.warning("Cannot save data.", exc_info=True) - # should we really rollback once we have written the actual data? - # at the db case (only), that rollback would be safe - raise except_orm(_('Error at doc write!'), str(e)) - - def prepare_unlink(self, cr, uid, storage_bo, fil_bo): - """ Before we unlink a file (fil_boo), prepare the list of real - files that have to be removed, too. """ - pass - - def do_unlink(self, cr, uid, unres): - return True - - def simple_rename(self, cr, uid, file_node, new_name, context=None): - """ A preparation for a file rename. - It will not affect the database, but merely check and perhaps - rename the realstore file. - - @return the dict of values that can safely be be stored in the db. - """ - # nothing to do for a rename, allow to change the db field - return { 'name': new_name, 'datas_fname': new_name } - - def simple_move(self, cr, uid, file_node, ndir_bro, context=None): - """ A preparation for a file move. - It will not affect the database, but merely check and perhaps - move the realstore file. - - @param ndir_bro a browse object of document.directory, where this - file should move to. - @return the dict of values that can safely be be stored in the db. - """ - return { 'parent_id': ndir_bro.id } - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: |
