models.py 7.93 KB
Newer Older
1
2
3
import json
import os
from collections import defaultdict
4
from pyld import jsonld
5
6
import logging
from flask import Response as FlaskResponse
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
7

8

9
10
11
12
13
14
class Leaf(dict):
    _prefix = None
    _frame = {}
    _context = {}

    def __init__(self,
15
16
17
18
19
20
21
22
23
                 *args,
                 **kwargs):

        id = kwargs.pop("id", None)
        context = kwargs.pop("context", self._context)
        vocab = kwargs.pop("vocab", None)
        prefix = kwargs.pop("prefix", None)
        frame = kwargs.pop("frame", None)
        super(Leaf, self).__init__(*args, **kwargs)
24
        if context is not None:
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
25
            self.context = context
26
27
        if frame is not None:
            self._frame = frame
28
        self._prefix = prefix
29
        self.id = id
30
31

    def __getattr__(self, key):
32
33
34
35
36
37
38
        try:
            return object.__getattr__(self, key)
        except AttributeError:
            try:
                return super(Leaf, self).__getitem__(self._get_key(key))
            except KeyError:
                raise AttributeError()
39

40
41
42
43
44
45
    def __setattr__(self, key, value):
        try:
            object.__getattr__(self, key)
            object.__setattr__(self, key, value)
        except AttributeError:
            key = self._get_key(key)
46
47
48
49
            if key == "@context":
                value = self.get_context(value)
            elif key == "@id":
                value = self.get_id(value)
50
51
52
            if key[0] == "_":
                object.__setattr__(self, key, value)
            else:
53
54
55
56
57
58
59
60
61
62
                if value is None:
                    try:
                        super(Leaf, self).__delitem__(key)
                    except KeyError:
                        pass
                else:
                    super(Leaf, self).__setitem__(key, value)

    def get_id(self, id):
        """
63
        Get id, dealing with prefixes
64
        """
65
66
67
        # This is not the most elegant solution to change the @id attribute,
        # but it is the quickest way to have it included in the dictionary
        # without extra boilerplate.
68
69
70
71
        if id and self._prefix and ":" not in id:
            return "{}{}".format(self._prefix, id)
        else:
            return id
72

73
    def __delattr__(self, key):
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
74
75
76
77
        if key in self.__dict__:
            del self.__dict__[key]
        else:
            super(Leaf, self).__delitem__(self._get_key(key))
78

79
    def _get_key(self, key):
80
81
82
        if key[0] == "_":
            return key
        elif key in ["context", "id"]:
83
            return "@{}".format(key)
84
85
        else:
            return key
86

J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
    @staticmethod
    def get_context(context):
        if isinstance(context, list):
            contexts = []
            for c in context:
                contexts.append(Response.get_context(c))
            return contexts
        elif isinstance(context, dict):
            return context
        elif isinstance(context, basestring):
            try:
                with open(context) as f:
                    return json.loads(f.read())
            except IOError:
                return context

103
    def compact(self):
104
        return jsonld.compact(self, self.get_context(self.context))
105
106
107
108
109
110
111
112

    def frame(self, frame=None, options=None):
        if frame is None:
            frame = self._frame
        if options is None:
            options = {}
        return jsonld.frame(self, frame, options)

113
114
115
116
    def jsonld(self, frame=None, options=None,
               context=None, removeContext=None):
        if removeContext is None:
            removeContext = Response._context  # Loop?
