Subversion Repositories basico

Rev

Rev 397 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
340 t00mlabs 1
#!/usr/bin/python
2
# -*- coding: utf-8 -*-
3
"""
4
# File: srv_annot.py
5
# Author: Tomás Vírseda
6
# License: GPL v3
7
# Description: Annotations service
8
"""
9
 
10
import os
11
import json
12
import uuid
367 t00mlabs 13
from html import escape
340 t00mlabs 14
import glob
15
from os.path import sep as SEP
16
 
17
from basico.core.mod_env import FILE, LPATH
18
from basico.core.mod_srv import Service
19
 
20
 
21
class Annotation(Service):
22
    def initialize(self):
23
        '''
24
        Setup Annotation Service
25
        '''
26
        self.get_services()
351 t00mlabs 27
        self.__fix_annotations()
340 t00mlabs 28
 
29
 
30
    def get_services(self):
31
        self.srvutl = self.get_service('Utils')
32
 
33
 
351 t00mlabs 34
    def __fix_annotations(self):
35
        """
36
        In Basico 0.4, new field 'created' is introduced.
37
        For annotations created before, created timestamp = updated timestamp.
38
        """
39
 
40
        for filename in self.get_all():
41
            metadata = self.get_metadata_from_file(filename)
395 t00mlabs 42
            if len(metadata) > 0:
43
                try:
44
                    ts = metadata['Created']
45
                except Exception as error:
46
                    # Fix annotation metadata: add 'Created' field
47
                    metadata['Created'] = metadata['Timestamp']
48
                    with open(filename, 'w') as fa:
49
                        json.dump(metadata, fa)
50
                        self.log.debug("Fixed annotation with AID: %s", metadata['AID'])
351 t00mlabs 51
 
380 t00mlabs 52
 
53
    def gen_aid(self, sid='0000000000'):
340 t00mlabs 54
        '''
55
        Generate new annotation id
56
        '''
57
        return "%s@%s" % (sid, str(uuid.uuid4()))
58
 
59
 
60
    def create(self, annotation):
380 t00mlabs 61
        ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + annotation['AID'] + '.json'
340 t00mlabs 62
        ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + annotation['AID'] + '.adoc'
63
        annotation['Timestamp'] = self.srvutl.timestamp()
354 t00mlabs 64
        annotation['Created'] = self.srvutl.timestamp()
340 t00mlabs 65
 
66
        # Write annotation content first and delete it
67
        with open(ANNOTATION_FILE_CONTENT, 'w') as fc:
68
            fc.write(annotation['Content'])
69
        del annotation['Content']
70
 
71
        # Write annotation metadata
72
        with open(ANNOTATION_FILE_METADATA, 'w') as fa:
73
            json.dump(annotation, fa)
74
 
75
        title = self.get_title(annotation['AID'])
76
        self.log.info("Annotation '%s' (%s) created" % (title, annotation['AID']))
77
 
78
 
360 t00mlabs 79
    def update_metadata(self, metadata):
80
        # Update annotation metadata
81
        ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + metadata['AID'] + '.json'
82
        annotation = self.get_metadata_from_file(ANNOTATION_FILE_METADATA)
83
        for key in metadata:
84
            annotation[key] = metadata[key]
85
        annotation['Timestamp'] = self.srvutl.timestamp()
380 t00mlabs 86
 
360 t00mlabs 87
        # Write annotation metadata
88
        with open(ANNOTATION_FILE_METADATA, 'w') as fa:
89
            json.dump(annotation, fa)
90
 
91
        title = self.get_title(annotation['AID'])
92
        self.log.info("Annotation '%s' (%s) updated" % (title, annotation['AID']))
93
 
94
 
95
    def update_timestamp(self, aid):
96
        # Update annotation timestamp
97
        ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + aid + '.json'
98
        annotation = self.get_metadata_from_file(ANNOTATION_FILE_METADATA)
99
        annotation['Timestamp'] = self.srvutl.timestamp()
380 t00mlabs 100
 
360 t00mlabs 101
        # Write annotation metadata
102
        with open(ANNOTATION_FILE_METADATA, 'w') as fa:
103
            json.dump(annotation, fa)
104
 
105
        title = self.get_title(annotation['AID'])
106
        self.log.info("Annotation '%s' (%s) updated" % (title, annotation['AID']))
380 t00mlabs 107
 
108
 
340 t00mlabs 109
    def update(self, annotation):
408 t00mlabs 110
        self.log.debug(annotation)
380 t00mlabs 111
        ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + annotation['AID'] + '.json'
340 t00mlabs 112
        ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + annotation['AID'] + '.adoc'
113
 
114
        annotation['Timestamp'] = self.srvutl.timestamp()
115
        # Write updated annotation first and delete the old one after
116
        with open(ANNOTATION_FILE_CONTENT, 'w') as fc:
117
            fc.write(annotation['Content'])
118
        del annotation['Content']
119
 
120
        # Write annotation metadata
121
        with open(ANNOTATION_FILE_METADATA, 'w') as fa:
122
            json.dump(annotation, fa)
123
 
124
        title = self.get_title(annotation['AID'])
125
        self.log.info("Annotation '%s' (%s) updated" % (title, annotation['AID']))
126
 
127
 
128
    def delete(self, aid):
129
        sid = self.get_sid(aid)
130
        ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + aid + '.json'
131
        ANNOTATION_FILE_CONTENT = LPATH['ANNOTATIONS'] + aid + '.adoc'
132
        title = self.get_title(aid)
133
 
134
        if os.path.exists(ANNOTATION_FILE_METADATA):
135
            os.unlink(ANNOTATION_FILE_METADATA)
136
 
137
        if os.path.exists(ANNOTATION_FILE_CONTENT):
138
            os.unlink(ANNOTATION_FILE_CONTENT)
139
 
140
        self.log.info("Annotation '%s' (%s) deleted" % (title, aid))
141
 
142
 
143
    def get_by_sid(self, sid):
384 t00mlabs 144
        ANNOTATION_FILES = LPATH['ANNOTATIONS'] + '*%s@*.json' % sid
340 t00mlabs 145
        annotations = glob.glob(ANNOTATION_FILES)
146
        annotations.sort(reverse=True)
147
 
148
        return annotations
149
 
150
 
151
    def get_all(self):
152
        return glob.glob(LPATH['ANNOTATIONS'] + '*.json')
153
 
154
 
396 t00mlabs 155
    def get_all_aids(self):
156
        aids = []
157
        fnames = self.get_all()
158
        for fname in fnames:
159
            aid = os.path.basename(fname)[:-5]
160
            aids.append(aid)
161
        return aids
162
 
163
 
340 t00mlabs 164
    def get_total(self):
165
        return len(self.get_all())
166
 
167
 
168
    def get_sid(self, aid):
169
        if '@' in aid:
170
            return aid[:aid.find('@')]
171
        else:
172
            return aid # aid = sid
173
 
174
 
351 t00mlabs 175
    def get_metadata_from_aid(self, aid=None):
389 t00mlabs 176
        ANNOTATION_FILE_METADATA = LPATH['ANNOTATIONS'] + aid + '.json'
177
        if os.path.exists(ANNOTATION_FILE_METADATA):
340 t00mlabs 178
            with open(ANNOTATION_FILE_METADATA, 'r') as fa:
179
                annotation = json.load(fa)
180
            return annotation
181
        else:
182
            return None
183
 
184
 
351 t00mlabs 185
    def get_metadata_from_file(self, filename=None):
395 t00mlabs 186
        metadata = []
351 t00mlabs 187
        if filename is not None:
188
            with open(filename, 'r') as fa:
395 t00mlabs 189
                try:
190
                    metadata = json.load(fa)
191
                except Exception as error:
192
                    self.log.debug("Annotation: %s", filename)
193
                    self.log.error(error)
194
            return metadata
351 t00mlabs 195
        else:
395 t00mlabs 196
            return metadata
351 t00mlabs 197
 
198
 
370 t00mlabs 199
    def get_metadata_value(self, aid, key):
200
        metadata = self.get_metadata_from_aid(aid)
