This repository has been archived by the owner on Oct 25, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
couchdb.py
149 lines (119 loc) · 5.01 KB
/
couchdb.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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# ISIS-DM: the ISIS Data Model API
#
# Copyright (C) 2010 BIREME/PAHO/WHO
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ..utils import base28
from .mapper import Document, TextProperty, FileProperty
import uuid
import couchdbkit
import time
import colander
import deform
def _attach_exists(old_doc, property_name):
if property_name in old_doc:
try:
if old_doc[property_name]['filename'] in old_doc['_attachments']:
return True
except (AttributeError, TypeError):
pass
return False
def _attach_updated(new_doc, property_name):
try:
return new_doc[property_name]['fp'] is not None
except KeyError:
return False
class CouchdbDocument(Document):
def __init__(self, **kwargs):
super(CouchdbDocument, self).__init__(**kwargs)
if '_id' not in kwargs:
self._id = base28.genbase(5)
def __clean_before_save(self, doc):
'''
removes attributes with None values
'''
doc = dict((str(k), v) for k, v in doc.items() if v is not None)
return doc
def to_python(self):
pstruct = super(CouchdbDocument, self).to_python()
try:
pstruct['_id'] = self._id
except AttributeError:
pass
try:
pstruct['_rev'] = self._rev
except AttributeError:
pass
return pstruct
@classmethod
def from_python(cls, pystruct):
if pystruct.get('_id',None) == 'None':
pystruct['_id'] = None
if pystruct.get('_rev',None) == 'None':
pystruct['_rev'] = None
return super(CouchdbDocument, cls).from_python(pystruct)
def save(self, db):
new_doc = self.to_python()
new_doc = self.__clean_before_save(new_doc)
old_doc = db.get(new_doc['_id']) if '_rev' in new_doc else None
while True:
try:
if old_doc is not None and '_attachments' in old_doc:
new_doc['_attachments'] = old_doc['_attachments']
db.save_doc(new_doc)
break
except couchdbkit.ResourceConflict:
time.sleep(0.5)
new_doc['_id'] = base28.genbase(5)
for key in self.__class__:
prop = self.__class__.__getattribute__(self.__class__,key)
if isinstance(prop, FileProperty):
if old_doc is None:
#New document
file_metadata = getattr(self,key, None)
if file_metadata:
db.put_attachment(new_doc, file_metadata['fp'], getattr(self, key)['filename'])
elif _attach_exists(old_doc, key) and not _attach_updated(new_doc, key):
#Attachment exists and had not been changed
new_doc[key] = old_doc[key]
db.save_doc(new_doc)
else:
#Attachment updated
file_metadata = getattr(self,key, None)
if file_metadata:
db.put_attachment(new_doc, file_metadata['fp'], getattr(self, key)['filename'])
self._id, self._rev = new_doc['_id'], new_doc['_rev']
@classmethod
def get(cls, db, doc_id, controls=True):
doc = db.get(doc_id)
couchdocument = cls.from_python(doc)
if not controls:
del(couchdocument._id)
del(couchdocument._rev)
return couchdocument
@classmethod
def get_schema(cls, controls=True):
schema = super(CouchdbDocument, cls).get_schema()
if controls:
rev_definition = colander.SchemaNode(colander.String(),
widget = deform.widget.HiddenWidget(),
default=None,
name='_rev')
id_definition = colander.SchemaNode(colander.String(),
widget = deform.widget.HiddenWidget(),
default=None,
name='_id')
schema.add(rev_definition)
schema.add(id_definition)
return schema