2
# -*- coding: utf-8 -*-
3
##############################################################################
5
# OpenERP, Open Source Management Solution
6
# Copyright (C) 2013 TeMPO Consulting, MSF. All Rights Reserved
7
# Developer: Olivier DOSSMANN
9
# This program is free software: you can redistribute it and/or modify
10
# it under the terms of the GNU Affero General Public License as
11
# published by the Free Software Foundation, either version 3 of the
12
# License, or (at your option) any later version.
14
# This program is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
# GNU Affero General Public License for more details.
19
# You should have received a copy of the GNU Affero General Public License
20
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22
##############################################################################
25
from tools.translate import _
27
from cStringIO import StringIO
30
from tempfile import NamedTemporaryFile
33
class finance_archive():
35
SQLREQUESTS DICTIONNARY
36
- key: name of the SQL request
37
- value: the SQL request to use
39
PROCESS REQUESTS LIST: list of dict containing info to process some SQL requests
41
- [optional] headers: list of headers that should appears in the CSV file
42
- filename: the name of the result filename in the future ZIP file
43
- key: the name of the key in SQLREQUESTS DICTIONNARY to have the right SQL request
44
- [optional] query_params: data to use to complete SQL requests
45
- [optional] function: name of the function to postprocess data (example: to change selection field into a human readable text)
46
- [optional] fnct_params: params that would used on the given function
47
- [optional] delete_columns: list of columns to delete before writing files into result
48
- [optional] id (need 'object'): number of the column that contains the ID of the element. Column number begin from 0. Note that you should adapt your SQL request to add the ID of lines.
49
- [optional] object (need 'id'): name of the object in the system. For an example: 'account.bank.statement'.
51
+ More than 1 request in 1 file: just use same filename for each request you want to be in the same file.
52
+ If you cannot do a SQL request to create the content of the file, do a simple request (with key) and add a postprocess function that returns the result you want
53
+ Do not repeat headers if you use the same filename for more than 1 request. This avoid having multiple lines as headers.
56
def __init__(self, sql, process):
57
self.sqlrequests = sql
58
self.processrequests = process
60
def line_to_utf8(self, line):
62
Change all elements of this line to UTF-8
68
if type(element) == unicode:
69
newline.append(element.encode('utf-8'))
71
newline.append(element)
74
def delete_x_column(self, line, columns=[]):
76
Take numbers in 'columns' list and delete them from given line.
83
# Create a list of all elements from line except those given in columns
85
for element in sorted([x for x in xrange(len(line)) if x not in columns]):
86
newline.append(line[element])
89
def get_selection(self, cr, model, field):
91
Return a list of all selection from a field in a given model.
93
pool = pooler.get_pool(cr.dbname)
94
data = pool.get(model).fields_get(cr, 1, [field])
95
return dict(data[field]['selection'])
97
def postprocess_selection_columns(self, cr, uid, data, changes, column_deletion=False):
99
This method takes each line from data and change some columns regarding "changes" variable.
100
'changes' should be a list containing some tuples. A tuple is composed of:
101
- a model (example: res.partner)
102
- the selection field in which retrieve all real values (example: partner_type)
103
- the column number in the data lines from which you want to change the value (begin from 0)
108
# Prepare some values
112
for change in changes:
115
changes_values[change] = self.get_selection(cr, model, field)
116
# Browse each line to replace partner type by it's human readable value (selection)
117
# partner_type is the 3rd column
119
tmp_line = list(line)
120
# Delete some columns if needed
122
tmp_line = self.delete_x_column(list(line), column_deletion)
123
for change in changes:
125
# use line value to search into changes_values[change] (the list of selection) the right value
126
tmp_line[column] = changes_values[change][tmp_line[column]]
127
new_data.append(self.line_to_utf8(tmp_line))
130
def archive(self, cr, uid):
132
Create an archive with sqlrequests params and processrequests params.
134
# open buffer for result zipfile
135
zip_buffer = StringIO()
136
# Prepare some values
137
pool = pooler.get_pool(cr.dbname)
139
# List is composed of a tuple containing:
141
# - key of sqlrequests dict to fetch its SQL request
143
for fileparams in self.processrequests:
144
if not fileparams.get('filename', False):
145
raise osv.except_osv(_('Error'), _('Filename param is missing!'))
146
if not fileparams.get('key', False):
147
raise osv.except_osv(_('Error'), _('Key param is missing!'))
148
# temporary file (process filename to display datetime data instead of %(year)s chars)
149
filename = pool.get('ir.sequence')._process(cr, uid, fileparams['filename'] or '') or fileparams['filename']
150
if filename not in files:
151
tmp_file = NamedTemporaryFile('w+b', delete=False)
153
tmp_file = files[filename]
155
# fetch data with given sql query
156
sql = self.sqlrequests[fileparams['key']]
157
if fileparams.get('query_params', False):
158
cr.execute(sql, fileparams['query_params'])
161
sqlres = cr.fetchall()
162
# Fetch ID column and mark lines as exported
163
if fileparams.get('id', None) != None:
164
if not fileparams.get('object', False):
165
raise osv.except_osv(_('Error'), _('object param is missing to use ID one.'))
166
# prepare needed values
167
object_name = fileparams['object']
168
pool = pooler.get_pool(cr.dbname)
169
tablename = pool.get(object_name)._table
171
raise osv.except_osv(_('Error'), _("Table name not found for the given object: %s") % (fileparams['object'],))
172
key_column_number = fileparams['id']
173
# get ids from previous request
174
ids = [x and x[key_column_number] or 0 for x in sqlres]
175
# mark lines as exported
177
update_request = 'UPDATE ' + tablename + ' SET exported=\'t\' WHERE id in %s'
179
cr.execute(update_request, (tuple(ids),))
181
raise osv.except_osv(_('Error'), _('An error occured: %s') % (e.message and e.message or '',))
183
# Check if a function is given. If yes, use it.
184
# If not, transform lines into UTF-8. Note that postprocess method should transform lines into UTF-8 ones.
185
if fileparams.get('function', False):
186
fnct = getattr(self, fileparams['function'], False)
187
delete_columns = fileparams.get('delete_columns', False)
188
# If the function has some params, use them.
189
if fnct and fileparams.get('fnct_params', False):
190
without_headers = fnct(cr, uid, sqlres, fileparams['fnct_params'], column_deletion=delete_columns)
192
without_headers = fnct(cr, uid, sqlres, column_deletion=delete_columns)
194
# Change to UTF-8 all unicode elements
196
# Delete useless columns if needed
197
if fileparams.get('delete_columns', False):
198
line = self.delete_x_column(line, fileparams['delete_columns'])
199
without_headers.append(self.line_to_utf8(line))
200
result = without_headers
201
if fileparams.get('headers', False):
202
headers = [fileparams['headers']]
206
# Write result in a CSV writer then close it.
207
writer = csv.writer(tmp_file, quoting=csv.QUOTE_ALL)
208
writer.writerows(result)
209
# Only add a link to the temporary file if not in "files" dict
210
if filename not in files:
211
files[filename] = tmp_file
213
# WRITE RESULT INTO AN ARCHIVE
215
out_zipfile = zipfile.ZipFile(zip_buffer, "w")
216
for filename in files:
217
tmpfile = files[filename]
218
# close temporary file
220
# write content into zipfile
221
out_zipfile.write(tmpfile.name, filename, zipfile.ZIP_DEFLATED)
222
# unlink temporary file
223
os.unlink(tmpfile.name)
226
out = zip_buffer.getvalue()
230
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: