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
109
pool = pooler.get_pool(cr.dbname)
113
for change in changes:
116
changes_values[change] = self.get_selection(cr, model, field)
117
# Browse each line to replace partner type by it's human readable value (selection)
118
# partner_type is the 3rd column
120
tmp_line = list(line)
121
# Delete some columns if needed
123
tmp_line = self.delete_x_column(list(line), column_deletion)
124
for change in changes:
126
# use line value to search into changes_values[change] (the list of selection) the right value
127
tmp_line[column] = changes_values[change][tmp_line[column]]
128
new_data.append(self.line_to_utf8(tmp_line))
131
def archive(self, cr, uid):
133
Create an archive with sqlrequests params and processrequests params.
135
# open buffer for result zipfile
136
zip_buffer = StringIO()
137
# Prepare some values
138
pool = pooler.get_pool(cr.dbname)
140
# List is composed of a tuple containing:
142
# - key of sqlrequests dict to fetch its SQL request
144
for fileparams in self.processrequests:
145
if not fileparams.get('filename', False):
146
raise osv.except_osv(_('Error'), _('Filename param is missing!'))
147
if not fileparams.get('key', False):
148
raise osv.except_osv(_('Error'), _('Key param is missing!'))
149
# temporary file (process filename to display datetime data instead of %(year)s chars)
150
filename = pool.get('ir.sequence')._process(cr, uid, fileparams['filename'] or '') or fileparams['filename']
151
if filename not in files:
152
tmp_file = NamedTemporaryFile('w+b', delete=False)
154
tmp_file = files[filename]
156
# fetch data with given sql query
157
sql = self.sqlrequests[fileparams['key']]
158
if fileparams.get('query_params', False):
159
cr.execute(sql, fileparams['query_params'])
162
sqlres = cr.fetchall()
163
# Fetch ID column and mark lines as exported
164
if fileparams.get('id', None) != None:
165
if not fileparams.get('object', False):
166
raise osv.except_osv(_('Error'), _('object param is missing to use ID one.'))
167
# prepare needed values
168
object_name = fileparams['object']
169
pool = pooler.get_pool(cr.dbname)
170
tablename = pool.get(object_name)._table
172
raise osv.except_osv(_('Error'), _("Table name not found for the given object: %s") % (fileparams['object'],))
173
key_column_number = fileparams['id']
174
# get ids from previous request
175
ids = [x and x[key_column_number] or 0 for x in sqlres]
176
# mark lines as exported
178
update_request = 'UPDATE ' + tablename + ' SET exported=\'t\' WHERE id in %s'
180
cr.execute(update_request, (tuple(ids),))
182
raise osv.except_osv(_('Error'), _('An error occured: %s') % (e.message and e.message or '',))
184
# Check if a function is given. If yes, use it.
185
# If not, transform lines into UTF-8. Note that postprocess method should transform lines into UTF-8 ones.
186
if fileparams.get('function', False):
187
fnct = getattr(self, fileparams['function'], False)
188
delete_columns = fileparams.get('delete_columns', False)
189
# If the function has some params, use them.
190
if fnct and fileparams.get('fnct_params', False):
191
without_headers = fnct(cr, uid, sqlres, fileparams['fnct_params'], column_deletion=delete_columns)
193
without_headers = fnct(cr, uid, sqlres, column_deletion=delete_columns)
195
# Change to UTF-8 all unicode elements
197
# Delete useless columns if needed
198
if fileparams.get('delete_columns', False):
199
line = self.delete_x_column(line, fileparams['delete_columns'])
200
without_headers.append(self.line_to_utf8(line))
201
result = without_headers
202
if fileparams.get('headers', False):
203
headers = [fileparams['headers']]
207
# Write result in a CSV writer then close it.
208
writer = csv.writer(tmp_file, quoting=csv.QUOTE_ALL)
209
writer.writerows(result)
210
# Only add a link to the temporary file if not in "files" dict
211
if filename not in files:
212
files[filename] = tmp_file
214
# WRITE RESULT INTO AN ARCHIVE
216
out_zipfile = zipfile.ZipFile(zip_buffer, "w")
217
for filename in files:
218
tmpfile = files[filename]
219
# close temporary file
221
# write content into zipfile
222
out_zipfile.write(tmpfile.name, filename, zipfile.ZIP_DEFLATED)
223
# unlink temporary file
224
os.unlink(tmpfile.name)
227
out = zip_buffer.getvalue()
231
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: