witten
/
luminotes
Archived
1
0
Fork 0
This repository has been archived on 2023-12-16. You can view files and clone it, but cannot push or open issues or pull requests.
luminotes/tools/convertdb.py

168 lines
6.1 KiB
Python
Executable File

#!/usr/bin/python2.5
import os
import os.path
import psycopg2 as psycopg
from pytz import timezone, utc
from datetime import datetime
from controller.Old_database import Old_database
from controller.Scheduler import Scheduler
pacific = timezone( "US/Pacific" )
def quote( value ):
if value is None:
return "null"
# if this is a datetime, assume it's in the Pacific timezone, and then convert it to UTC
if isinstance( value, datetime ):
value = value.replace( tzinfo = pacific ).astimezone( utc )
value = unicode( value )
return "'%s'" % value.replace( "'", "''" ).replace( "\\", "\\\\" )
class Converter( object ):
"""
Converts a Luminotes database from bsddb to PostgreSQL, using the old bsddb controller.Old_database.
This assumes that the PostgreSQL schema from model/schema.sql is already in the database.
"""
def __init__( self, scheduler, database ):
self.scheduler = scheduler
self.database = database
self.conn = psycopg.connect( "dbname=luminotes user=luminotes password=dev" )
self.cursor = self.conn.cursor()
thread = self.convert_database()
self.scheduler.add( thread )
self.scheduler.wait_for( thread )
def convert_database( self ):
inserts = set()
notes = {} # map of note object id to its notebook
startup_notes = {} # map of startup note object id to its notebook
for key in self.database._Old_database__db.keys():
if not self.database._Old_database__db.get( key ):
continue
self.database.load( key, self.scheduler.thread )
value = ( yield Scheduler.SLEEP )
class_name = value.__class__.__name__
if class_name == "Notebook":
if ( value.object_id, value.revision ) in inserts: continue
inserts.add( ( value.object_id, value.revision ) )
self.cursor.execute(
"insert into notebook " +
"( id, revision, name, trash_id ) " +
"values ( %s, %s, %s, %s );" %
( quote( value.object_id ), quote( value.revision ), quote( value.name ), value.trash and quote( value.trash.object_id ) or "null" )
)
for note in value.notes:
notes[ note.object_id ] = value
for startup_note in value.startup_notes:
startup_notes[ startup_note.object_id ] = value
elif class_name == "Note":
if ( value.object_id, value.revision ) in inserts: continue
inserts.add( ( value.object_id, value.revision ) )
# notebook_id, startup, and rank are all set below since they're pulled out of Notebook objects
self.cursor.execute(
"insert into note " +
"( id, revision, title, contents, notebook_id, startup, deleted_from_id, rank ) " +
"values ( %s, %s, %s, %s, %s, %s, %s, %s );" %
( quote( value.object_id ), quote( value.revision ), quote( value.title or None ), quote( value.contents or None ), quote( None ), quote( "f" ), quote( value.deleted_from or None ), quote( None ) )
)
elif class_name == "User":
if value.username is None: continue # note: this will skip all demo users
if ( value.object_id, value.revision ) in inserts: continue
inserts.add( ( value.object_id, value.revision ) )
self.cursor.execute(
"insert into luminotes_user " +
"( id, revision, username, salt, password_hash, email_address, storage_bytes, rate_plan ) " +
"values ( %s, %s, %s, %s, %s, %s, %s, %s );" %
( quote( value.object_id ), quote( value.revision ), quote( value.username ), quote( value._User__salt ), quote( value._User__password_hash ), quote( value.email_address ), value.storage_bytes, value.rate_plan )
)
for notebook in value.notebooks:
if notebook is None: continue
read_only = ( notebook.__class__.__name__ == "Read_only_notebook" )
if read_only:
notebook_id = notebook._Read_only_notebook__wrapped.object_id
else:
notebook_id = notebook.object_id
if ( value.object_id, notebook_id ) in inserts: continue
inserts.add( ( value.object_id, notebook_id ) )
self.cursor.execute(
"insert into user_notebook " +
"( user_id, notebook_id, read_write ) " +
"values ( %s, %s, %s );" %
( quote( value.object_id ), quote( notebook_id ),
quote( read_only and "f" or "t" ) )
)
if notebook.trash:
self.cursor.execute(
"insert into user_notebook " +
"( user_id, notebook_id, read_write ) " +
"values ( %s, %s, %s );" %
( quote( value.object_id ), quote( notebook.trash.object_id ),
quote( read_only and "f" or "t" ) )
)
elif class_name == "Read_only_notebook":
pass
elif class_name == "Password_reset":
if value.redeemed == True: continue
if ( value.object_id, value.revision ) in inserts: continue
inserts.add( ( value.object_id, value.revision ) )
self.cursor.execute(
"insert into password_reset " +
"( id, email_address, redeemed ) " +
"values ( %s, %s, %s );" %
( quote( value.object_id ), quote( value.email_address ), quote( value.redeemed and "t" or "f" ) )
)
elif class_name == "User_list":
pass
else:
raise Exception( "Unconverted value of type %s" % class_name )
for ( note_id, notebook ) in notes.items():
self.cursor.execute(
"update note set notebook_id = %s where id = %s" % ( quote( notebook.object_id ), quote( note_id ) )
)
for ( startup_note_id, notebook ) in startup_notes.items():
startup_ids = [ note.object_id for note in notebook.startup_notes ]
rank = startup_ids.index( startup_note_id )
self.cursor.execute(
"update note set startup = 't', rank = %s where id = %s" % ( rank, quote( startup_note_id ) )
)
self.conn.commit()
yield None
def main():
scheduler = Scheduler()
database = Old_database( scheduler, "data.db" )
initializer = Converter( scheduler, database )
scheduler.wait_until_idle()
if __name__ == "__main__":
main()