Commit | Line | Data |
---|---|---|
70b44584 | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
b781c3c9 | 2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. |
70b44584 CAW |
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 | ||
29fdd3bb AW |
17 | import datetime |
18 | ||
88a9662b | 19 | from sqlalchemy import (MetaData, Table, Column, Boolean, SmallInteger, |
316e1dfd | 20 | Integer, Unicode, UnicodeText, DateTime, |
0f14c362 E |
21 | ForeignKey) |
22 | from sqlalchemy.exc import ProgrammingError | |
316e1dfd | 23 | from sqlalchemy.ext.declarative import declarative_base |
0f14c362 | 24 | from migrate.changeset.constraint import UniqueConstraint |
b781c3c9 | 25 | |
c130e3ee | 26 | from mediagoblin.db.migration_tools import RegisterMigration, inspect_table |
b0c8328e | 27 | from mediagoblin.db.models import MediaEntry, Collection, User |
b781c3c9 | 28 | |
3ea1cf36 | 29 | MIGRATIONS = {} |
b781c3c9 JK |
30 | |
31 | ||
32 | @RegisterMigration(1, MIGRATIONS) | |
33 | def ogg_to_webm_audio(db_conn): | |
34 | metadata = MetaData(bind=db_conn.bind) | |
35 | ||
36 | file_keynames = Table('core__file_keynames', metadata, autoload=True, | |
37 | autoload_with=db_conn.bind) | |
38 | ||
39 | db_conn.execute( | |
38c6d441 | 40 | file_keynames.update().where(file_keynames.c.name == 'ogg'). |
b781c3c9 JK |
41 | values(name='webm_audio') |
42 | ) | |
b1055401 | 43 | db_conn.commit() |
38c6d441 JW |
44 | |
45 | ||
46 | @RegisterMigration(2, MIGRATIONS) | |
47 | def add_wants_notification_column(db_conn): | |
48 | metadata = MetaData(bind=db_conn.bind) | |
49 | ||
50 | users = Table('core__users', metadata, autoload=True, | |
51 | autoload_with=db_conn.bind) | |
52 | ||
53 | col = Column('wants_comment_notification', Boolean, | |
c4869eff | 54 | default=True, nullable=True) |
38c6d441 | 55 | col.create(users, populate_defaults=True) |
b1055401 | 56 | db_conn.commit() |
64712915 JW |
57 | |
58 | ||
59 | @RegisterMigration(3, MIGRATIONS) | |
60 | def add_transcoding_progress(db_conn): | |
61 | metadata = MetaData(bind=db_conn.bind) | |
62 | ||
c4466cb4 | 63 | media_entry = inspect_table(metadata, 'core__media_entries') |
64712915 JW |
64 | |
65 | col = Column('transcoding_progress', SmallInteger) | |
66 | col.create(media_entry) | |
67 | db_conn.commit() | |
be5be115 | 68 | |
88a9662b | 69 | |
316e1dfd E |
70 | class Collection_v0(declarative_base()): |
71 | __tablename__ = "core__collections" | |
72 | ||
73 | id = Column(Integer, primary_key=True) | |
74 | title = Column(Unicode, nullable=False) | |
75 | slug = Column(Unicode) | |
76 | created = Column(DateTime, nullable=False, default=datetime.datetime.now, | |
77 | index=True) | |
78 | description = Column(UnicodeText) | |
79 | creator = Column(Integer, ForeignKey(User.id), nullable=False) | |
80 | items = Column(Integer, default=0) | |
81 | ||
82 | class CollectionItem_v0(declarative_base()): | |
83 | __tablename__ = "core__collection_items" | |
84 | ||
85 | id = Column(Integer, primary_key=True) | |
86 | media_entry = Column( | |
87 | Integer, ForeignKey(MediaEntry.id), nullable=False, index=True) | |
88 | collection = Column(Integer, ForeignKey(Collection.id), nullable=False) | |
89 | note = Column(UnicodeText, nullable=True) | |
90 | added = Column(DateTime, nullable=False, default=datetime.datetime.now) | |
91 | position = Column(Integer) | |
92 | ||
93 | ## This should be activated, normally. | |
94 | ## But this would change the way the next migration used to work. | |
95 | ## So it's commented for now. | |
0f14c362 E |
96 | __table_args__ = ( |
97 | UniqueConstraint('collection', 'media_entry'), | |
98 | {}) | |
99 | ||
100 | collectionitem_unique_constraint_done = False | |
316e1dfd | 101 | |
be5be115 | 102 | @RegisterMigration(4, MIGRATIONS) |
29fdd3bb | 103 | def add_collection_tables(db_conn): |
316e1dfd E |
104 | Collection_v0.__table__.create(db_conn.bind) |
105 | CollectionItem_v0.__table__.create(db_conn.bind) | |
29fdd3bb | 106 | |
0f14c362 E |
107 | global collectionitem_unique_constraint_done |
108 | collectionitem_unique_constraint_done = True | |
109 | ||
29fdd3bb AW |
110 | db_conn.commit() |
111 | ||
88a9662b | 112 | |
29fdd3bb | 113 | @RegisterMigration(5, MIGRATIONS) |
59fb87c9 | 114 | def add_mediaentry_collected(db_conn): |
be5be115 AW |
115 | metadata = MetaData(bind=db_conn.bind) |
116 | ||
c4466cb4 | 117 | media_entry = inspect_table(metadata, 'core__media_entries') |
be5be115 | 118 | |
d8984df8 | 119 | col = Column('collected', Integer, default=0) |
be5be115 AW |
120 | col.create(media_entry) |
121 | db_conn.commit() | |
5354f954 JW |
122 | |
123 | ||
316e1dfd E |
124 | class ProcessingMetaData_v0(declarative_base()): |
125 | __tablename__ = 'core__processing_metadata' | |
939d57a0 | 126 | |
316e1dfd E |
127 | id = Column(Integer, primary_key=True) |
128 | media_entry_id = Column(Integer, ForeignKey(MediaEntry.id), nullable=False, | |
129 | index=True) | |
130 | callback_url = Column(Unicode) | |
939d57a0 | 131 | |
316e1dfd E |
132 | @RegisterMigration(6, MIGRATIONS) |
133 | def create_processing_metadata_table(db): | |
134 | ProcessingMetaData_v0.__table__.create(db.bind) | |
5354f954 | 135 | db.commit() |
0f14c362 | 136 | |
ea91c183 E |
137 | |
138 | # Okay, problem being: | |
139 | # Migration #4 forgot to add the uniqueconstraint for the | |
140 | # new tables. While creating the tables from scratch had | |
141 | # the constraint enabled. | |
142 | # | |
a64abbb1 | 143 | # So we have four situations that should end up at the same |
ea91c183 E |
144 | # db layout: |
145 | # | |
146 | # 1. Fresh install. | |
147 | # Well, easy. Just uses the tables in models.py | |
148 | # 2. Fresh install using a git version just before this migration | |
149 | # The tables are all there, the unique constraint is also there. | |
150 | # This migration should do nothing. | |
151 | # But as we can't detect the uniqueconstraint easily, | |
152 | # this migration just adds the constraint again. | |
153 | # And possibly fails very loud. But ignores the failure. | |
154 | # 3. old install, not using git, just releases. | |
155 | # This one will get the new tables in #4 (now with constraint!) | |
156 | # And this migration is just skipped silently. | |
157 | # 4. old install, always on latest git. | |
158 | # This one has the tables, but lacks the constraint. | |
a64abbb1 | 159 | # So this migration adds the constraint. |
0f14c362 E |
160 | @RegisterMigration(7, MIGRATIONS) |
161 | def fix_CollectionItem_v0_constraint(db_conn): | |
162 | """Add the forgotten Constraint on CollectionItem""" | |
163 | ||
164 | global collectionitem_unique_constraint_done | |
165 | if collectionitem_unique_constraint_done: | |
0f14c362 E |
166 | # Reset it. Maybe the whole thing gets run again |
167 | # For a different db? | |
168 | collectionitem_unique_constraint_done = False | |
169 | return | |
170 | ||
171 | metadata = MetaData(bind=db_conn.bind) | |
172 | ||
c4466cb4 | 173 | CollectionItem_table = inspect_table(metadata, 'core__collection_items') |
0f14c362 E |
174 | |
175 | constraint = UniqueConstraint('collection', 'media_entry', | |
176 | name='core__collection_items_collection_media_entry_key', | |
177 | table=CollectionItem_table) | |
178 | ||
179 | try: | |
180 | constraint.create() | |
78fd5581 CAW |
181 | except ProgrammingError: |
182 | # User probably has an install that was run since the | |
183 | # collection tables were added, so we don't need to run this migration. | |
184 | pass | |
185 | ||
0f14c362 | 186 | db_conn.commit() |
dc4dfbde MH |
187 | |
188 | ||
189 | @RegisterMigration(8, MIGRATIONS) | |
190 | def add_license_preference(db): | |
191 | metadata = MetaData(bind=db.bind) | |
192 | ||
0c871f81 | 193 | user_table = inspect_table(metadata, 'core__users') |
dc4dfbde | 194 | |
0c871f81 | 195 | col = Column('license_preference', Unicode) |
dc4dfbde MH |
196 | col.create(user_table) |
197 | db.commit() | |
e66431f4 CAW |
198 | |
199 | ||
200 | @RegisterMigration(9, MIGRATIONS) | |
201 | def mediaentry_new_slug_era(db): | |
202 | """ | |
203 | Update for the new era for media type slugs. | |
204 | ||
205 | Entries without slugs now display differently in the url like: | |
206 | /u/cwebber/m/id=251/ | |
207 | ||
208 | ... because of this, we should back-convert: | |
209 | - entries without slugs should be converted to use the id, if possible, to | |
210 | make old urls still work | |
211 | - slugs with = (or also : which is now also not allowed) to have those | |
212 | stripped out (small possibility of breakage here sadly) | |
213 | """ | |
214 | import uuid | |
215 | ||
216 | def slug_and_user_combo_exists(slug, uploader): | |
217 | # Technically returns the number of entries with this slug and user | |
218 | # that already exist | |
219 | return db.execute( | |
220 | media_table.select( | |
221 | media_table.c.uploader==uploader, | |
0c6a34bf | 222 | media_table.c.slug==slug).count()).first().tbl_row_count |
e66431f4 CAW |
223 | |
224 | def append_garbage_till_unique(row, new_slug): | |
225 | """ | |
226 | Attach junk to this row until it's unique, then save it | |
227 | """ | |
228 | if slug_and_user_combo_exists(new_slug, row.uploader): | |
229 | # okay, still no success; | |
230 | # let's whack junk on there till it's unique. | |
231 | new_slug += '-' + uuid.uuid4().hex[:4] | |
232 | # keep going if necessary! | |
233 | while slug_and_user_combo_exists(new_slug, row.uploader): | |
234 | new_slug += uuid.uuid4().hex[:4] | |
235 | ||
236 | db.execute( | |
237 | media_table.update(). \ | |
238 | where(media_table.c.id==row.id). \ | |
239 | values(slug=new_slug)) | |
240 | ||
241 | metadata = MetaData(bind=db.bind) | |
242 | ||
243 | media_table = inspect_table(metadata, 'core__media_entries') | |
244 | for row in db.execute(media_table.select()): | |
245 | # no slug, try setting to an id | |
246 | if not row.slug: | |
247 | append_garbage_till_unique(row, unicode(row.id)) | |
248 | # has "=" or ":" in it... we're getting rid of those | |
249 | elif u"=" in row.slug or u":" in row.slug: | |
250 | append_garbage_till_unique( | |
251 | row, row.slug.replace(u"=", u"-").replace(u":", u"-")) |