117
118
119
        if frame is None:
            frame = self._frame
        if context is None:
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
            context = self.context
        else:
            context = self.get_context(context)
        # For some reason, this causes errors with pyld
        # if options is None:
            # options = {"expandContext": context.copy() }
        js = self
        if frame:
            logging.debug("Framing: %s", json.dumps(self, indent=4))
            logging.debug("Framing with %s", json.dumps(frame, indent=4))
            js = jsonld.frame(js, frame, options)
            logging.debug("Result: %s", json.dumps(js, indent=4))
            logging.debug("Compacting with %s", json.dumps(context, indent=4))
            js = jsonld.compact(js, context, options)
            logging.debug("Result: %s", json.dumps(js, indent=4))
        if removeContext == context:
            del js["@context"]
        return js

    def to_JSON(self, removeContext=None):
        return json.dumps(self.jsonld(removeContext=removeContext),
141
142
143
                          default=lambda o: o.__dict__,
                          sort_keys=True, indent=4)

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
    def flask(self,
              in_headers=False,
              url="http://demos.gsi.dit.upm.es/senpy/senpy.jsonld"):
        """
        Return the values and error to be used in flask
        """
        js = self.jsonld()
        headers = None
        if in_headers:
            ctx = js["@context"]
            headers = {
                "Link": ('<%s>;'
                         'rel="http://www.w3.org/ns/json-ld#context";'
                         ' type="application/ld+json"' % url)
            }
            del js["@context"]
160
        return FlaskResponse(json.dumps(js, indent=4),
161
162
163
                             status=self.get("status", 200),
                             headers=headers,
                             mimetype="application/json")
164

J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
165

166
class Response(Leaf):
167
168
169
170
171
172
173
174
175
176
177
178
    _context = Leaf.get_context("{}/context.jsonld".format(
        os.path.dirname(os.path.realpath(__file__))))
    _frame = {
        "@context": _context,
        "analysis": {
            "@explicit": True,
            "maxPolarityValue": {},
            "minPolarityValue": {},
            "name": {},
            "version": {},
        },
        "entries": {}
179
180
    }

181
182
183
    def __init__(self, *args, **kwargs):
        context = kwargs.pop("context", None)
        frame = kwargs.pop("frame", None)
184
        if context is None:
185
186
187
188
189
190
191
192
193
194
195
196
197
            context = self._context
        self.context = context
        super(Response, self).__init__(
            *args, context=context, frame=frame, **kwargs)
        if self._frame is not None and "entries" in self._frame:
            self.analysis = []
            self.entries = []

    def jsonld(self, frame=None, options=None, context=None, removeContext={}):
        return super(Response, self).jsonld(frame,
                                            options,
                                            context,
                                            removeContext)
198
199
200


class Entry(Leaf):
201
    _context = {
202
203
        "@vocab": ("http://persistence.uni-leipzig.org/"
                   "nlp2rdf/ontologies/nif-core#")
204
205

    }
206

J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
207
    def __init__(self, text=None, emotion_sets=None, opinions=None, **kwargs):
208
209
210
        super(Entry, self).__init__(**kwargs)
        if text:
            self.text = text
211
212
        self.emotionSets = emotion_sets if emotion_sets else []
        self.opinions = opinions if opinions else []
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
213

214

215
class Opinion(Leaf):
216
217
218
    _context = {
        "@vocab": "http://www.gsi.dit.upm.es/ontologies/marl/ns#"
    }
219

J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
220
    def __init__(self, polarityValue=None, hasPolarity=None, *args, **kwargs):
221
        super(Opinion, self).__init__(*args,
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
222
223
                                      **kwargs)
        if polarityValue is not None:
224
            self.hasPolarityValue = polarityValue
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
225
226
        if hasPolarity is not None:
            self.hasPolarity = hasPolarity
227
228
229


class EmotionSet(Leaf):
230
    _context = {}
231

J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
232
    def __init__(self, emotions=None, *args, **kwargs):
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
233
234
        if not emotions:
            emotions = []
235
        super(EmotionSet, self).__init__(context=EmotionSet._context,
J. Fernando Sánchez's avatar
J. Fernando Sánchez committed
236
237
                                         *args,
                                         **kwargs)
238
        self.emotions = emotions or []
239

240

241
class Emotion(Leaf):
242
243
    _context = {}

244

245
class Error(Leaf):
246
247
248
249
250
    # A better pattern would be this:
    # http://flask.pocoo.org/docs/0.10/patterns/apierrors/
    _frame = {}
    _context = {}

251
    def __init__(self, *args, **kwargs):
252
        super(Error, self).__init__(*args, **kwargs)