-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmodel.py
More file actions
154 lines (118 loc) · 4.77 KB
/
model.py
File metadata and controls
154 lines (118 loc) · 4.77 KB
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
import datetime as dt
# Third-party imports
from google.appengine.api.app_identity import get_application_id
from google.appengine.ext import db
SESSION_LIFETIME = 1 # day
GLOBAL_PARENT = db.Key.from_path('app', get_application_id())
class SimpleProjection(object):
def __init__(self, entity):
self.entity = entity
def __getattr__(self, item):
return getattr(self.entity, item)
def __repr__(self):
return '[Projection of {}]'.format(repr(self.entity))
class BaseModel(db.Model):
def __getattr__(self, item):
if item == 'id':
return self.key().id()
raise AttributeError(item)
@classmethod
def by_prop(cls, prop_name, value, ancestor=GLOBAL_PARENT):
q = cls.all().ancestor(ancestor).filter('{} ='.format(prop_name), value)
return q.get()
class User(BaseModel):
name = db.StringProperty(required=True)
password_hash = db.StringProperty(required=True, indexed=False)
email = db.EmailProperty(required=False)
class Session(BaseModel):
sid = db.StringProperty(required=True)
user = db.ReferenceProperty(User, collection_name="Sessions")
created = db.DateTimeProperty(auto_now_add=True)
logout_url = db.StringProperty(default='/')
def has_expired(self):
delta = dt.datetime.now() - self.created
return delta.days > SESSION_LIFETIME
class Article(BaseModel):
# Reference class is not set, because Version class is defined lower in this
# file and interpreter would not see it.
# Both first_version and latest_version must actually be set to
# required=True, but this is not done due to technical limitations.
first_version = db.ReferenceProperty()
# Collection name won't be used; this is to suppress DuplicatePropertyError.
latest_version = db.ReferenceProperty(collection_name='_')
url = db.StringProperty(required=True)
def all_versions(self):
q = self.version_set
return q.order('-created')
def new_version(self, head, body):
version = Version(
article=self, head=head, body=body, parent=GLOBAL_PARENT)
version.put()
self.latest_version = version
self.put()
def project(self, version):
projection = SimpleProjection(self)
projection.url = self.url
projection.head = version.head
projection.body = version.body
projection.modified = version.created
projection.version = version
return projection
def get_latest_version(self):
return self.project(self.latest_version)
def version_by_id(self, version_id):
version = Version.get_by_id(int(version_id), parent=GLOBAL_PARENT)
if version is not None:
return self.project(version)
@classmethod
def by_url(cls, url, version=None, project_with_version=True):
article = cls.by_prop('url', url)
if article is not None:
if version is None:
if project_with_version:
return article.get_latest_version()
return article
else:
p = article.version_by_id(version)
return p if p is not None else article.get_latest_version()
@classmethod
@db.transactional
def new(cls, url, head, body):
article = cls(url=url, parent=GLOBAL_PARENT)
article.put()
first_version = Version(
article=article, head=head, body=body, parent=GLOBAL_PARENT)
first_version.put()
article.first_version = first_version
article.latest_version = first_version
article.put()
return article.project(first_version)
class Version(BaseModel):
article = db.ReferenceProperty(Article, required=True)
created = db.DateTimeProperty(auto_now_add=True)
head = db.StringProperty(required=True)
body = db.TextProperty()
@classmethod
def by_id(cls, version_id):
return cls.get_by_id(version_id, parent=GLOBAL_PARENT)
def belongs_to_article(self, article):
return self.article.key() == article.key()
def delete(self):
article = self.article
is_latest = self.is_latest()
is_first = self.is_first()
super(Version, self).delete()
if is_latest:
new_latest = article.version_set.ancestor(
GLOBAL_PARENT).order('-created').get()
self.article.latest_version = new_latest
self.article.put()
elif is_first:
new_first = article.version_set.ancestor(
GLOBAL_PARENT).order('created').get()
self.article.first_version = new_first
self.article.put()
def is_first(self):
return self.key() == self.article.first_version.key()
def is_latest(self):
return self.key() == self.article.latest_version.key()