Passed
Push — master ( 98a6fb...9891f0 )
by Jordi
07:41
created

bika.lims.exportimport.genericsetup.adapters   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 210
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 28
eloc 126
dl 0
loc 210
rs 10
c 0
b 0
f 0

16 Methods

Rating   Name   Duplication   Size   Complexity  
A ATFieldNodeAdapter._exportNode() 0 5 1
A ATFieldNodeAdapter.get_node_value() 0 8 1
A ATDateTimeFieldNodeAdapter.get_json_value() 0 7 2
A ATFileFieldNodeAdapter.get_archive_path() 0 7 1
A ATFileFieldNodeAdapter.set_node_value() 0 5 1
A ATFileFieldNodeAdapter.get_file_data() 0 4 1
A ATFieldNodeAdapter.set_field_value() 0 5 1
A ATDateTimeFieldNodeAdapter.parse_json_value() 0 4 2
A ATFieldNodeAdapter.__init__() 0 3 1
A ATFieldNodeAdapter.get_json_value() 0 13 2
A ATReferenceFieldNodeAdapter.get_json_value() 0 11 3
A ATFieldNodeAdapter.get_field_value() 0 4 1
A ATFieldNodeAdapter.parse_json_value() 0 2 1
A ATFileFieldNodeAdapter.get_json_value() 0 15 4
A ATFieldNodeAdapter._importNode() 0 12 4
A ATFieldNodeAdapter.set_node_value() 0 6 2
1
# -*- coding: utf-8 -*-
2
3
import json
4
5
from bika.lims import api
6
from bika.lims import logger
7
from bika.lims.interfaces.field import IUIDReferenceField
8
from DateTime import DateTime
9
from plone.app.blob.interfaces import IBlobField
10
from Products.Archetypes.interfaces import IBaseObject
11
from Products.Archetypes.interfaces import IDateTimeField
12
from Products.Archetypes.interfaces import IField
13
from Products.Archetypes.interfaces import IFileField
14
from Products.Archetypes.interfaces import IReferenceField
15
from Products.Archetypes.interfaces import ITextField
16
from Products.CMFPlone.utils import safe_unicode
17
from Products.GenericSetup.interfaces import ISetupEnviron
18
from Products.GenericSetup.utils import NodeAdapterBase
19
from zope.component import adapts
20
from zope.interface import implements
21
22
from .config import SITE_ID
23
from .interfaces import IFieldNode
24
from .interfaces import IRecordField
25
26
SKIP_FIELDS = [
27
    "id",
28
    "rights",
29
]
30
31
32
class ATFieldNodeAdapter(NodeAdapterBase):
33
    """Node im- and exporter for Fields.
34
    """
35
    implements(IFieldNode)
36
    adapts(IBaseObject, IField, ISetupEnviron)
37
38
    el = "field"
39
40
    def __init__(self, context, field, environ):
41
        super(ATFieldNodeAdapter, self).__init__(context, environ)
42
        self.field = field
43
44
    def set_field_value(self, value, **kw):
45
        """Set the field value
46
        """
47
        # logger.info("Set: {} -> {}".format(self.field.getName(), value))
48
        return self.field.set(self.context, value, **kw)
49
50
    def get_field_value(self):
51
        """Get the field value
52
        """
53
        return self.field.get(self.context)
54
55
    def get_json_value(self):
56
        """JSON converted field value
57
        """
58
        value = self.get_field_value()
59
        try:
60
            # Always handle the value as unicode
61
            return json.dumps(safe_unicode(value))
62
        except TypeError:
63
            logger.warning(
64
                "ParseError: '{}.{} ('{}') -> {}' is not JSON serializable!"
65
                .format(self.context.getId(), self.field.getName(),
66
                        self.field.type, repr(value)))
67
            return ""
68
69
    def parse_json_value(self, value):
70
        return json.loads(value)
71
72
    def get_node_value(self, value):
73
        """Convert the field value to a XML node
74
        """
75
        node = self._doc.createElement(self.el)
76
        node.setAttribute("name", self.field.getName())
77
        child = self._doc.createTextNode(value)
78
        node.appendChild(child)
79
        return node
80
81
    def set_node_value(self, node):
