Commit | Line | Data |
---|---|---|
0f10058f | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
89694d6d | 2 | # Copyright (C) 2012, 2012 MediaGoblin contributors. See AUTHORS. |
0f10058f 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 | ||
17 | import copy | |
18 | ||
19 | from sqlalchemy import ( | |
5de0f4da | 20 | Table, Column, MetaData, Index, |
89694d6d | 21 | Integer, Float, Unicode, UnicodeText, DateTime, Boolean, |
c7fa585b | 22 | ForeignKey, UniqueConstraint, PickleType, VARCHAR) |
d74a9483 | 23 | from sqlalchemy.orm import sessionmaker, relationship |
0f10058f | 24 | from sqlalchemy.ext.declarative import declarative_base |
89694d6d | 25 | from sqlalchemy.sql import select, insert |
0f10058f CAW |
26 | from migrate import changeset |
27 | ||
39dc3bf8 | 28 | from mediagoblin.db.base import GMGTableBase |
c130e3ee | 29 | from mediagoblin.db.migration_tools import MigrationManager, RegisterMigration |
35a24fc2 | 30 | from mediagoblin.tools.common import CollectingPrinter |
0f10058f CAW |
31 | |
32 | ||
33 | # This one will get filled with local migrations | |
34 | FULL_MIGRATIONS = {} | |
35 | ||
36 | ||
37 | ####################################################### | |
38 | # Migration set 1: Define initial models, no migrations | |
39 | ####################################################### | |
40 | ||
41 | Base1 = declarative_base(cls=GMGTableBase) | |
42 | ||
43 | class Creature1(Base1): | |
44 | __tablename__ = "creature" | |
45 | ||
46 | id = Column(Integer, primary_key=True) | |
47 | name = Column(Unicode, unique=True, nullable=False, index=True) | |
48 | num_legs = Column(Integer, nullable=False) | |
49 | is_demon = Column(Boolean) | |
50 | ||
51 | class Level1(Base1): | |
52 | __tablename__ = "level" | |
53 | ||
64d28064 | 54 | id = Column(Unicode, primary_key=True) |
caed154a | 55 | name = Column(Unicode) |
40f0996a | 56 | description = Column(Unicode) |
0f10058f CAW |
57 | exits = Column(PickleType) |
58 | ||
59 | SET1_MODELS = [Creature1, Level1] | |
60 | ||
63c3ca28 | 61 | SET1_FOUNDATIONS = {Creature1:[{'name':u'goblin','num_legs':2,'is_demon':False}, |
62 | {'name':u'cerberus','num_legs':4,'is_demon':True}]} | |
63 | ||
a5e03db6 | 64 | SET1_MIGRATIONS = {} |
0f10058f CAW |
65 | |
66 | ####################################################### | |
67 | # Migration set 2: A few migrations and new model | |
68 | ####################################################### | |
69 | ||
70 | Base2 = declarative_base(cls=GMGTableBase) | |
71 | ||
72 | class Creature2(Base2): | |
73 | __tablename__ = "creature" | |
74 | ||
75 | id = Column(Integer, primary_key=True) | |
76 | name = Column(Unicode, unique=True, nullable=False, index=True) | |
77 | num_legs = Column(Integer, nullable=False) | |
d74a9483 | 78 | magical_powers = relationship("CreaturePower2") |
0f10058f CAW |
79 | |
80 | class CreaturePower2(Base2): | |
81 | __tablename__ = "creature_power" | |
82 | ||
83 | id = Column(Integer, primary_key=True) | |
84 | creature = Column( | |
85 | Integer, ForeignKey('creature.id'), nullable=False) | |
86 | name = Column(Unicode) | |
87 | description = Column(Unicode) | |
89694d6d | 88 | hitpower = Column(Integer, nullable=False) |
0f10058f CAW |
89 | |
90 | class Level2(Base2): | |
91 | __tablename__ = "level" | |
92 | ||
64d28064 | 93 | id = Column(Unicode, primary_key=True) |
0f10058f | 94 | name = Column(Unicode) |
40f0996a | 95 | description = Column(Unicode) |
0f10058f CAW |
96 | |
97 | class LevelExit2(Base2): | |
98 | __tablename__ = "level_exit" | |
99 | ||
100 | id = Column(Integer, primary_key=True) | |
101 | name = Column(Unicode) | |
102 | from_level = Column( | |
64d28064 | 103 | Unicode, ForeignKey('level.id'), nullable=False) |
0f10058f | 104 | to_level = Column( |
64d28064 | 105 | Unicode, ForeignKey('level.id'), nullable=False) |
0f10058f CAW |
106 | |
107 | SET2_MODELS = [Creature2, CreaturePower2, Level2, LevelExit2] | |
108 | ||
109 | ||
110 | @RegisterMigration(1, FULL_MIGRATIONS) | |
111 | def creature_remove_is_demon(db_conn): | |
356654de CAW |
112 | """ |
113 | Remove the is_demon field from the creature model. We don't need | |
114 | it! | |
115 | """ | |
7f3ec607 CAW |
116 | # :( Commented out 'cuz of: |
117 | # http://code.google.com/p/sqlalchemy-migrate/issues/detail?id=143&thanks=143&ts=1327882242 | |
118 | ||
78d17b80 CAW |
119 | # metadata = MetaData(bind=db_conn.bind) |
120 | # creature_table = Table( | |
121 | # 'creature', metadata, | |
122 | # autoload=True, autoload_with=db_conn.bind) | |
123 | # creature_table.drop_column('is_demon') | |
124 | pass | |
125 | ||
0f10058f CAW |
126 | |
127 | @RegisterMigration(2, FULL_MIGRATIONS) | |
128 | def creature_powers_new_table(db_conn): | |
356654de CAW |
129 | """ |
130 | Add a new table for creature powers. Nothing needs to go in it | |
131 | yet though as there wasn't anything that previously held this | |
132 | information | |
133 | """ | |
e920b968 | 134 | metadata = MetaData(bind=db_conn.bind) |
78d17b80 CAW |
135 | |
136 | # We have to access the creature table so sqlalchemy can make the | |
137 | # foreign key relationship | |
138 | creature_table = Table( | |
139 | 'creature', metadata, | |
140 | autoload=True, autoload_with=db_conn.bind) | |
141 | ||
89694d6d CAW |
142 | creature_powers = Table( |
143 | 'creature_power', metadata, | |
144 | Column('id', Integer, primary_key=True), | |
145 | Column('creature', | |
146 | Integer, ForeignKey('creature.id'), nullable=False), | |
147 | Column('name', Unicode), | |
148 | Column('description', Unicode), | |
149 | Column('hitpower', Integer, nullable=False)) | |
e920b968 | 150 | metadata.create_all(db_conn.bind) |
89694d6d | 151 | |
0f10058f CAW |
152 | |
153 | @RegisterMigration(3, FULL_MIGRATIONS) | |
154 | def level_exits_new_table(db_conn): | |
356654de CAW |
155 | """ |
156 | Make a new table for level exits and move the previously pickled | |
157 | stuff over to here (then drop the old unneeded table) | |
158 | """ | |
89694d6d CAW |
159 | # First, create the table |
160 | # ----------------------- | |
e920b968 | 161 | metadata = MetaData(bind=db_conn.bind) |
78d17b80 CAW |
162 | |
163 | # Minimal representation of level table. | |
164 | # Not auto-introspecting here because of pickle table. I'm not | |
165 | # sure sqlalchemy can auto-introspect pickle columns. | |
166 | levels = Table( | |
167 | 'level', metadata, | |
168 | Column('id', Unicode, primary_key=True), | |
169 | Column('name', Unicode), | |
170 | Column('description', Unicode), | |
171 | Column('exits', PickleType)) | |
172 | ||
89694d6d CAW |
173 | level_exits = Table( |
174 | 'level_exit', metadata, | |
175 | Column('id', Integer, primary_key=True), | |
176 | Column('name', Unicode), | |
177 | Column('from_level', | |
78d17b80 | 178 | Unicode, ForeignKey('level.id'), nullable=False), |
89694d6d | 179 | Column('to_level', |
78d17b80 | 180 | Unicode, ForeignKey('level.id'), nullable=False)) |
e920b968 | 181 | metadata.create_all(db_conn.bind) |
89694d6d CAW |
182 | |
183 | # And now, convert all the old exit pickles to new level exits | |
184 | # ------------------------------------------------------------ | |
185 | ||
89694d6d CAW |
186 | # query over and insert |
187 | result = db_conn.execute( | |
188 | select([levels], levels.c.exits!=None)) | |
189 | ||
190 | for level in result: | |
78d17b80 CAW |
191 | |
192 | for exit_name, to_level in level['exits'].iteritems(): | |
193 | # Insert the level exit | |
194 | db_conn.execute( | |
195 | level_exits.insert().values( | |
196 | name=exit_name, | |
197 | from_level=level.id, | |
198 | to_level=to_level)) | |
89694d6d CAW |
199 | |
200 | # Finally, drop the old level exits pickle table | |
201 | # ---------------------------------------------- | |
473e0605 | 202 | levels.drop_column('exits') |
0f10058f CAW |
203 | |
204 | ||
205 | # A hack! At this point we freeze-fame and get just a partial list of | |
206 | # migrations | |
207 | ||
129c36be | 208 | SET2_MIGRATIONS = copy.copy(FULL_MIGRATIONS) |
0f10058f CAW |
209 | |
210 | ####################################################### | |
211 | # Migration set 3: Final migrations | |
212 | ####################################################### | |
213 | ||
214 | Base3 = declarative_base(cls=GMGTableBase) | |
215 | ||
216 | class Creature3(Base3): | |
217 | __tablename__ = "creature" | |
218 | ||
219 | id = Column(Integer, primary_key=True) | |
220 | name = Column(Unicode, unique=True, nullable=False, index=True) | |
221 | num_limbs= Column(Integer, nullable=False) | |
0f3526c6 | 222 | magical_powers = relationship("CreaturePower3") |
0f10058f CAW |
223 | |
224 | class CreaturePower3(Base3): | |
225 | __tablename__ = "creature_power" | |
226 | ||
227 | id = Column(Integer, primary_key=True) | |
228 | creature = Column( | |
229 | Integer, ForeignKey('creature.id'), nullable=False, index=True) | |
230 | name = Column(Unicode) | |
231 | description = Column(Unicode) | |
89694d6d | 232 | hitpower = Column(Float, nullable=False) |
0f10058f CAW |
233 | |
234 | class Level3(Base3): | |
235 | __tablename__ = "level" | |
236 | ||
64d28064 | 237 | id = Column(Unicode, primary_key=True) |
0f10058f | 238 | name = Column(Unicode) |
40f0996a | 239 | description = Column(Unicode) |
0f10058f CAW |
240 | |
241 | class LevelExit3(Base3): | |
242 | __tablename__ = "level_exit" | |
243 | ||
244 | id = Column(Integer, primary_key=True) | |
245 | name = Column(Unicode) | |
246 | from_level = Column( | |
64d28064 | 247 | Unicode, ForeignKey('level.id'), nullable=False, index=True) |
0f10058f | 248 | to_level = Column( |
64d28064 | 249 | Unicode, ForeignKey('level.id'), nullable=False, index=True) |
0f10058f CAW |
250 | |
251 | ||
252 | SET3_MODELS = [Creature3, CreaturePower3, Level3, LevelExit3] | |
caed154a | 253 | SET3_MIGRATIONS = FULL_MIGRATIONS |
0f10058f CAW |
254 | |
255 | ||
256 | @RegisterMigration(4, FULL_MIGRATIONS) | |
257 | def creature_num_legs_to_num_limbs(db_conn): | |
d6cdf64b CAW |
258 | """ |
259 | Turns out we're tracking all sorts of limbs, not "legs" | |
260 | specifically. Humans would be 4 here, for instance. So we | |
261 | renamed the column. | |
262 | """ | |
e920b968 | 263 | metadata = MetaData(bind=db_conn.bind) |
248b5061 CAW |
264 | creature_table = Table( |
265 | 'creature', metadata, | |
e920b968 | 266 | autoload=True, autoload_with=db_conn.bind) |
94eff10d | 267 | creature_table.c.num_legs.alter(name=u"num_limbs") |
248b5061 | 268 | |
0f10058f CAW |
269 | |
270 | @RegisterMigration(5, FULL_MIGRATIONS) | |
271 | def level_exit_index_from_and_to_level(db_conn): | |
d6cdf64b CAW |
272 | """ |
273 | Index the from and to levels of the level exit table. | |
274 | """ | |
e920b968 | 275 | metadata = MetaData(bind=db_conn.bind) |
248b5061 CAW |
276 | level_exit = Table( |
277 | 'level_exit', metadata, | |
e920b968 | 278 | autoload=True, autoload_with=db_conn.bind) |
78d17b80 CAW |
279 | Index('ix_level_exit_from_level', |
280 | level_exit.c.from_level).create(db_conn.bind) | |
281 | Index('ix_level_exit_to_level', | |
282 | level_exit.c.to_level).create(db_conn.bind) | |
248b5061 | 283 | |
0f10058f CAW |
284 | |
285 | @RegisterMigration(6, FULL_MIGRATIONS) | |
286 | def creature_power_index_creature(db_conn): | |
d6cdf64b CAW |
287 | """ |
288 | Index our foreign key relationship to the creatures | |
289 | """ | |
e920b968 | 290 | metadata = MetaData(bind=db_conn.bind) |
248b5061 CAW |
291 | creature_power = Table( |
292 | 'creature_power', metadata, | |
e920b968 | 293 | autoload=True, autoload_with=db_conn.bind) |
78d17b80 CAW |
294 | Index('ix_creature_power_creature', |
295 | creature_power.c.creature).create(db_conn.bind) | |
248b5061 | 296 | |
89694d6d CAW |
297 | |
298 | @RegisterMigration(7, FULL_MIGRATIONS) | |
299 | def creature_power_hitpower_to_float(db_conn): | |
d6cdf64b CAW |
300 | """ |
301 | Convert hitpower column on creature power table from integer to | |
302 | float. | |
303 | ||
304 | Turns out we want super precise values of how much hitpower there | |
305 | really is. | |
306 | """ | |
e920b968 | 307 | metadata = MetaData(bind=db_conn.bind) |
78d17b80 CAW |
308 | |
309 | # We have to access the creature table so sqlalchemy can make the | |
310 | # foreign key relationship | |
311 | creature_table = Table( | |
312 | 'creature', metadata, | |
313 | autoload=True, autoload_with=db_conn.bind) | |
314 | ||
248b5061 CAW |
315 | creature_power = Table( |
316 | 'creature_power', metadata, | |
78d17b80 CAW |
317 | Column('id', Integer, primary_key=True), |
318 | Column('creature', Integer, | |
319 | ForeignKey('creature.id'), nullable=False, | |
320 | index=True), | |
321 | Column('name', Unicode), | |
322 | Column('description', Unicode), | |
323 | Column('hitpower', Integer, nullable=False)) | |
324 | ||
248b5061 | 325 | creature_power.c.hitpower.alter(type=Float) |
64d28064 CAW |
326 | |
327 | ||
52539aca CAW |
328 | @RegisterMigration(8, FULL_MIGRATIONS) |
329 | def creature_power_name_creature_unique(db_conn): | |
330 | """ | |
331 | Add a unique constraint to name and creature on creature_power. | |
332 | ||
333 | We don't want multiple creature powers with the same name per creature! | |
334 | """ | |
335 | # Note: We don't actually check to see if this constraint is set | |
336 | # up because at present there's no way to do so in sqlalchemy :\ | |
337 | ||
338 | metadata = MetaData(bind=db_conn.bind) | |
339 | ||
340 | creature_power = Table( | |
341 | 'creature_power', metadata, | |
342 | autoload=True, autoload_with=db_conn.bind) | |
343 | ||
344 | cons = changeset.constraint.UniqueConstraint( | |
345 | 'name', 'creature', table=creature_power) | |
346 | ||
347 | cons.create() | |
348 | ||
349 | ||
64d28064 | 350 | def _insert_migration1_objects(session): |
356654de CAW |
351 | """ |
352 | Test objects to insert for the first set of things | |
353 | """ | |
64d28064 CAW |
354 | # Insert creatures |
355 | session.add_all( | |
94eff10d | 356 | [Creature1(name=u'centipede', |
64d28064 CAW |
357 | num_legs=100, |
358 | is_demon=False), | |
94eff10d | 359 | Creature1(name=u'wolf', |
64d28064 CAW |
360 | num_legs=4, |
361 | is_demon=False), | |
362 | # don't ask me what a wizardsnake is. | |
94eff10d | 363 | Creature1(name=u'wizardsnake', |
64d28064 CAW |
364 | num_legs=0, |
365 | is_demon=True)]) | |
366 | ||
d74a9483 | 367 | # Insert levels |
64d28064 | 368 | session.add_all( |
94eff10d CAW |
369 | [Level1(id=u'necroplex', |
370 | name=u'The Necroplex', | |
371 | description=u'A complex full of pure deathzone.', | |
64d28064 | 372 | exits={ |
c7dfd4fb BS |
373 | u'deathwell': u'evilstorm', |
374 | u'portal': u'central_park'}), | |
94eff10d CAW |
375 | Level1(id=u'evilstorm', |
376 | name=u'Evil Storm', | |
377 | description=u'A storm full of pure evil.', | |
64d28064 | 378 | exits={}), # you can't escape the evilstorm |
caed154a | 379 | Level1(id=u'central_park', |
94eff10d CAW |
380 | name=u'Central Park, NY, NY', |
381 | description=u"New York's friendly Central Park.", | |
64d28064 | 382 | exits={ |
c7dfd4fb | 383 | u'portal': u'necroplex'})]) |
64d28064 CAW |
384 | |
385 | session.commit() | |
386 | ||
d74a9483 CAW |
387 | |
388 | def _insert_migration2_objects(session): | |
356654de CAW |
389 | """ |
390 | Test objects to insert for the second set of things | |
391 | """ | |
d74a9483 CAW |
392 | # Insert creatures |
393 | session.add_all( | |
394 | [Creature2( | |
94eff10d | 395 | name=u'centipede', |
d74a9483 CAW |
396 | num_legs=100), |
397 | Creature2( | |
94eff10d | 398 | name=u'wolf', |
d74a9483 CAW |
399 | num_legs=4, |
400 | magical_powers = [ | |
401 | CreaturePower2( | |
94eff10d CAW |
402 | name=u"ice breath", |
403 | description=u"A blast of icy breath!", | |
d74a9483 CAW |
404 | hitpower=20), |
405 | CreaturePower2( | |
94eff10d CAW |
406 | name=u"death stare", |
407 | description=u"A frightening stare, for sure!", | |
d74a9483 CAW |
408 | hitpower=45)]), |
409 | Creature2( | |
94eff10d | 410 | name=u'wizardsnake', |
d74a9483 CAW |
411 | num_legs=0, |
412 | magical_powers=[ | |
413 | CreaturePower2( | |
94eff10d CAW |
414 | name=u'death_rattle', |
415 | description=u'A rattle... of DEATH!', | |
d74a9483 CAW |
416 | hitpower=1000), |
417 | CreaturePower2( | |
94eff10d | 418 | name=u'sneaky_stare', |
caed154a | 419 | description=u"The sneakiest stare you've ever seen!", |
d74a9483 CAW |
420 | hitpower=300), |
421 | CreaturePower2( | |
94eff10d CAW |
422 | name=u'slithery_smoke', |
423 | description=u"A blast of slithery, slithery smoke.", | |
d74a9483 CAW |
424 | hitpower=10), |
425 | CreaturePower2( | |
94eff10d CAW |
426 | name=u'treacherous_tremors', |
427 | description=u"The ground shakes beneath footed animals!", | |
d74a9483 CAW |
428 | hitpower=0)])]) |
429 | ||
430 | # Insert levels | |
431 | session.add_all( | |
94eff10d CAW |
432 | [Level2(id=u'necroplex', |
433 | name=u'The Necroplex', | |
434 | description=u'A complex full of pure deathzone.'), | |
435 | Level2(id=u'evilstorm', | |
436 | name=u'Evil Storm', | |
437 | description=u'A storm full of pure evil.', | |
d74a9483 | 438 | exits=[]), # you can't escape the evilstorm |
caed154a | 439 | Level2(id=u'central_park', |
94eff10d CAW |
440 | name=u'Central Park, NY, NY', |
441 | description=u"New York's friendly Central Park.")]) | |
d74a9483 CAW |
442 | |
443 | # necroplex exits | |
444 | session.add_all( | |
94eff10d CAW |
445 | [LevelExit2(name=u'deathwell', |
446 | from_level=u'necroplex', | |
447 | to_level=u'evilstorm'), | |
448 | LevelExit2(name=u'portal', | |
449 | from_level=u'necroplex', | |
450 | to_level=u'central_park')]) | |
d74a9483 CAW |
451 | |
452 | # there are no evilstorm exits because there is no exit from the | |
453 | # evilstorm | |
454 | ||
455 | # central park exits | |
456 | session.add_all( | |
94eff10d CAW |
457 | [LevelExit2(name=u'portal', |
458 | from_level=u'central_park', | |
459 | to_level=u'necroplex')]) | |
d74a9483 CAW |
460 | |
461 | session.commit() | |
dc3db468 CAW |
462 | |
463 | ||
464 | def _insert_migration3_objects(session): | |
465 | """ | |
466 | Test objects to insert for the third set of things | |
467 | """ | |
468 | # Insert creatures | |
469 | session.add_all( | |
470 | [Creature3( | |
94eff10d | 471 | name=u'centipede', |
dc3db468 CAW |
472 | num_limbs=100), |
473 | Creature3( | |
94eff10d | 474 | name=u'wolf', |
dc3db468 CAW |
475 | num_limbs=4, |
476 | magical_powers = [ | |
477 | CreaturePower3( | |
94eff10d CAW |
478 | name=u"ice breath", |
479 | description=u"A blast of icy breath!", | |
dc3db468 CAW |
480 | hitpower=20.0), |
481 | CreaturePower3( | |
94eff10d CAW |
482 | name=u"death stare", |
483 | description=u"A frightening stare, for sure!", | |
dc3db468 CAW |
484 | hitpower=45.0)]), |
485 | Creature3( | |
94eff10d | 486 | name=u'wizardsnake', |
dc3db468 CAW |
487 | num_limbs=0, |
488 | magical_powers=[ | |
489 | CreaturePower3( | |
94eff10d CAW |
490 | name=u'death_rattle', |
491 | description=u'A rattle... of DEATH!', | |
dc3db468 CAW |
492 | hitpower=1000.0), |
493 | CreaturePower3( | |
94eff10d | 494 | name=u'sneaky_stare', |
caed154a | 495 | description=u"The sneakiest stare you've ever seen!", |
dc3db468 CAW |
496 | hitpower=300.0), |
497 | CreaturePower3( | |
94eff10d CAW |
498 | name=u'slithery_smoke', |
499 | description=u"A blast of slithery, slithery smoke.", | |
dc3db468 CAW |
500 | hitpower=10.0), |
501 | CreaturePower3( | |
94eff10d CAW |
502 | name=u'treacherous_tremors', |
503 | description=u"The ground shakes beneath footed animals!", | |
dc3db468 CAW |
504 | hitpower=0.0)])], |
505 | # annnnnd one more to test a floating point hitpower | |
506 | Creature3( | |
94eff10d | 507 | name=u'deity', |
dc3db468 | 508 | numb_limbs=30, |
caed154a | 509 | magical_powers=[ |
dc3db468 | 510 | CreaturePower3( |
94eff10d CAW |
511 | name=u'smite', |
512 | description=u'Smitten by holy wrath!', | |
caed154a | 513 | hitpower=9999.9)])) |
dc3db468 CAW |
514 | |
515 | # Insert levels | |
516 | session.add_all( | |
94eff10d CAW |
517 | [Level3(id=u'necroplex', |
518 | name=u'The Necroplex', | |
519 | description=u'A complex full of pure deathzone.'), | |
520 | Level3(id=u'evilstorm', | |
521 | name=u'Evil Storm', | |
522 | description=u'A storm full of pure evil.', | |
dc3db468 | 523 | exits=[]), # you can't escape the evilstorm |
caed154a | 524 | Level3(id=u'central_park', |
94eff10d CAW |
525 | name=u'Central Park, NY, NY', |
526 | description=u"New York's friendly Central Park.")]) | |
dc3db468 CAW |
527 | |
528 | # necroplex exits | |
529 | session.add_all( | |
94eff10d CAW |
530 | [LevelExit3(name=u'deathwell', |
531 | from_level=u'necroplex', | |
532 | to_level=u'evilstorm'), | |
533 | LevelExit3(name=u'portal', | |
534 | from_level=u'necroplex', | |
535 | to_level=u'central_park')]) | |
dc3db468 CAW |
536 | |
537 | # there are no evilstorm exits because there is no exit from the | |
538 | # evilstorm | |
539 | ||
540 | # central park exits | |
541 | session.add_all( | |
94eff10d CAW |
542 | [LevelExit3(name=u'portal', |
543 | from_level=u'central_park', | |
544 | to_level=u'necroplex')]) | |
dc3db468 CAW |
545 | |
546 | session.commit() | |
547 | ||
09e2c487 CAW |
548 | def create_test_engine(): |
549 | from sqlalchemy import create_engine | |
550 | engine = create_engine('sqlite:///:memory:', echo=False) | |
40f0996a CAW |
551 | Session = sessionmaker(bind=engine) |
552 | return engine, Session | |
553 | ||
554 | ||
caed154a CAW |
555 | def assert_col_type(column, this_class): |
556 | assert isinstance(column.type, this_class) | |
09e2c487 CAW |
557 | |
558 | ||
e8e52b3a CAW |
559 | def _get_level3_exits(session, level): |
560 | return dict( | |
561 | [(level_exit.name, level_exit.to_level) | |
562 | for level_exit in | |
563 | session.query(LevelExit3).filter_by(from_level=level.id)]) | |
564 | ||
565 | ||
09e2c487 CAW |
566 | def test_set1_to_set3(): |
567 | # Create / connect to database | |
94eff10d CAW |
568 | # ---------------------------- |
569 | ||
40f0996a | 570 | engine, Session = create_test_engine() |
94eff10d | 571 | |
09e2c487 | 572 | # Create tables by migrating on empty initial set |
94eff10d CAW |
573 | # ----------------------------------------------- |
574 | ||
ecb4cc89 | 575 | printer = CollectingPrinter() |
40f0996a | 576 | migration_manager = MigrationManager( |
63c3ca28 | 577 | u'__main__', SET1_MODELS, SET1_FOUNDATIONS, SET1_MIGRATIONS, Session(), |
40f0996a | 578 | printer) |
94eff10d | 579 | |
40f0996a CAW |
580 | # Check latest migration and database current migration |
581 | assert migration_manager.latest_migration == 0 | |
582 | assert migration_manager.database_current_migration == None | |
583 | ||
584 | result = migration_manager.init_or_migrate() | |
94eff10d | 585 | |
40f0996a CAW |
586 | # Make sure output was "inited" |
587 | assert result == u'inited' | |
588 | # Check output | |
94eff10d | 589 | assert printer.combined_string == ( |
63c3ca28 | 590 | "-> Initializing main mediagoblin tables... done.\n" + \ |
591 | " + Laying foundations for Creature1 table\n" ) | |
40f0996a CAW |
592 | # Check version in database |
593 | assert migration_manager.latest_migration == 0 | |
594 | assert migration_manager.database_current_migration == 0 | |
09e2c487 CAW |
595 | |
596 | # Install the initial set | |
94eff10d CAW |
597 | # ----------------------- |
598 | ||
40f0996a | 599 | _insert_migration1_objects(Session()) |
94eff10d | 600 | |
40f0996a CAW |
601 | # Try to "re-migrate" with same manager settings... nothing should happen |
602 | migration_manager = MigrationManager( | |
63c3ca28 | 603 | u'__main__', SET1_MODELS, SET1_FOUNDATIONS, SET1_MIGRATIONS, |
604 | Session(), printer) | |
40f0996a CAW |
605 | assert migration_manager.init_or_migrate() == None |
606 | ||
09e2c487 | 607 | # Check version in database |
40f0996a CAW |
608 | assert migration_manager.latest_migration == 0 |
609 | assert migration_manager.database_current_migration == 0 | |
610 | ||
611 | # Sanity check a few things in the database... | |
caed154a | 612 | metadata = MetaData(bind=engine) |
40f0996a CAW |
613 | |
614 | # Check the structure of the creature table | |
615 | creature_table = Table( | |
616 | 'creature', metadata, | |
caed154a | 617 | autoload=True, autoload_with=engine) |
40f0996a CAW |
618 | assert set(creature_table.c.keys()) == set( |
619 | ['id', 'name', 'num_legs', 'is_demon']) | |
620 | assert_col_type(creature_table.c.id, Integer) | |
c7fa585b | 621 | assert_col_type(creature_table.c.name, VARCHAR) |
40f0996a | 622 | assert creature_table.c.name.nullable is False |
c7fa585b CAW |
623 | #assert creature_table.c.name.index is True |
624 | #assert creature_table.c.name.unique is True | |
40f0996a CAW |
625 | assert_col_type(creature_table.c.num_legs, Integer) |
626 | assert creature_table.c.num_legs.nullable is False | |
627 | assert_col_type(creature_table.c.is_demon, Boolean) | |
628 | ||
629 | # Check the structure of the level table | |
630 | level_table = Table( | |
631 | 'level', metadata, | |
caed154a | 632 | autoload=True, autoload_with=engine) |
40f0996a CAW |
633 | assert set(level_table.c.keys()) == set( |
634 | ['id', 'name', 'description', 'exits']) | |
c7fa585b | 635 | assert_col_type(level_table.c.id, VARCHAR) |
94eff10d | 636 | assert level_table.c.id.primary_key is True |
c7fa585b CAW |
637 | assert_col_type(level_table.c.name, VARCHAR) |
638 | assert_col_type(level_table.c.description, VARCHAR) | |
40f0996a CAW |
639 | # Skipping exits... Not sure if we can detect pickletype, not a |
640 | # big deal regardless. | |
641 | ||
642 | # Now check to see if stuff seems to be in there. | |
caed154a CAW |
643 | session = Session() |
644 | ||
94eff10d CAW |
645 | creature = session.query(Creature1).filter_by( |
646 | name=u'centipede').one() | |
647 | assert creature.num_legs == 100 | |
648 | assert creature.is_demon == False | |
649 | ||
650 | creature = session.query(Creature1).filter_by( | |
651 | name=u'wolf').one() | |
652 | assert creature.num_legs == 4 | |
653 | assert creature.is_demon == False | |
654 | ||
655 | creature = session.query(Creature1).filter_by( | |
656 | name=u'wizardsnake').one() | |
657 | assert creature.num_legs == 0 | |
658 | assert creature.is_demon == True | |
659 | ||
660 | level = session.query(Level1).filter_by( | |
e8e52b3a | 661 | id=u'necroplex').one() |
94eff10d | 662 | assert level.name == u'The Necroplex' |
505c0db1 | 663 | assert level.description == u'A complex full of pure deathzone.' |
94eff10d CAW |
664 | assert level.exits == { |
665 | 'deathwell': 'evilstorm', | |
666 | 'portal': 'central_park'} | |
667 | ||
668 | level = session.query(Level1).filter_by( | |
e8e52b3a | 669 | id=u'evilstorm').one() |
94eff10d CAW |
670 | assert level.name == u'Evil Storm' |
671 | assert level.description == u'A storm full of pure evil.' | |
672 | assert level.exits == {} # You still can't escape the evilstorm! | |
673 | ||
674 | level = session.query(Level1).filter_by( | |
e8e52b3a | 675 | id=u'central_park').one() |
94eff10d CAW |
676 | assert level.name == u'Central Park, NY, NY' |
677 | assert level.description == u"New York's friendly Central Park." | |
678 | assert level.exits == { | |
679 | 'portal': 'necroplex'} | |
680 | ||
681 | # Create new migration manager, but make sure the db migration | |
682 | # isn't said to be updated yet | |
ecb4cc89 | 683 | printer = CollectingPrinter() |
94eff10d | 684 | migration_manager = MigrationManager( |
a00ac320 | 685 | u'__main__', SET3_MODELS, SET3_MIGRATIONS, Session(), |
94eff10d CAW |
686 | printer) |
687 | ||
52539aca | 688 | assert migration_manager.latest_migration == 8 |
94eff10d | 689 | assert migration_manager.database_current_migration == 0 |
09e2c487 CAW |
690 | |
691 | # Migrate | |
94eff10d CAW |
692 | result = migration_manager.init_or_migrate() |
693 | ||
40f0996a | 694 | # Make sure result was "migrated" |
94eff10d CAW |
695 | assert result == u'migrated' |
696 | ||
697 | # TODO: Check output to user | |
698 | assert printer.combined_string == """\ | |
78d17b80 | 699 | -> Updating main mediagoblin tables: |
94eff10d CAW |
700 | + Running migration 1, "creature_remove_is_demon"... done. |
701 | + Running migration 2, "creature_powers_new_table"... done. | |
78d17b80 CAW |
702 | + Running migration 3, "level_exits_new_table"... done. |
703 | + Running migration 4, "creature_num_legs_to_num_limbs"... done. | |
704 | + Running migration 5, "level_exit_index_from_and_to_level"... done. | |
705 | + Running migration 6, "creature_power_index_creature"... done. | |
706 | + Running migration 7, "creature_power_hitpower_to_float"... done. | |
52539aca | 707 | + Running migration 8, "creature_power_name_creature_unique"... done. |
78d17b80 | 708 | """ |
94eff10d | 709 | |
09e2c487 | 710 | # Make sure version matches expected |
94eff10d | 711 | migration_manager = MigrationManager( |
a00ac320 | 712 | u'__main__', SET3_MODELS, SET3_MIGRATIONS, Session(), |
94eff10d | 713 | printer) |
52539aca CAW |
714 | assert migration_manager.latest_migration == 8 |
715 | assert migration_manager.database_current_migration == 8 | |
94eff10d | 716 | |
09e2c487 | 717 | # Check all things in database match expected |
94eff10d CAW |
718 | |
719 | # Check the creature table | |
78d17b80 | 720 | metadata = MetaData(bind=engine) |
94eff10d CAW |
721 | creature_table = Table( |
722 | 'creature', metadata, | |
caed154a | 723 | autoload=True, autoload_with=engine) |
78d17b80 CAW |
724 | # assert set(creature_table.c.keys()) == set( |
725 | # ['id', 'name', 'num_limbs']) | |
94eff10d | 726 | assert set(creature_table.c.keys()) == set( |
78d17b80 | 727 | [u'id', 'name', u'num_limbs', u'is_demon']) |
94eff10d | 728 | assert_col_type(creature_table.c.id, Integer) |
c7fa585b | 729 | assert_col_type(creature_table.c.name, VARCHAR) |
94eff10d | 730 | assert creature_table.c.name.nullable is False |
c7fa585b CAW |
731 | #assert creature_table.c.name.index is True |
732 | #assert creature_table.c.name.unique is True | |
78d17b80 CAW |
733 | assert_col_type(creature_table.c.num_limbs, Integer) |
734 | assert creature_table.c.num_limbs.nullable is False | |
94eff10d CAW |
735 | |
736 | # Check the CreaturePower table | |
737 | creature_power_table = Table( | |
738 | 'creature_power', metadata, | |
caed154a | 739 | autoload=True, autoload_with=engine) |
94eff10d CAW |
740 | assert set(creature_power_table.c.keys()) == set( |
741 | ['id', 'creature', 'name', 'description', 'hitpower']) | |
742 | assert_col_type(creature_power_table.c.id, Integer) | |
743 | assert_col_type(creature_power_table.c.creature, Integer) | |
744 | assert creature_power_table.c.creature.nullable is False | |
c7fa585b CAW |
745 | assert_col_type(creature_power_table.c.name, VARCHAR) |
746 | assert_col_type(creature_power_table.c.description, VARCHAR) | |
690b51fa | 747 | assert_col_type(creature_power_table.c.hitpower, Float) |
94eff10d CAW |
748 | assert creature_power_table.c.hitpower.nullable is False |
749 | ||
750 | # Check the structure of the level table | |
751 | level_table = Table( | |
752 | 'level', metadata, | |
caed154a | 753 | autoload=True, autoload_with=engine) |
94eff10d CAW |
754 | assert set(level_table.c.keys()) == set( |
755 | ['id', 'name', 'description']) | |
c7fa585b | 756 | assert_col_type(level_table.c.id, VARCHAR) |
94eff10d | 757 | assert level_table.c.id.primary_key is True |
c7fa585b CAW |
758 | assert_col_type(level_table.c.name, VARCHAR) |
759 | assert_col_type(level_table.c.description, VARCHAR) | |
94eff10d CAW |
760 | |
761 | # Check the structure of the level_exits table | |
762 | level_exit_table = Table( | |
763 | 'level_exit', metadata, | |
caed154a | 764 | autoload=True, autoload_with=engine) |
94eff10d CAW |
765 | assert set(level_exit_table.c.keys()) == set( |
766 | ['id', 'name', 'from_level', 'to_level']) | |
767 | assert_col_type(level_exit_table.c.id, Integer) | |
c7fa585b CAW |
768 | assert_col_type(level_exit_table.c.name, VARCHAR) |
769 | assert_col_type(level_exit_table.c.from_level, VARCHAR) | |
94eff10d | 770 | assert level_exit_table.c.from_level.nullable is False |
c7fa585b CAW |
771 | #assert level_exit_table.c.from_level.index is True |
772 | assert_col_type(level_exit_table.c.to_level, VARCHAR) | |
94eff10d | 773 | assert level_exit_table.c.to_level.nullable is False |
c7fa585b | 774 | #assert level_exit_table.c.to_level.index is True |
94eff10d CAW |
775 | |
776 | # Now check to see if stuff seems to be in there. | |
caed154a | 777 | session = Session() |
e8e52b3a | 778 | creature = session.query(Creature3).filter_by( |
690b51fa | 779 | name=u'centipede').one() |
5de0f4da | 780 | assert creature.num_limbs == 100.0 |
78d17b80 | 781 | assert creature.magical_powers == [] |
690b51fa | 782 | |
e8e52b3a | 783 | creature = session.query(Creature3).filter_by( |
690b51fa | 784 | name=u'wolf').one() |
5de0f4da | 785 | assert creature.num_limbs == 4.0 |
78d17b80 | 786 | assert creature.magical_powers == [] |
690b51fa | 787 | |
e8e52b3a | 788 | creature = session.query(Creature3).filter_by( |
690b51fa | 789 | name=u'wizardsnake').one() |
5de0f4da | 790 | assert creature.num_limbs == 0.0 |
78d17b80 | 791 | assert creature.magical_powers == [] |
94eff10d | 792 | |
e8e52b3a CAW |
793 | level = session.query(Level3).filter_by( |
794 | id=u'necroplex').one() | |
795 | assert level.name == u'The Necroplex' | |
505c0db1 | 796 | assert level.description == u'A complex full of pure deathzone.' |
e8e52b3a CAW |
797 | level_exits = _get_level3_exits(session, level) |
798 | assert level_exits == { | |
799 | u'deathwell': u'evilstorm', | |
800 | u'portal': u'central_park'} | |
801 | ||
802 | level = session.query(Level3).filter_by( | |
803 | id=u'evilstorm').one() | |
804 | assert level.name == u'Evil Storm' | |
805 | assert level.description == u'A storm full of pure evil.' | |
806 | level_exits = _get_level3_exits(session, level) | |
807 | assert level_exits == {} # You still can't escape the evilstorm! | |
808 | ||
809 | level = session.query(Level3).filter_by( | |
810 | id=u'central_park').one() | |
811 | assert level.name == u'Central Park, NY, NY' | |
812 | assert level.description == u"New York's friendly Central Park." | |
813 | level_exits = _get_level3_exits(session, level) | |
814 | assert level_exits == { | |
815 | 'portal': 'necroplex'} | |
09e2c487 CAW |
816 | |
817 | ||
d2506eeb | 818 | #def test_set2_to_set3(): |
09e2c487 CAW |
819 | # Create / connect to database |
820 | # Create tables by migrating on empty initial set | |
821 | ||
822 | # Install the initial set | |
823 | # Check version in database | |
824 | # Sanity check a few things in the database | |
825 | ||
826 | # Migrate | |
827 | # Make sure version matches expected | |
828 | # Check all things in database match expected | |
d2506eeb | 829 | # pass |
09e2c487 CAW |
830 | |
831 | ||
d2506eeb | 832 | #def test_set1_to_set2_to_set3(): |
09e2c487 CAW |
833 | # Create / connect to database |
834 | # Create tables by migrating on empty initial set | |
835 | ||
836 | # Install the initial set | |
837 | # Check version in database | |
838 | # Sanity check a few things in the database | |
839 | ||
840 | # Migrate | |
841 | # Make sure version matches expected | |
842 | # Check all things in database match expected | |
843 | ||
844 | # Migrate again | |
845 | # Make sure version matches expected again | |
846 | # Check all things in database match expected again | |
690b51fa CAW |
847 | |
848 | ##### Set2 | |
849 | # creature_table = Table( | |
850 | # 'creature', metadata, | |
e920b968 | 851 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
852 | # assert set(creature_table.c.keys()) == set( |
853 | # ['id', 'name', 'num_legs']) | |
854 | # assert_col_type(creature_table.c.id, Integer) | |
c7fa585b | 855 | # assert_col_type(creature_table.c.name, VARCHAR) |
690b51fa CAW |
856 | # assert creature_table.c.name.nullable is False |
857 | # assert creature_table.c.name.index is True | |
858 | # assert creature_table.c.name.unique is True | |
859 | # assert_col_type(creature_table.c.num_legs, Integer) | |
860 | # assert creature_table.c.num_legs.nullable is False | |
861 | ||
862 | # # Check the CreaturePower table | |
863 | # creature_power_table = Table( | |
864 | # 'creature_power', metadata, | |
e920b968 | 865 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
866 | # assert set(creature_power_table.c.keys()) == set( |
867 | # ['id', 'creature', 'name', 'description', 'hitpower']) | |
868 | # assert_col_type(creature_power_table.c.id, Integer) | |
869 | # assert_col_type(creature_power_table.c.creature, Integer) | |
870 | # assert creature_power_table.c.creature.nullable is False | |
c7fa585b CAW |
871 | # assert_col_type(creature_power_table.c.name, VARCHAR) |
872 | # assert_col_type(creature_power_table.c.description, VARCHAR) | |
690b51fa CAW |
873 | # assert_col_type(creature_power_table.c.hitpower, Integer) |
874 | # assert creature_power_table.c.hitpower.nullable is False | |
875 | ||
876 | # # Check the structure of the level table | |
877 | # level_table = Table( | |
878 | # 'level', metadata, | |
e920b968 | 879 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
880 | # assert set(level_table.c.keys()) == set( |
881 | # ['id', 'name', 'description']) | |
c7fa585b | 882 | # assert_col_type(level_table.c.id, VARCHAR) |
690b51fa | 883 | # assert level_table.c.id.primary_key is True |
c7fa585b CAW |
884 | # assert_col_type(level_table.c.name, VARCHAR) |
885 | # assert_col_type(level_table.c.description, VARCHAR) | |
690b51fa CAW |
886 | |
887 | # # Check the structure of the level_exits table | |
888 | # level_exit_table = Table( | |
889 | # 'level_exit', metadata, | |
e920b968 | 890 | # autoload=True, autoload_with=db_conn.bind) |
690b51fa CAW |
891 | # assert set(level_exit_table.c.keys()) == set( |
892 | # ['id', 'name', 'from_level', 'to_level']) | |
893 | # assert_col_type(level_exit_table.c.id, Integer) | |
c7fa585b CAW |
894 | # assert_col_type(level_exit_table.c.name, VARCHAR) |
895 | # assert_col_type(level_exit_table.c.from_level, VARCHAR) | |
690b51fa | 896 | # assert level_exit_table.c.from_level.nullable is False |
c7fa585b | 897 | # assert_col_type(level_exit_table.c.to_level, VARCHAR) |
690b51fa | 898 | |
d2506eeb | 899 | # pass |