4 from sqlalchemy
.exc
import IntegrityError
5 import sqlalchemy
.types
7 from .db
import connect
, metadata
, tables
as tables_module
8 from pokedex
.lookup
import lookup
as pokedex_lookup
11 if len(sys
.argv
) <= 1:
17 # Find the command as a function in this file
18 func
= globals().get("command_%s" % command
, None)
25 def command_csvimport(engine_uri
, directory
='.'):
28 from sqlalchemy
.orm
.attributes
import instrumentation_registry
30 # Use autocommit in case rows fail due to foreign key incest
31 session
= connect(engine_uri
, autocommit
=True, autoflush
=False)
35 # SQLAlchemy is retarded and there is no way for me to get a list of ORM
36 # classes besides to inspect the module they all happen to live in for
37 # things that look right.
38 table_base
= tables_module
.TableBase
39 orm_classes
= {} # table object => table class
41 for name
in dir(tables_module
):
42 # dir() returns strings! How /convenient/.
43 thingy
= getattr(tables_module
, name
)
45 if not isinstance(thingy
, type):
48 elif not issubclass(thingy
, table_base
):
49 # Not a declarative table; bail
51 elif thingy
== table_base
:
52 # Declarative table base, so not a real table; bail
55 # thingy is definitely a table class! Hallelujah.
56 orm_classes
[thingy
.__table__
] = thingy
58 # Okay, run through the tables and actually load the data now
59 for table_obj
in metadata
.sorted_tables
:
60 table_class
= orm_classes
[table_obj
]
61 table_name
= table_obj
.name
63 # Print the table name but leave the cursor in a fixed column
64 print table_name
+ '...', ' ' * (40 - len(table_name
)),
68 csvfile
= open("%s/%s.csv" %
(directory
, table_name
), 'rb')
70 # File doesn't exist; don't load anything!
74 reader
= csv
.reader(csvfile
, lineterminator
='\n')
75 column_names
= [unicode(column
) for column
in reader
.next()]
77 # Self-referential tables may contain rows with foreign keys of
78 # other rows in the same table that do not yet exist. We'll keep
79 # a running list of these and try inserting them again after the
86 for column_name
, value
in zip(column_names
, csvs
):
87 column
= table_obj
.c
[column_name
]
88 if column
.nullable
and value
== '':
89 # Empty string in a nullable column really means NULL
91 elif isinstance(column
.type, sqlalchemy
.types
.Boolean
):
92 # Boolean values are stored as string values 0/1, but both
93 # of those evaluate as true; SQLA wants True/False
99 # Otherwise, unflatten from bytes
100 value
= value
.decode('utf-8')
102 setattr(row
, column_name
, value
)
107 except IntegrityError
as e
:
108 failed_rows
.append(row
)
110 # Loop over the failed rows and keep trying to insert them. If a loop
111 # doesn't manage to insert any rows, bail.
112 do_another_loop
= True
113 while failed_rows
and do_another_loop
:
114 do_another_loop
= False
116 for i
, row
in enumerate(failed_rows
):
123 do_another_loop
= True
124 except IntegrityError
as e
:
128 print len(failed_rows
), "rows failed"
132 def command_csvexport(engine_uri
, directory
='.'):
134 session
= connect(engine_uri
)
136 for table_name
in sorted(metadata
.tables
.keys()):
138 table
= metadata
.tables
[table_name
]
140 writer
= csv
.writer(open("%s/%s.csv" %
(directory
, table_name
), 'wb'),
142 columns
= [col
.name
for col
in table
.columns
]
143 writer
.writerow(columns
)
145 for row
in session
.query(table
).all():
148 # Convert Pythony values to something more universal
149 val
= getattr(row
, col
)
157 val
= unicode(val
).encode('utf-8')
161 writer
.writerow(csvs
)
163 def command_lookup(engine_uri
, name
):
164 # XXX don't require uri! somehow
165 session
= connect(engine_uri
)
167 results
, exact
= pokedex_lookup(session
, name
)
171 print "Fuzzy-matched:"
173 for object in results
:
174 print object.__tablename__
, object.name
178 print u
"""pokedex -- a command-line Pokédex interface
180 help Displays this message.
181 lookup {uri} [name] Look up something in the Pokédex.
183 These commands are only useful for developers:
184 csvimport {uri} [dir] Import data from a set of CSVs to the database
186 csvexport {uri} [dir] Export data from the database given by the URI
188 Directory defaults to cwd.