82
        value = self.parse_json_value(node.nodeValue)
83
        # encode unicodes to UTF8
84
        if isinstance(value, unicode):
85
            value = value.encode("utf8")
86
        self.set_field_value(value)
87
88
    def _exportNode(self):
89
        """Export the object as a DOM node.
90
        """
91
        value = self.get_json_value()
92
        return self.get_node_value(value)
93
94
    def _importNode(self, node):
95
        """Import the object from the DOM node.
96
        """
97
        if self.field.getName() in SKIP_FIELDS:
98
            return
99
        child = node.firstChild
100
        if child is None:
101
            return
102
        if child.nodeName != "#text":
103
            logger.warning("No textnode found!")
104
            return False
105
        self.set_node_value(child)
106
107
    node = property(_exportNode, _importNode)
108
109
110
class ATTextFieldNodeAdapter(ATFieldNodeAdapter):
111
    """Import/Export Text
112
    """
113
    adapts(IBaseObject, ITextField, ISetupEnviron)
114
115
116
class ATFileFieldNodeAdapter(ATFieldNodeAdapter):
117
    """Import/Export Files/Images
118
    """
119
    adapts(IBaseObject, IFileField, ISetupEnviron)
120
121
    def set_node_value(self, node):
122
        filename = node.nodeValue
123
        filepath = "/".join([self.get_archive_path(), filename])
124
        data = self.get_file_data(filepath)
125
        self.set_field_value(data, filename=filename)
126
127
    def get_archive_path(self):
128
        """Get the unified archive path
129
        """
130
        site = self.environ.getSite()
131
        site_path = api.get_path(site)
132
        obj_path = api.get_path(self.context)
133
        return obj_path.replace(site_path, SITE_ID, 1)
134
135
    def get_file_data(self, path):
136
        """Return the file data from the archive path
137
        """
138
        return self.environ.readDataFile(path)
139
140
    def get_json_value(self):
141
        """Returns the filename
142
        """
143
        value = self.get_field_value()
144
145
        if isinstance(value, basestring):
146
            return value
147
148
        filename = safe_unicode(value.filename) or ""
149
        data = value.data
150
        if filename and data:
151
            path = self.get_archive_path()
152
            content_type = value.content_type
153
            self.environ.writeDataFile(filename, str(data), content_type, path)
154
        return filename
155
156
157
class BlobFileFieldNodeAdapter(ATFileFieldNodeAdapter):
158
    """Import/Export Files/Images
159
    """
160
    adapts(IBaseObject, IBlobField, ISetupEnviron)
161
162
163
class ATDateTimeFieldNodeAdapter(ATFieldNodeAdapter):
164
    """Import/Export Date Fields
165
    """
166
    adapts(IBaseObject, IDateTimeField, ISetupEnviron)
167
168
    def get_json_value(self):
169
        """Returns the date as ISO string
170
        """
171
        value = self.field.get(self.context)
172
        if not isinstance(value, DateTime):
173
            return ""
174
        return value.ISO()
175
176
    def parse_json_value(self, value):
177
        if not value:
178
            return None
179
        return DateTime(value)
180
181
182
class ATReferenceFieldNodeAdapter(ATFieldNodeAdapter):
183
    """Import/Export UID Reference Fields
184
    """
185
    adapts(IBaseObject, IReferenceField, ISetupEnviron)
186
187
    def get_json_value(self):
188
        """Returns the date as ISO string
189
        """
190
        value = self.field.get(self.context)
191
        if api.is_object(value):
192
            value = api.get_uid(value)
193
        elif isinstance(value, list):
194
            value = map(api.get_uid, value)
195
        else:
196
            value = ""
197
        return json.dumps(value)
198
199
200
class ATUIDReferenceFieldNodeAdapter(ATReferenceFieldNodeAdapter):
201
    """Import/Export UID Reference Fields
202
    """
203
    adapts(IBaseObject, IUIDReferenceField, ISetupEnviron)
204
205
206
class ATRecordFieldNodeAdapter(ATFieldNodeAdapter):
207
    """Import/Export Records Fields
208
    """
209
    adapts(IBaseObject, IRecordField, ISetupEnviron)
210