1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011,2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
20 from sqlalchemy
import (
21 Column
, Integer
, Unicode
, UnicodeText
, DateTime
, Boolean
, ForeignKey
,
23 from sqlalchemy
.orm
import relationship
24 from sqlalchemy
.orm
.collections
import attribute_mapped_collection
25 from sqlalchemy
.sql
.expression
import desc
26 from sqlalchemy
.ext
.associationproxy
import association_proxy
28 from mediagoblin
.db
.sql
.extratypes
import PathTupleWithSlashes
29 from mediagoblin
.db
.sql
.base
import Base
30 from mediagoblin
.db
.mixin
import UserMixin
, MediaEntryMixin
33 class SimpleFieldAlias(object):
34 """An alias for any field"""
35 def __init__(self
, fieldname
):
36 self
.fieldname
= fieldname
38 def __get__(self
, instance
, cls
):
39 return getattr(instance
, self
.fieldname
)
41 def __set__(self
, instance
, val
):
42 setattr(instance
, self
.fieldname
, val
)
45 class User(Base
, UserMixin
):
46 __tablename__
= "users"
48 id = Column(Integer
, primary_key
=True)
49 username
= Column(Unicode
, nullable
=False, unique
=True)
50 email
= Column(Unicode
, nullable
=False)
51 created
= Column(DateTime
, nullable
=False, default
=datetime
.datetime
.now
)
52 pw_hash
= Column(Unicode
, nullable
=False)
53 email_verified
= Column(Boolean
)
54 status
= Column(Unicode
, default
=u
"needs_email_verification", nullable
=False)
55 verification_key
= Column(Unicode
)
56 is_admin
= Column(Boolean
, default
=False, nullable
=False)
58 bio
= Column(UnicodeText
) # ??
59 bio_html
= Column(UnicodeText
) # ??
60 fp_verification_key
= Column(Unicode
)
61 fp_token_expire
= Column(DateTime
)
64 # plugin data would be in a separate model
66 _id
= SimpleFieldAlias("id")
69 class MediaEntry(Base
, MediaEntryMixin
):
70 __tablename__
= "media_entries"
72 id = Column(Integer
, primary_key
=True)
73 uploader
= Column(Integer
, ForeignKey('users.id'), nullable
=False)
74 title
= Column(Unicode
, nullable
=False)
75 slug
= Column(Unicode
)
76 created
= Column(DateTime
, nullable
=False, default
=datetime
.datetime
.now
)
77 description
= Column(UnicodeText
) # ??
78 description_html
= Column(UnicodeText
) # ??
79 media_type
= Column(Unicode
, nullable
=False)
80 state
= Column(Unicode
, nullable
=False) # or use sqlalchemy.types.Enum?
81 license
= Column(Unicode
)
83 fail_error
= Column(Unicode
)
84 fail_metadata
= Column(UnicodeText
)
86 queued_media_file
= Column(PathTupleWithSlashes
)
88 queued_task_id
= Column(Unicode
)
91 UniqueConstraint('uploader', 'slug'),
94 get_uploader
= relationship(User
)
96 media_files_helper
= relationship("MediaFile",
97 collection_class
=attribute_mapped_collection("name"),
98 cascade
="all, delete-orphan"
100 media_files
= association_proxy('media_files_helper', 'file_path',
101 creator
=lambda k
, v
: MediaFile(name
=k
, file_path
=v
)
109 def get_comments(self
, ascending
=False):
110 order_col
= MediaComment
.created
112 order_col
= desc(order_col
)
113 return MediaComment
.query
.filter_by(
114 media_entry
=self
.id).order_by(order_col
)
116 def url_to_prev(self
, urlgen
):
117 """get the next 'newer' entry by this user"""
118 media
= MediaEntry
.query
.filter(
119 (MediaEntry
.uploader
== self
.uploader
)
120 & (MediaEntry
.state
== 'processed')
121 & (MediaEntry
.id > self
.id)).order_by(MediaEntry
.id).first()
123 if media
is not None:
124 return media
.url_for_self(urlgen
)
126 def url_to_next(self
, urlgen
):
127 """get the next 'older' entry by this user"""
128 media
= MediaEntry
.query
.filter(
129 (MediaEntry
.uploader
== self
.uploader
)
130 & (MediaEntry
.state
== 'processed')
131 & (MediaEntry
.id < self
.id)).order_by(desc(MediaEntry
.id)).first()
133 if media
is not None:
134 return media
.url_for_self(urlgen
)
137 class MediaFile(Base
):
138 __tablename__
= "mediafiles"
140 media_entry
= Column(
141 Integer
, ForeignKey(MediaEntry
.id),
142 nullable
=False, primary_key
=True)
143 name
= Column(Unicode
, primary_key
=True)
144 file_path
= Column(PathTupleWithSlashes
)
147 return "<MediaFile %s: %r>" % (self
.name
, self
.file_path
)
151 __tablename__
= "tags"
153 id = Column(Integer
, primary_key
=True)
154 slug
= Column(Unicode
, nullable
=False, unique
=True)
157 class MediaTag(Base
):
158 __tablename__
= "media_tags"
160 id = Column(Integer
, primary_key
=True)
161 tag
= Column(Integer
, ForeignKey('tags.id'), nullable
=False)
162 name
= Column(Unicode
)
163 media_entry
= Column(
164 Integer
, ForeignKey('media_entries.id'),
166 # created = Column(DateTime, nullable=False, default=datetime.datetime.now)
169 UniqueConstraint('tag', 'media_entry'),
173 class MediaComment(Base
):
174 __tablename__
= "media_comments"
176 id = Column(Integer
, primary_key
=True)
177 media_entry
= Column(
178 Integer
, ForeignKey('media_entries.id'), nullable
=False)
179 author
= Column(Integer
, ForeignKey('users.id'), nullable
=False)
180 created
= Column(DateTime
, nullable
=False, default
=datetime
.datetime
.now
)
181 content
= Column(UnicodeText
, nullable
=False)
182 content_html
= Column(UnicodeText
)
184 get_author
= relationship(User
)
187 def show_table_init():
188 from sqlalchemy
import create_engine
189 engine
= create_engine('sqlite:///:memory:', echo
=True)
191 Base
.metadata
.create_all(engine
)
194 if __name__
== '__main__':