Removing print statements from convert_gps_media_data migration
[mediagoblin.git] / mediagoblin / db / mongo / models.py
CommitLineData
8e1e744d 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
e5572c60
ML
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU Affero General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Affero General Public License for more details.
13#
14# You should have received a copy of the GNU Affero General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
25bdf9b6 17import datetime
4ad5af85 18
c2ddd85e 19from mongokit import Document
4329be14 20
25bdf9b6
AW
21from mediagoblin.db.mongo import migrations
22from mediagoblin.db.mongo.util import ASCENDING, DESCENDING, ObjectId
152a3bfa 23from mediagoblin.tools.pagination import Pagination
feba5c52 24from mediagoblin.db.mixin import UserMixin, MediaEntryMixin, MediaCommentMixin
d232e0f6 25
58f96a13
E
26
27class MongoPK(object):
28 """An alias for the _id primary key"""
29 def __get__(self, instance, cls):
30 return instance['_id']
31 def __set__(self, instance, val):
32 instance['_id'] = val
33 def __delete__(self, instance):
34 del instance['_id']
35
36
7bf3f5db
CAW
37###################
38# Custom validators
39###################
40
41########
42# Models
43########
44
45
25bdf9b6 46class User(Document, UserMixin):
16bcd1e7
CAW
47 """
48 A user of MediaGoblin.
49
50 Structure:
51 - username: The username of this user, should be unique to this instance.
52 - email: Email address of this user
53 - created: When the user was created
54 - plugin_data: a mapping of extra plugin information for this User.
55 Nothing uses this yet as we don't have plugins, but someday we
56 might... :)
57 - pw_hash: Hashed version of user's password.
58 - email_verified: Whether or not the user has verified their email or not.
59 Most parts of the site are disabled for users who haven't yet.
60 - status: whether or not the user is active, etc. Currently only has two
61 values, 'needs_email_verification' or 'active'. (In the future, maybe
62 we'll change this to a boolean with a key of 'active' and have a
63 separate field for a reason the user's been disabled if that's
64 appropriate... email_verified is already separate, after all.)
65 - verification_key: If the user is awaiting email verification, the user
66 will have to provide this key (which will be encoded in the presented
67 URL) in order to confirm their email as active.
68 - is_admin: Whether or not this user is an administrator or not.
69 - url: this user's personal webpage/website, if appropriate.
70 - bio: biography of this user (plaintext, in markdown)
16bcd1e7 71 """
73a6e206 72 __collection__ = 'users'
25bdf9b6 73 use_dot_notation = True
73a6e206 74
d232e0f6
CAW
75 structure = {
76 'username': unicode,
24181820 77 'email': unicode,
d232e0f6 78 'created': datetime.datetime,
25bdf9b6 79 'plugin_data': dict, # plugins can dump stuff here.
d232e0f6 80 'pw_hash': unicode,
24181820 81 'email_verified': bool,
4d75522b 82 'status': unicode,
18cf34d4
CAW
83 'verification_key': unicode,
84 'is_admin': bool,
25bdf9b6
AW
85 'url': unicode,
86 'bio': unicode, # May contain markdown
25bdf9b6
AW
87 'fp_verification_key': unicode, # forgotten password verification key
88 'fp_token_expire': datetime.datetime,
d232e0f6
CAW
89 }
90
db5912e3 91 required_fields = ['username', 'created', 'pw_hash', 'email']
fc9bb821
CAW
92
93 default_values = {
24181820 94 'created': datetime.datetime.utcnow,
4d75522b 95 'email_verified': False,
db1a438f 96 'status': u'needs_email_verification',
18cf34d4 97 'is_admin': False}
080a81ec 98
58f96a13
E
99 id = MongoPK()
100
d232e0f6 101
25bdf9b6 102class MediaEntry(Document, MediaEntryMixin):
080a81ec
CAW
103 """
104 Record of a piece of media.
105
106 Structure:
107 - uploader: A reference to a User who uploaded this.
108
109 - title: Title of this work
110
111 - slug: A normalized "slug" which can be used as part of a URL to retrieve
112 this work, such as 'my-works-name-in-slug-form' may be viewable by
113 'http://mg.example.org/u/username/m/my-works-name-in-slug-form/'
114 Note that since URLs are constructed this way, slugs must be unique
115 per-uploader. (An index is provided to enforce that but code should be
116 written on the python side to ensure this as well.)
117
118 - created: Date and time of when this piece of work was uploaded.
119
120 - description: Uploader-set description of this work. This can be marked
121 up with MarkDown for slight fanciness (links, boldness, italics,
122 paragraphs...)
123
080a81ec
CAW
124 - media_type: What type of media is this? Currently we only support
125 'image' ;)
126
127 - media_data: Extra information that's media-format-dependent.
128 For example, images might contain some EXIF data that's not appropriate
129 to other formats. You might store it like:
130
25bdf9b6 131 mediaentry.media_data['exif'] = {
080a81ec
CAW
132 'manufacturer': 'CASIO',
133 'model': 'QV-4000',
134 'exposure_time': .659}
135
136 Alternately for video you might store:
137
138 # play length in seconds
25bdf9b6 139 mediaentry.media_data['play_length'] = 340
080a81ec
CAW
140
141 ... so what's appropriate here really depends on the media type.
142
143 - plugin_data: a mapping of extra plugin information for this User.
144 Nothing uses this yet as we don't have plugins, but someday we
145 might... :)
146
147 - tags: A list of tags. Each tag is stored as a dictionary that has a key
148 for the actual name and the normalized name-as-slug, so ultimately this
149 looks like:
150 [{'name': 'Gully Gardens',
151 'slug': 'gully-gardens'},
152 {'name': 'Castle Adventure Time?!",
153 'slug': 'castle-adventure-time'}]
154
155 - state: What's the state of this file? Active, inactive, disabled, etc...
156 But really for now there are only two states:
157 "unprocessed": uploaded but needs to go through processing for display
158 "processed": processed and able to be displayed
159
a6c49d49
AW
160 - license: URI for media's license.
161
080a81ec
CAW
162 - queued_media_file: storage interface style filepath describing a file
163 queued for processing. This is stored in the mg_globals.queue_store
164 storage system.
165
6b9ee0ca
CAW
166 - queued_task_id: celery task id. Use this to fetch the task state.
167
080a81ec
CAW
168 - media_files: Files relevant to this that have actually been processed
169 and are available for various types of display. Stored like:
170 {'thumb': ['dir1', 'dir2', 'pic.png'}
171
172 - attachment_files: A list of "attachment" files, ones that aren't
173 critical to this piece of media but may be usefully relevant to people
174 viewing the work. (currently unused.)
6c50c210 175
25bdf9b6
AW
176 - fail_error: path to the exception raised
177 - fail_metadata:
080a81ec 178 """
4d75522b 179 __collection__ = 'media_entries'
25bdf9b6 180 use_dot_notation = True
4d75522b
CAW
181
182 structure = {
757f37a5 183 'uploader': ObjectId,
4d75522b 184 'title': unicode,
1013bdaf 185 'slug': unicode,
4d75522b 186 'created': datetime.datetime,
25bdf9b6 187 'description': unicode, # May contain markdown/up
4d75522b 188 'media_type': unicode,
25bdf9b6
AW
189 'media_data': dict, # extra data relevant to this media_type
190 'plugin_data': dict, # plugins can dump stuff here.
0712a06d 191 'tags': [dict],
74ae6b11 192 'state': unicode,
a6c49d49 193 'license': unicode,
74ae6b11 194
fa7f9c61
CAW
195 # For now let's assume there can only be one main file queued
196 # at a time
197 'queued_media_file': [unicode],
6b9ee0ca 198 'queued_task_id': unicode,
fa7f9c61
CAW
199
200 # A dictionary of logical names to filepaths
201 'media_files': dict,
202
74ae6b11
CAW
203 # The following should be lists of lists, in appropriate file
204 # record form
6c50c210
CAW
205 'attachment_files': list,
206
207 # If things go badly in processing things, we'll store that
208 # data here
209 'fail_error': unicode,
210 'fail_metadata': dict}
4d75522b
CAW
211
212 required_fields = [
b1ae76ae 213 'uploader', 'created', 'media_type', 'slug']
4d75522b
CAW
214
215 default_values = {
74ae6b11
CAW
216 'created': datetime.datetime.utcnow,
217 'state': u'unprocessed'}
4d75522b 218
58f96a13
E
219 id = MongoPK()
220
5ff57582
E
221 def media_data_init(self, **kwargs):
222 self.media_data.update(kwargs)
223
25bdf9b6
AW
224 def get_comments(self, ascending=False):
225 if ascending:
226 order = ASCENDING
227 else:
228 order = DESCENDING
229
6f59a3a3 230 return self.db.MediaComment.find({
25bdf9b6 231 'media_entry': self._id}).sort('created', order)
6f59a3a3 232
9c0fe63f
CFD
233 def url_to_prev(self, urlgen):
234 """
235 Provide a url to the previous entry from this user, if there is one
236 """
25bdf9b6
AW
237 cursor = self.db.MediaEntry.find({'_id': {"$gt": self._id},
238 'uploader': self.uploader,
ce2ac488 239 'state': 'processed'}).sort(
77b95801 240 '_id', ASCENDING).limit(1)
25bdf9b6
AW
241 for media in cursor:
242 return media.url_for_self(urlgen)
080a81ec 243
9c0fe63f
CFD
244 def url_to_next(self, urlgen):
245 """
246 Provide a url to the next entry from this user, if there is one
247 """
25bdf9b6
AW
248 cursor = self.db.MediaEntry.find({'_id': {"$lt": self._id},
249 'uploader': self.uploader,
ce2ac488 250 'state': 'processed'}).sort(
77b95801 251 '_id', DESCENDING).limit(1)
9c0fe63f 252
25bdf9b6
AW
253 for media in cursor:
254 return media.url_for_self(urlgen)
6ee9c719 255
25bdf9b6
AW
256 @property
257 def get_uploader(self):
258 return self.db.User.find_one({'_id': self.uploader})
25b48323 259
b27ec167 260
feba5c52 261class MediaComment(Document, MediaCommentMixin):
e83dc091
CAW
262 """
263 A comment on a MediaEntry.
264
265 Structure:
266 - media_entry: The media entry this comment is attached to
267 - author: user who posted this comment
268 - created: when the comment was created
269 - content: plaintext (but markdown'able) version of the comment's content.
e83dc091
CAW
270 """
271
c11f21ab 272 __collection__ = 'media_comments'
25bdf9b6 273 use_dot_notation = True
6926b23d 274
c11f21ab
JW
275 structure = {
276 'media_entry': ObjectId,
277 'author': ObjectId,
278 'created': datetime.datetime,
279 'content': unicode,
feba5c52 280 }
c11f21ab
JW
281
282 required_fields = [
7bd8197f 283 'media_entry', 'author', 'created', 'content']
c11f21ab
JW
284
285 default_values = {
286 'created': datetime.datetime.utcnow}
287
288 def media_entry(self):
7bd8197f 289 return self.db.MediaEntry.find_one({'_id': self['media_entry']})
c11f21ab 290
25bdf9b6
AW
291 @property
292 def get_author(self):
c11f21ab 293 return self.db.User.find_one({'_id': self['author']})
6926b23d 294
c2ddd85e 295
c11f21ab
JW
296REGISTER_MODELS = [
297 MediaEntry,
298 User,
299 MediaComment]
d232e0f6 300
4329be14 301
d232e0f6
CAW
302def register_models(connection):
303 """
304 Register all models in REGISTER_MODELS with this connection.
305 """
db61f7d1 306 connection.register(REGISTER_MODELS)