389 t00mlabs 201
        if metadata is not None:
202
            return metadata[key]
203
        return None
370 t00mlabs 204
 
380 t00mlabs 205
 
340 t00mlabs 206
    def is_valid(self, aid):
207
        ANNOTATION_FILE = LPATH['ANNOTATIONS'] + aid + '.json'
208
        valid = os.path.exists(ANNOTATION_FILE)
209
        if valid is False:
210
            self.log.debug("Annotation %s is not valid or it doesn't exist yet." % aid)
408 t00mlabs 211
        # ~ self.log.debug("Annotation valid? %s", valid)
340 t00mlabs 212
        return valid
213
 
397 t00mlabs 214
 
340 t00mlabs 215
    def get_title(self, aid):
216
        ANNOTATION_FILE = LPATH['ANNOTATIONS'] + aid + '.json'
383 t00mlabs 217
        try:
218
            with open(ANNOTATION_FILE, 'r') as fa:
219
                metadata = json.load(fa)
220
                return escape(metadata['Title'])
221
        except:
222
            return None
340 t00mlabs 223
 
397 t00mlabs 224
 
365 t00mlabs 225
    def get_content_file(self, aid):
226
        return LPATH['ANNOTATIONS'] + aid + '.adoc'
227
 
228
 
340 t00mlabs 229
    def search_term(self, term):
230
        matches = set()
231
        annotations = self.get_all()
232
 
233
        for fname in annotations:
234
            # search only in title
235
            with open(fname, 'r') as fa:
236
                try:
237
                    annotation = json.load(fa)
238
                    text = annotation['Title']
239
                    if term.upper() in text.upper():
240
                        # ~ self.log.debug("Found '%s' in '%s'", term, text)
241
                        matches.add(fname)
242
                except Exception as error:
243
                    self.log.error("%s: %s", fname, error)
244
 
245
            # SEARCH IN ALL PROPERTIES (DISABLED)
246
            # ~ with open(fname, 'r') as fa:
247
                # ~ try:
248
                    # ~ annotation = json.load(fa)
249
                    # ~ text = ''
250
                    # ~ for node in annotation:
251
                        # ~ text += annotation[node]
252
                    # ~ if term.upper() in text.upper():
253
                        # ~ self.log.debug("Found '%s' in '%s'", term, text)
254
                        # ~ matches.add(fname)
255
                # ~ except Exception as error:
256
                    # ~ self.log.error("%s: %s", fname, error)
257
 
258
            # SEARCH IN CONTENT (DISABLED)
259
            # ~ fcontent = fname.replace('.json', '.adoc')
260
            # ~ text = open(fcontent, 'r').read()
261
            # ~ if term.upper() in text.upper():
262
                # ~ matches.add(fname)
263
 
264
        return matches
265
 
266
 
397 t00mlabs 267
    def duplicate_from_template(self, aid):
268
        new_aid = self.gen_aid()
269
        fcname = self.get_content_file(aid)
270
        with open(fcname, 'r') as fc:
271
            content = fc.read()
272
        annotation = self.get_metadata_from_aid(aid)
273
        annotation['AID'] = new_aid
274
        annotation['Type'] = 'Note'
275
        annotation['Content'] = content
276
        self.create(annotation)
277
 
278
        return new_aid
279
 
280
 
281
    def duplicate(self, aid):
282
        new_aid = self.gen_aid()
283
        fcname = self.get_content_file(aid)
284
        with open(fcname, 'r') as fc:
285
            content = fc.read()
286
        annotation = self.get_metadata_from_aid(aid)
287
        annotation['AID'] = new_aid
288
        self.create(annotation)
289
 
290
        return new_aid
291
 
292
 
293
 
396 t00mlabs 294
    def get_annotations_by_type(self, atype):
295
        selected = set()
296
        aids = self.get_all_aids()
297
        for aid in aids:
298
            this_type = self.get_metadata_value(aid, 'Type')
299
            if  this_type == atype:
300
                selected.add(aid)
301
        return selected
302
 
303
 
340 t00mlabs 304
    def finalize(self):
305
        pass
306