-
Notifications
You must be signed in to change notification settings - Fork 9
/
attachment.py
153 lines (134 loc) · 4.96 KB
/
attachment.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# -*- coding: utf-8 -*-
"""
attachment
Send attachments to S3
:copyright: © 2012-2015 by Openlabs Technologies & Consulting (P) Limited
:license: BSD, see LICENSE for more details.
"""
try:
import hashlib
except ImportError:
hashlib = None
import md5
from boto.s3.key import Key
from boto.s3.connection import S3Connection
from boto.exception import S3ResponseError
from trytond.config import config
from trytond.transaction import Transaction
from trytond.pool import PoolMeta
__all__ = ['Attachment']
__metaclass__ = PoolMeta
class Attachment:
"Attachment"
__name__ = 'ir.attachment'
@classmethod
def __setup__(cls):
super(Attachment, cls).__setup__()
cls._error_messages.update({
"no_such_key": "File: %s with Key: %s doesn't exist in S3 bucket"
})
def get_data(self, name):
"""
Get the data from S3 instead of filesystem.
The filename is built as '<DBNAME>/<FILENAME>' in the given S3 bucket
:param name: name of field name
:return: Buffer of the file binary
"""
if not config.has_section('attachment_s3'):
return super(Attachment, self).get_data(name)
s3_conn = S3Connection(
config.get('attachment_s3', 'access_key'),
config.get('attachment_s3', 'secret_key')
)
bucket = s3_conn.get_bucket(config.get('attachment_s3', 'bucket_name'))
db_name = Transaction().cursor.dbname
format_ = Transaction().context.pop(
'%s.%s' % (self.__name__, name), ''
)
value = None
if name == 'data_size' or format_ == 'size':
value = 0
if self.digest:
filename = self.digest
if self.collision:
filename = filename + '-' + str(self.collision)
filename = "/".join([db_name, filename])
if name == 'data_size' or format_ == 'size':
key = bucket.lookup(filename)
if key is not None:
# Get the size only if bucket has key;
value = key.size
else:
k = Key(bucket)
k.key = filename
try:
value = buffer(k.get_contents_as_string())
except S3ResponseError:
self.raise_user_error(
"no_such_key", error_args=(self.name, filename)
)
return value
@classmethod
def set_data(cls, attachments, name, value):
"""
Save the attachment to S3 instead of the filesystem
:param attachments: List of ir.attachment instances
:param name: name of the field
:param value: binary data of the attachment (string)
"""
if not config.has_section('attachment_s3'):
return super(Attachment, cls).set_data(attachments, name, value)
s3_conn = S3Connection(
config.get('attachment_s3', 'access_key'),
config.get('attachment_s3', 'secret_key')
)
bucket = s3_conn.get_bucket(config.get('attachment_s3', 'bucket_name'))
if value is None:
return
cursor = Transaction().cursor
db_name = cursor.dbname
if hashlib:
digest = hashlib.md5(value).hexdigest()
else:
digest = md5.new(value).hexdigest()
filename = "/".join([db_name, digest])
collision = 0
if bucket.get_key(filename):
key2 = Key(bucket)
key2.key = filename
data2 = key2.get_contents_as_string()
if value != data2:
cursor.execute('SELECT DISTINCT(collision) '
'FROM ir_attachment '
'WHERE digest = %s '
'AND collision != 0 '
'ORDER BY collision', (digest,))
collision2 = 0
for row in cursor.fetchall():
collision2 = row[0]
filename = "/".join([
db_name, digest + '-' + str(collision2)
])
if bucket.get_key(filename):
key2 = Key(bucket)
key2.key = filename
data2 = key2.get_contents_as_string()
if value == data2:
collision = collision2
break
if collision == 0:
collision = collision2 + 1
filename = "/".join([
db_name, digest + '-' + str(collision)
])
key = Key(bucket)
key.key = filename
key.set_contents_from_string(value[:])
else:
key = Key(bucket)
key.key = filename
key.set_contents_from_string(value[:])
cls.write(attachments, {
'digest': digest,
'collision': collision,
})