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