-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdbmodels.py
328 lines (270 loc) · 8.94 KB
/
dbmodels.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
import datetime
import logging
import string
from google.appengine.ext import db
from google.appengine.api import mail
from google.appengine.api import memcache
from pytz.gae import pytz
from utils import *
# Since app is open to only spacecom.in emails as of now
DEFAULT_COMPANY = 'Spacecom Software LLP'
DEFAULT_TIMEZONE = 'Asia/Kolkata'
def users_key(group = 'Spacecom'): # makes further grouping possible
return db.Key.from_path('users', group)
class User(db.Model):
"""Datastore entity for each user."""
username = db.StringProperty()
email = db.EmailProperty()
fullname = db.StringProperty()
pw_hash = db.StringProperty()
profile_picture = db.BlobProperty()
company = db.StringProperty(default = DEFAULT_COMPANY)
timezone = db.StringProperty(default = DEFAULT_TIMEZONE)
date_created = db.DateTimeProperty() # in user's timezone
verified = db.BooleanProperty(default = False)
@classmethod
def get_group_users(cls, group = 'Spacecom'):
group_users = memcache.get(group)
if not group_users:
group_users = cls.db_group_users(group)
if group_users:
set_cache(group, group_users)
return group_users
@classmethod
def db_group_users(cls, group = 'Spacecom'):
logging.error('DB Query Group')
return cls.all().ancestor(users_key(group))
@classmethod
def get_user(cls, uid_or_username_or_email):
"""
uid_or_username: String
Returns User
Attempts memcache get first.
If memcache get is not successful -> Calls the appropriate DB Query
funciton (And sets to memcache.)
"""
user = memcache.get(uid_or_username_or_email)
if not user:
attribute = what_attribute(uid_or_username_or_email)
if attribute == 'uid':
user = cls.by_id(uid_or_username_or_email)
elif attribute == 'email':
user = cls.by_email(uid_or_username_or_email)
elif attribute == 'username':
user = cls.by_username(uid_or_username_or_email)
if user:
user.set_user_caches()
return user
@classmethod
def by_id(cls, uid):
'''
Returns the user with the id uid.
uid: String
Returns: User entity
'''
logging.error('DB QUERY User')
return cls.get_by_id(long(uid), parent = users_key())
@classmethod
def by_username(cls, username):
'''
Returns the user with the given username.
username: String
Returns: User entity
'''
logging.error('DB QUERY User')
user = cls.all().ancestor(users_key()).filter("username = ",
username).get()
return user
@classmethod
def by_email(cls, email):
"""
Returns the user with the given email.
email: String (from EmailProperty)
"""
logging.error('DB QUERY User')
user = cls.all().ancestor(users_key()).filter("email = ",
email).get()
return user
@classmethod
def register(cls, username, email, fullname, pw, profile_picture):
'''
Hashes the given password (pw).
Constructs a User entity for the given information.
Returns the construted User entity.
name: String
pw: String
email: String (optional)
Returns: User entity
'''
pw_hash = make_pw_hash(username, pw)
return cls(parent = users_key(),
username = username,
email = email,
fullname = fullname,
pw_hash = pw_hash,
profile_picture = profile_picture,
date_created = timezone_now())
@classmethod
def valid_login(cls, username_or_email, pw):
'''
if user exits and if login is valid: Returns User
name: String
pw: String
Returns: User entity
'''
user = cls.get_user(username_or_email)
if user and valid_pw(user.username, pw, user.pw_hash):
return user
def set_user_caches(self):
set_cache(self.username, self)
set_cache(str(self.key().id()), self)
set_cache(self.email, self)
def reset_pw(self, pw):
self.pw_hash = make_pw_hash(self.username, pw)
def send_confirmation_mail(self):
confirmation_url = 'http://stl-workday.appspot.com/verify/%s' % (
self.key())
sender = 'Spacecom Workday <[email protected]>'
user_address = '%s <%s>' % (self.fullname, self.email)
subject = 'Hello %s! Confirm your email address please.' % (
self.username)
body = ('Hi %s!\n\n'
'Thank you for creating an account on Spacecom Workday!'
'\nPlease confirm your email address by clicking on the '
'following link:\n\n%s' % (self.fullname.split(' ')[0],
confirmation_url))
mail.send_mail(sender, user_address, subject, body)
def send_pw_reset_mail(self):
reset_url = 'http://stl-workday.appspot.com/reset/%s' % (self.key())
sender = 'Spacecom Workday <[email protected]>'
user_address = '%s <%s>' % (self.fullname, self.email)
subject = 'Hello %s! Your password reset link.' % self.username
body = ('Hi %s!\n\n'
'You can reset your password on the following link:\n\n%s' % (
self.fullname.split(' ')[0],
reset_url))
mail.send_mail(sender, user_address, subject, body)
def send_reset_success_mail(self):
sender = 'Spacecom Workday <[email protected]>'
user_address = '%s <%s>' % (self.fullname, self.email)
subject = 'Hello %s! Your password has been reset.' % self.username
body = ('Hi %s!\n\n'
'Your password has been reset successfully!' % (
self.fullname.split(' ')[0]))
mail.send_mail(sender, user_address, subject, body)
class DoneList(db.Model):
"""Datastore entity for the Done List."""
tasks = db.ListProperty(db.Text)
user_id = db.IntegerProperty()
tz_date = db.DateProperty()
company = db.StringProperty(default = DEFAULT_COMPANY)
@classmethod
def todays_done_list(cls, username):
done_list = cls.get_done_list(username,
date_to_date_key(timezone_now().date()))
return done_list
@classmethod
def get_done_list(cls, username, date_key):
"""
This decorator will first check the cache.
If not found in cache, call DB query and set the cache.
"""
done_list_key = username + '/' + date_key
done_list = memcache.get(done_list_key)
if not done_list:
done_list = cls.by_key(done_list_key)
if done_list:
done_list.set_done_list_cache()
return done_list
@classmethod
def by_key(cls, done_list_key):
logging.error('DB QUERY DoneList')
done_list = cls.get_by_key_name(done_list_key,
parent = User.get_user(done_list_key.split('/')[0]))
return done_list
@classmethod
def by_user_n_date(cls, user, tz_date):
"""
This decorator is a DB Query.
I wrote this decorator earlier and I'll probably never use it.
"""
logging.error('DB QUERY DoneList')
done_task = cls.all().ancestor(user)
done_task.filter("user_id = ", user.key().id())
done_task.filter("tz_date = ", tz_date)
return done_task.get()
@classmethod
def construct(cls, user, task):
"""Constructs the DoneList and returns it without putting it."""
tz_date = timezone_now().date()
return cls(parent = user,
key_name = done_list_key(user.username, tz_date),
tasks = [db.Text(task)],
user_id = user.key().id(),
tz_date = tz_date)
@classmethod
def prev_date_key(cls, user, date_key):
prev_key = memcache.get(user.username + '/before/' + date_key)
if not prev_key:
logging.error('DB Query prev list')
prev_done_list = cls.all().ancestor(user)
prev_done_list.filter("tz_date <",
date_key_to_date(date_key)).order("-tz_date")
prev_done_list = prev_done_list.get()
if prev_done_list:
prev_key = date_to_date_key(prev_done_list.tz_date)
set_cache(user.username + '/before/' + date_key, prev_key)
return prev_key
def update(self, task):
"""Updates itself with the given task without putting it to the db."""
self.tasks.append(db.Text(task))
return self
def set_done_list_cache(self):
set_cache(self.key().name(), self)
def del_task(self, task_index):
self.tasks.pop(task_index)
return self
def edit(self, task_index, task):
self.tasks[task_index] = db.Text(task)
return self
class TodoList(db.Model):
"""Datastore entity for the Todo List."""
content = db.ListProperty(db.Text, required = True)
user_id = db.IntegerProperty()
timestamps = db.ListProperty(datetime.datetime)
company = db.StringProperty(default = DEFAULT_COMPANY)
@classmethod
def get_todo_list(cls, user):
todo_list = memcache.get(user.username + '/todo')
if not todo_list:
todo_list = cls.by_user(user)
if todo_list:
todo_list.set_todo_list_cache()
return todo_list
@classmethod
def by_user(cls, user):
logging.error('DB Query Todo List')
todo_list = cls.all().ancestor(user).get()
return todo_list
@classmethod
def construct(cls, user, content):
"""Constructs the TodoList and returns it without putting it."""
timestamp = timezone_now()
return cls(parent = user,
key_name = user.username + '/todo',
content = [db.Text(content)],
user_id = user.key().id(),
timestamps = [timestamp])
def set_todo_list_cache(self):
set_cache(self.key().name(), self)
def render_content(self, version = -1):
"""
version: integer
"""
render_text = self.content[version].replace('\n', '<br>')
return render_text
def update(self, content):
timestamp = timezone_now()
self.content.append(db.Text(content))
self.timestamps.append(timestamp)
return self