diff --git a/controller/Old_database.py b/controller/Old_database.py deleted file mode 100644 index e56dd1f..0000000 --- a/controller/Old_database.py +++ /dev/null @@ -1,303 +0,0 @@ -import re -import bsddb -import random -import cPickle -from cStringIO import StringIO -from copy import copy -from model.Persistent import Persistent -from Async import async - - -class Old_database( object ): - ID_BITS = 128 # number of bits within an id - ID_DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz" - - def __init__( self, scheduler, database_path = None ): - """ - Create a new database and return it. - - @type scheduler: Scheduler - @param scheduler: scheduler to use - @type database_path: unicode - @param database_path: path to the database file - @rtype: Old_database - @return: database at the given path - """ - self.__scheduler = scheduler - self.__env = bsddb.db.DBEnv() - self.__env.open( None, bsddb.db.DB_CREATE | bsddb.db.DB_PRIVATE | bsddb.db.DB_INIT_MPOOL ) - self.__db = bsddb.db.DB( self.__env ) - self.__db.open( database_path, "database", bsddb.db.DB_HASH, bsddb.db.DB_CREATE ) - self.__cache = {} - - def __persistent_id( self, obj, skip = None ): - # save the object and return its persistent id - if obj != skip and isinstance( obj, Persistent ): - self.__save( obj ) - return obj.object_id - - # returning None indicates that the object should be pickled normally without using a persistent id - return None - - @async - def save( self, obj, callback = None ): - """ - Save the given object to the database, including any objects that it references. - - @type obj: Persistent - @param obj: object to save - @type callback: generator or NoneType - @param callback: generator to wakeup when the save is complete (optional) - """ - self.__save( obj ) - yield callback - - def __save( self, obj ): - # if this object's current revision is already saved, bail - revision_id = obj.revision_id() - if revision_id in self.__cache: - return - - object_id = unicode( obj.object_id ).encode( "utf8" ) - revision_id = unicode( obj.revision_id() ).encode( "utf8" ) - secondary_id = obj.secondary_id and unicode( obj.full_secondary_id() ).encode( "utf8" ) or None - - # update the cache with this saved object - self.__cache[ object_id ] = obj - self.__cache[ revision_id ] = copy( obj ) - if secondary_id: - self.__cache[ secondary_id ] = obj - - # set the pickler up to save persistent ids for every object except for the obj passed in, which - # will be pickled normally - buffer = StringIO() - pickler = cPickle.Pickler( buffer, protocol = -1 ) - pickler.persistent_id = lambda o: self.__persistent_id( o, skip = obj ) - - # pickle the object and write it to the database under both its id key and its revision id key - pickler.dump( obj ) - pickled = buffer.getvalue() - self.__db.put( object_id, pickled ) - self.__db.put( revision_id, pickled ) - - # write the pickled object id (only) to the database under its secondary id - if secondary_id: - buffer = StringIO() - pickler = cPickle.Pickler( buffer, protocol = -1 ) - pickler.persistent_id = lambda o: self.__persistent_id( o ) - pickler.dump( obj ) - self.__db.put( secondary_id, buffer.getvalue() ) - - self.__db.sync() - - @async - def load( self, object_id, callback, revision = None ): - """ - Load the object corresponding to the given object id from the database, and yield the provided - callback generator with the loaded object as its argument, or None if the object_id is unknown. - If a revision is provided, a specific revision of the object will be loaded. - - @type object_id: unicode - @param object_id: id of the object to load - @type callback: generator - @param callback: generator to send the loaded object to - @type revision: int or NoneType - @param revision: revision of the object to load (optional) - """ - obj = self.__load( object_id, revision ) - yield callback, obj - - def __load( self, object_id, revision = None ): - if revision is not None: - object_id = Persistent.make_revision_id( object_id, revision ) - - object_id = unicode( object_id ).encode( "utf8" ) - - # if the object corresponding to the given id has already been loaded, simply return it without - # loading it again - obj = self.__cache.get( object_id ) - if obj is not None: - return obj - - # grab the object for the given id from the database - buffer = StringIO() - unpickler = cPickle.Unpickler( buffer ) - unpickler.persistent_load = self.__load - - pickled = self.__db.get( object_id ) - if pickled is None or pickled == "": - return None - - buffer.write( pickled ) - buffer.flush() - buffer.seek( 0 ) - - # unpickle the object and update the cache with this saved object - obj = unpickler.load() - if obj is None: - print "error unpickling %s: %s" % ( object_id, pickled ) - return None - self.__cache[ unicode( obj.object_id ).encode( "utf8" ) ] = obj - self.__cache[ unicode( obj.revision_id() ).encode( "utf8" ) ] = copy( obj ) - - return obj - - @async - def reload( self, object_id, callback = None ): - """ - Load and immediately save the object corresponding to the given object id or database key. This - is useful when the object has a __setstate__() method that performs some sort of schema - evolution operation. - - @type object_id: unicode - @param object_id: id or key of the object to reload - @type callback: generator or NoneType - @param callback: generator to wakeup when the save is complete (optional) - """ - self.__reload( object_id ) - yield callback - - def __reload( self, object_id, revision = None ): - object_id = unicode( object_id ).encode( "utf8" ) - - # grab the object for the given id from the database - buffer = StringIO() - unpickler = cPickle.Unpickler( buffer ) - unpickler.persistent_load = self.__load - - pickled = self.__db.get( object_id ) - if pickled is None or pickled == "": - return - - buffer.write( pickled ) - buffer.flush() - buffer.seek( 0 ) - - # unpickle the object. this should trigger __setstate__() if the object has such a method - obj = unpickler.load() - if obj is None: - print "error unpickling %s: %s" % ( object_id, pickled ) - return - self.__cache[ object_id ] = obj - - # set the pickler up to save persistent ids for every object except for the obj passed in, which - # will be pickled normally - buffer = StringIO() - pickler = cPickle.Pickler( buffer, protocol = -1 ) - pickler.persistent_id = lambda o: self.__persistent_id( o, skip = obj ) - - # pickle the object and write it to the database under its id key - pickler.dump( obj ) - pickled = buffer.getvalue() - self.__db.put( object_id, pickled ) - - self.__db.sync() - - def size( self, object_id, revision = None ): - """ - Load the object corresponding to the given object id from the database, and return the size of - its pickled data in bytes. If a revision is provided, a specific revision of the object will be - loaded. - - @type object_id: unicode - @param object_id: id of the object whose size should be returned - @type revision: int or NoneType - @param revision: revision of the object to load (optional) - """ - if revision is not None: - object_id = Persistent.make_revision_id( object_id, revision ) - - object_id = unicode( object_id ).encode( "utf8" ) - - pickled = self.__db.get( object_id ) - if pickled is None or pickled == "": - return None - - return len( pickled ) - - @staticmethod - def generate_id(): - int_id = random.getrandbits( Old_database.ID_BITS ) - - base = len( Old_database.ID_DIGITS ) - digits = [] - - while True: - index = int_id % base - digits.insert( 0, Old_database.ID_DIGITS[ index ] ) - int_id = int_id / base - if int_id == 0: - break - - return "".join( digits ) - - @async - def next_id( self, callback ): - """ - Generate the next available object id, and yield the provided callback generator with the - object id as its argument. - - @type callback: generator - @param callback: generator to send the next available object id to - """ - # generate a random id, but on the off-chance that it collides with something else already in - # the database, try again - next_id = Old_database.generate_id() - while self.__db.get( next_id, default = None ) is not None: - next_id = Old_database.generate_id() - - # save the next_id as a key in the database so that it's not handed out again to another client - self.__db[ next_id ] = "" - - yield callback, next_id - - @async - def close( self ): - """ - Shutdown the database. - """ - self.__db.close() - self.__env.close() - yield None - - @async - def clear_cache( self ): - """ - Clear the memory object cache. - """ - self.__cache.clear() - yield None - - scheduler = property( lambda self: self.__scheduler ) - - -class Valid_id( object ): - """ - Validator for an object id. - """ - ID_PATTERN = re.compile( "^[%s]+$" % Old_database.ID_DIGITS ) - - def __init__( self, none_okay = False ): - self.__none_okay = none_okay - - def __call__( self, value ): - if self.__none_okay and value in ( None, "None", "" ): return None - if self.ID_PATTERN.search( value ): return str( value ) - - raise ValueError() - - -class Valid_revision( object ): - """ - Validator for an object id. - """ - REVISION_PATTERN = re.compile( "^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d+$" ) - - def __init__( self, none_okay = False ): - self.__none_okay = none_okay - - def __call__( self, value ): - if self.__none_okay and value in ( None, "None", "" ): return None - if self.REVISION_PATTERN.search( value ): return str( value ) - - raise ValueError() diff --git a/controller/Scheduler.py b/controller/Scheduler.py deleted file mode 100644 index 385565e..0000000 --- a/controller/Scheduler.py +++ /dev/null @@ -1,157 +0,0 @@ -from time import time, sleep -from threading import Thread, Semaphore - - -class Scheduler( object ): - SLEEP = 0 # yielded by a generator to indicate that it should be put to sleep - - def __init__( self ): - """ - A scheduler for generator-based microthreads. - """ - self.__running = [] # list of active microthreads - self.__sleeping = [] # list of sleeping microthreads - self.__messages = {} # map of thread to list of its incoming messages - self.__thread = None # currently executing microthread (if any) - self.__done = False # whether it's time to exit - self.__idle = Semaphore( 0 ) - self.__last_error = None # used for unit tests - - self.add( self.__idle_thread() ) - self.__idle.acquire() # don't count the idle thread - - # TODO: Running the scheduler from anything other than the main Python thread somehow prevents - # tracebacks from within a generator from indicating the offending line and line number. So it - # would be really useful for debugging purposes to start the scheduler from the main thread. - # The reason that it's not done here is because CherryPy's blocking server must be started - # from the main Python thread. - self.__scheduler_thread = Thread( target = self.run ) - self.__scheduler_thread.setDaemon( True ) - self.__scheduler_thread.start() - - def run( self ): - """ - Run all threads repeatedly. - """ - while not self.__done: - self.__run_once() - - def __run_once( self ): - """ - Run all active threads once. - """ - turn_start = time() - - for thread in list( self.__running ): - try: - messages = self.__messages.get( thread ) - - self.__thread = thread - try: - if messages: - result = thread.send( *messages.pop( 0 ) ) - else: - result = thread.next() - except StopIteration: - raise - except Exception, e: - self.__last_error = e - import traceback - traceback.print_exc() - raise StopIteration() - - self__thread = None - - if self.__done: - return True - - if result is None: - continue - - # a yielded result of SLEEP indicates to put the thread to sleep - if result == Scheduler.SLEEP: - self.sleep( thread ) - # any other result indicates to run the yielded thread - elif isinstance( result, ( tuple, list ) ): - self.add( *result ) - else: - self.add( result ) - - except StopIteration: - self.__idle.acquire( blocking = False ) - self.__running.remove( thread ) - self.__messages.pop( thread, None ) - - def __idle_thread( self ): - while not self.__done: - # if the idle thread is the only one running, block until there's another running thread - self.__idle.acquire( blocking = True ) - self.__idle.release() - yield None - - IDLE_SLEEP_SECONDS = 0.01 - def wait_for( self, thread ): - """ - Block until the given thread exits. Intended for use in unit tests only. - - @type thread: generator - @param thread: thread to wait for - """ - while thread in self.__running or thread in self.__sleeping: - sleep( self.IDLE_SLEEP_SECONDS ) - - if self.__last_error: - raise self.__last_error - - def wait_until_idle( self ): - """ - Block until all threads have exited. Intended for use in unit tests only. - """ - while len( self.__running ) > 1 or len( self.__sleeping ) > 0: - sleep( self.IDLE_SLEEP_SECONDS ) - - def sleep( self, thread ): - """ - Put the given thread to sleep so that is is no longer actively running. - - @type thread: generator - @param thread: thread to put to sleep - """ - self.__idle.acquire( blocking = False ) - self.__sleeping.append( thread ) - self.__running.remove( thread ) - - def add( self, thread, *args ): - """ - Add the given thread to the running list for this Scheduler, and wake it up if it's asleep. - - @type thread: generator - @param thread: thread to add - @type args: tuple - @param args: arguments to send() to the given thread when it is executed - """ - if thread is None: - return - - self.__idle.release() - - if thread in self.__sleeping: - self.__sleeping.remove( thread ) - else: - self.__messages[ thread ] = [ ( None, ) ] - - self.__running.append( thread ) - - if len( args ) > 0: - self.__messages[ thread ].append( args ) - - def shutdown( self ): - """ - Stop all running threads and shutdown the Scheduler. - """ - self.__done = True - self.__idle.release() - self.__scheduler_thread.join() - - # currently executing microthread (if any) - thread = property( lambda self: self.__thread ) diff --git a/model/Note.py b/model/Note.py deleted file mode 100644 index 33b8c96..0000000 --- a/model/Note.py +++ /dev/null @@ -1,70 +0,0 @@ -import re -from Persistent import Persistent -from controller.Html_nuker import Html_nuker - - -class Note( Persistent ): - """ - An single textual wiki note. - """ - TITLE_PATTERN = re.compile( u"

(.*?)

", flags = re.IGNORECASE ) - - def __setstate__( self, state ): - if "_Note__deleted_from" not in state: - state[ "_Note__deleted_from" ] = False - - self.__dict__.update( state ) - - def __init__( self, id, contents = None ): - """ - Create a new note with the given id and contents. - - @type id: unicode - @param id: id of the note - @type contents: unicode or NoneType - @param contents: initial contents of the note (optional) - @rtype: Note - @return: newly constructed note - """ - Persistent.__init__( self, id ) - self.__title = None - self.__contents = None or "" - self.__deleted_from = None - - self.__set_contents( contents, new_revision = False ) - - def __set_contents( self, contents, new_revision = True ): - if new_revision: - self.update_revision() - self.__contents = contents - - if contents is None: - self.__title = None - return - - # parse title out of the beginning of the contents - result = Note.TITLE_PATTERN.search( contents ) - - if result: - self.__title = result.groups()[ 0 ] - self.__title = Html_nuker( allow_refs = True ).nuke( self.__title ) - else: - self.__title = None - - def __set_deleted_from( self, deleted_from ): - self.__deleted_from = deleted_from - self.update_revision() - - def to_dict( self ): - d = Persistent.to_dict( self ) - d.update( dict( - contents = self.__contents, - title = self.__title, - deleted_from = self.__deleted_from, - ) ) - - return d - - contents = property( lambda self: self.__contents, __set_contents ) - title = property( lambda self: self.__title ) - deleted_from = property( lambda self: self.__deleted_from, __set_deleted_from ) diff --git a/model/Notebook.py b/model/Notebook.py deleted file mode 100644 index d336377..0000000 --- a/model/Notebook.py +++ /dev/null @@ -1,185 +0,0 @@ -from copy import copy -from Note import Note -from Persistent import Persistent - - -class Notebook( Persistent ): - """ - A collection of wiki notes. - """ - - class UnknownNoteError( ValueError ): - """ - Indicates that an accessed note is not in this notebook. - """ - def __init__( self, note_id ): - ValueError.__init__( self, note_id ) - - def __setstate__( self, state ): - if "_Notebook__trash" not in state: - state[ "_Notebook__trash" ] = None - - self.__dict__.update( state ) - - def __init__( self, id, name, trash = None ): - """ - Create a new note with the given id and name. - - @type id: unicode - @param id: id of the notebook - @type name: unicode - @param name: name of this notebook - @type trash: Notebook or NoneType - @param trash: where deleted notes within this Notebook go to die (optional) - @rtype: Notebook - @return: newly constructed notebook - """ - Persistent.__init__( self, id ) - self.__name = name - self.__trash = trash - self.__notes = {} # map of note id to note - self.__titles = {} # map of note title to note - self.__startup_notes = [] # list of notes shown on startup - - def add_note( self, note ): - """ - Add a note to this notebook. - - @type note: Note - @param note: note to add - """ - self.update_revision() - self.__notes[ note.object_id ] = note - self.__titles[ note.title ] = note - - def remove_note( self, note ): - """ - Remove a note from this notebook. - - @type note: Note - @param note: note to remove - @rtype: bool - @return: True if the note was removed, False if the note wasn't in this notebook - """ - if self.__notes.pop( note.object_id, None ): - self.update_revision() - self.__titles.pop( note.title, None ) - if self.is_startup_note( note ): - self.__startup_notes.remove( note ) - return True - - return False - - def lookup_note( self, note_id ): - """ - Return the note in this notebook with the given id. - - @type note_id: unicode - @param note_id: id of the note to return - @rtype: Note or NoneType - @return: note corresponding to the note id or None - """ - return self.__notes.get( note_id ) - - def lookup_note_by_title( self, title ): - """ - Return the note in this notebook with the given title. - - @type title: unicode - @param title: title of the note to return - @rtype: Note or NoneType - @return: note corresponding to the title or None - """ - return self.__titles.get( title ) - - def update_note( self, note, contents ): - """ - Update the given note with new contents. Bail if the note's contents are unchanged. - - @type note: Note - @param note: note to update - @type contents: unicode - @param contents: new textual contents for the note - @raises UnknownNoteError: note to update is not in this notebook - """ - old_note = self.__notes.get( note.object_id ) - if old_note is None: - raise Notebook.UnknownNoteError( note.object_id ) - - self.update_revision() - self.__titles.pop( note.title, None ) - - note.contents = contents - - self.__titles[ note.title ] = note - - def add_startup_note( self, note ): - """ - Add the given note to be shown on startup. It must already be a note in this notebook. - - @type note: Note - @param note: note to be added for startup - @rtype: bool - @return: True if the note was added for startup - @raises UnknownNoteError: given note is not in this notebook - """ - if self.__notes.get( note.object_id ) is None: - raise Notebook.UnknownNoteError( note.object_id ) - - if not self.is_startup_note( note ): - self.update_revision() - self.__startup_notes.append( note ) - return True - - return False - - def remove_startup_note( self, note ): - """ - Remove the given note from being shown on startup. - - @type note: Note - @param note: note to be removed from startup - @rtype: bool - @return: True if the note was removed from startup - """ - if self.is_startup_note( note ): - self.update_revision() - self.__startup_notes.remove( note ) - return True - - return False - - def is_startup_note( self, note ): - """ - Return whether the given note is a startup note. - - @type note: Note - @param note: note to test for startup status - @rtype bool - @return: True if the note is a startup note - """ - return note.object_id in [ n.object_id for n in self.__startup_notes if n ] - - def to_dict( self ): - d = Persistent.to_dict( self ) - - # as an optimization, don't include the revisions list because it's not - # currently used anywhere for Notebook objects - del d[ "revisions_list" ] - - d.update( dict( - name = self.__name, - trash = self.__trash, - read_write = True, - ) ) - - return d - - def __set_name( self, name ): - self.__name = name - self.update_revision() - - name = property( lambda self: self.__name, __set_name ) - trash = property( lambda self: self.__trash ) - startup_notes = property( lambda self: [ note for note in copy( self.__startup_notes ) if note is not None ] ) - notes = property( lambda self: [ note for note in self.__notes.values() if note is not None ] ) diff --git a/model/Password_reset.py b/model/Password_reset.py deleted file mode 100644 index a2d4a30..0000000 --- a/model/Password_reset.py +++ /dev/null @@ -1,29 +0,0 @@ -from Persistent import Persistent - - -class Password_reset( Persistent ): - """ - A request for a password reset. - """ - def __init__( self, id, email_address ): - """ - Create a password reset request with the given id. - - @type id: unicode - @param id: id of the password reset - @type email_address: unicode - @param email_address: where the reset confirmation was emailed - @rtype: Password_reset - @return: newly constructed password reset - """ - Persistent.__init__( self, id ) - self.__email_address = email_address - self.__redeemed = False - - def __set_redeemed( self, redeemed ): - if redeemed != self.__redeemed: - self.update_revision() - self.__redeemed = redeemed - - email_address = property( lambda self: self.__email_address ) - redeemed = property( lambda self: self.__redeemed, __set_redeemed ) diff --git a/model/Persistent.py b/model/Persistent.py deleted file mode 100644 index 954087b..0000000 --- a/model/Persistent.py +++ /dev/null @@ -1,37 +0,0 @@ -from datetime import datetime - - -class Persistent( object ): - def __init__( self, object_id, secondary_id = None ): - self.__object_id = object_id - self.__secondary_id = secondary_id - self.__revision = datetime.now() - self.__revisions_list = [ self.__revision ] - - def update_revision( self ): - self.__revision = datetime.now() - - # make a new copy of the list to prevent sharing of this list between different revisions - self.__revisions_list = self.__revisions_list + [ self.__revision ] - - def revision_id( self ): - return "%s %s" % ( self.__object_id, self.__revision ) - - @staticmethod - def make_revision_id( object_id, revision ): - return "%s %s" % ( object_id, revision ) - - def full_secondary_id( self ): - return "%s %s" % ( type( self ).__name__, self.secondary_id ) - - def to_dict( self ): - return dict( - object_id = self.__object_id, - revision = self.__revision, - revisions_list = self.__revisions_list, - ) - - object_id = property( lambda self: self.__object_id ) - secondary_id = property( lambda self: self.__secondary_id ) - revision = property( lambda self: self.__revision ) - revisions_list = property( lambda self: self.__revisions_list ) diff --git a/model/Read_only_notebook.py b/model/Read_only_notebook.py deleted file mode 100644 index c41c93e..0000000 --- a/model/Read_only_notebook.py +++ /dev/null @@ -1,31 +0,0 @@ -from Persistent import Persistent - - -class Read_only_notebook( Persistent ): - """ - A wrapper for Notebook that hides all of its destructive update functions. - """ - def __init__( self, id, notebook ): - Persistent.__init__( self, id ) - self.__wrapped = notebook - - def lookup_note( self, note_id ): - return self.__wrapped.lookup_note( note_id ) - - def lookup_note_by_title( self, title ): - return self.__wrapped.lookup_note_by_title( title ) - - def to_dict( self ): - d = self.__wrapped.to_dict() - del( d[ "trash" ] ) # don't expose the trash to read-only views of this notebook - d.update( dict( - object_id = self.object_id, - read_write = False, - ) ) - - return d - - name = property( lambda self: self.__wrapped.name ) - trash = None # read-only access doesn't give you access to the Notebook's trash - notes = property( lambda self: self.__wrapped.notes ) - startup_notes = property( lambda self: self.__wrapped.startup_notes ) diff --git a/model/User.py b/model/User.py deleted file mode 100644 index e373a8f..0000000 --- a/model/User.py +++ /dev/null @@ -1,122 +0,0 @@ -import sha -import random -from copy import copy -from Persistent import Persistent - - -class User( Persistent ): - """ - A user of this application. - """ - SALT_CHARS = [ chr( c ) for c in range( ord( "!" ), ord( "~" ) + 1 ) ] - SALT_SIZE = 12 - - def __setstate__( self, state ): - if "_User__storage_bytes" not in state: - state[ "_User__storage_bytes" ] = 0 - if "_User__rate_plan" not in state: - state[ "_User__rate_plan" ] = 0 - - self.__dict__.update( state ) - - def __init__( self, id, username, password, email_address, notebooks = None ): - """ - Create a new user with the given credentials and information. - - @type id: unicode - @param id: id of the user - @type username: unicode - @param username: unique user identifier for login purposes - @type password: unicode - @param password: secret password for login purposes - @type email_address: unicode - @param email_address: a hopefully valid email address - @type notebooks: [ Notebook ] - @param notebooks: list of notebooks (read-only and read-write) that this user has access to - @rtype: User - @return: newly created user - """ - Persistent.__init__( self, id, secondary_id = username ) - self.__salt = self.__create_salt() - self.__password_hash = self.__hash_password( password ) - self.__email_address = email_address - self.__notebooks = notebooks or [] - self.__storage_bytes = 0 # total storage bytes for this user's notebooks, notes, and revisions - self.__rate_plan = 0 # each rate plan is an integer index into the array in config/Common.py - - def __create_salt( self ): - return "".join( [ random.choice( self.SALT_CHARS ) for i in range( self.SALT_SIZE ) ] ) - - def __hash_password( self, password ): - if password is None or len( password ) == 0: - return None - - return sha.new( self.__salt + password ).hexdigest() - - def check_password( self, password ): - """ - Check that the given password matches this user's password. - - @type password: unicode - @param password: password to check - @rtype: bool - @return: True if the password matches - """ - if self.__password_hash == None: - return False - - hash = self.__hash_password( password ) - if hash == self.__password_hash: - return True - - return False - - def has_access( self, notebook_id ): - if notebook_id in [ notebook.object_id for notebook in self.__notebooks ]: - return True - - # a user who has read-write access to a notebook also has access to that notebook's trash - if notebook_id in [ notebook.trash.object_id for notebook in self.__notebooks if notebook.trash ]: - return True - - return False - - def to_dict( self ): - d = Persistent.to_dict( self ) - d.update( dict( - username = self.username, - storage_bytes = self.__storage_bytes, - rate_plan = self.__rate_plan, - ) ) - - return d - - def __set_email_address( self, email_address ): - self.update_revision() - self.__email_address = email_address - - def __set_password( self, password ): - self.update_revision() - self.__salt = self.__create_salt() - self.__password_hash = self.__hash_password( password ) - - def __set_notebooks( self, notebooks ): - self.update_revision() - self.__notebooks = notebooks - - def __set_storage_bytes( self, storage_bytes ): - self.update_revision() - self.__storage_bytes = storage_bytes - - def __set_rate_plan( self, rate_plan ): - self.update_revision() - self.__rate_plan = rate_plan - - username = property( lambda self: self.secondary_id ) - email_address = property( lambda self: self.__email_address, __set_email_address ) - password = property( None, __set_password ) - storage_bytes = property( lambda self: self.__storage_bytes, __set_storage_bytes ) - rate_plan = property( lambda self: self.__rate_plan, __set_rate_plan ) - - # the notebooks (read-only and read-write) that this user has access to - notebooks = property( lambda self: copy( self.__notebooks ), __set_notebooks ) diff --git a/model/User_list.py b/model/User_list.py deleted file mode 100644 index fc53683..0000000 --- a/model/User_list.py +++ /dev/null @@ -1,48 +0,0 @@ -from copy import copy -from Persistent import Persistent - - -class User_list( Persistent ): - """ - A list of users. - """ - def __init__( self, id, secondary_id = None ): - """ - Create a list of users, and give the list the provided id. - - @type id: unicode - @param id: id of the user list - @type secondary_id: unicode or NoneType - @param secondary_id: convenience id for easy access (optional) - @rtype: User_list - @return: newly constructed user list - """ - Persistent.__init__( self, id, secondary_id ) - self.__users = [] - - def add_user( self, user ): - """ - Add a user to this list. - - @type user: User - @param user: user to add - """ - if user.object_id not in [ u.object_id for u in self.__users ]: - self.update_revision() - self.__users.append( user ) - - def remove_user( self, user ): - """ - Remove a user from this list. - - @type user: User - @param user: user to remove - """ - if user in self.__users: - self.update_revision() - self.__users.remove( user ) - - def __set_users( self, users ): - self.__users = users - - users = property( lambda self: copy( self.__users ), __set_users ) diff --git a/model/__init__.py b/model/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/model/test/Test_note.py b/model/test/Test_note.py deleted file mode 100644 index 684dc91..0000000 --- a/model/test/Test_note.py +++ /dev/null @@ -1,87 +0,0 @@ -from model.Note import Note - - -class Test_note( object ): - def setUp( self ): - self.object_id = u"17" - self.title = u"title goes here" - self.contents = u"

%s

blah" % self.title - - self.note = Note( self.object_id, self.contents ) - - def test_create( self ): - assert self.note.object_id == self.object_id - assert self.note.contents == self.contents - assert self.note.title == self.title - assert self.note.deleted_from == None - - def test_create_blank( self ): - object_id = u"22" - blank_note = Note( object_id ) - - assert blank_note.object_id == object_id - assert blank_note.contents == None - assert blank_note.title == None - assert blank_note.deleted_from == None - - def test_set_contents( self ): - new_title = u"new title" - new_contents = u"

%s

new blah" % new_title - previous_revision = self.note.revision - - self.note.contents = new_contents - - assert self.note.contents == new_contents - assert self.note.title == new_title - assert self.note.deleted_from == None - assert self.note.revision > previous_revision - - def test_set_contents_with_html_title( self ): - new_title = u"new title" - new_contents = u"

new
title

new blah" - previous_revision = self.note.revision - - self.note.contents = new_contents - - # html should be stripped out of the title - assert self.note.contents == new_contents - assert self.note.title == new_title - assert self.note.deleted_from == None - assert self.note.revision > previous_revision - - def test_set_contents_with_multiple_titles( self ): - new_title = u"new title" - new_contents = u"

new
title

new blah

other title

hmm" - previous_revision = self.note.revision - - self.note.contents = new_contents - - # should only use the first title - assert self.note.contents == new_contents - assert self.note.title == new_title - assert self.note.deleted_from == None - assert self.note.revision > previous_revision - - def test_delete( self ): - previous_revision = self.note.revision - self.note.deleted_from = u"55" - - assert self.note.deleted_from == u"55" - assert self.note.revision > previous_revision - - def test_undelete( self ): - previous_revision = self.note.revision - self.note.deleted_from = None - - assert self.note.deleted_from == None - assert self.note.revision > previous_revision - - def test_to_dict( self ): - d = self.note.to_dict() - - assert d.get( "contents" ) == self.contents - assert d.get( "title" ) == self.title - assert d.get( "deleted_from" ) == None - assert d.get( "object_id" ) == self.note.object_id - assert d.get( "revision" ) - assert d.get( "revisions_list" ) diff --git a/model/test/Test_notebook.py b/model/test/Test_notebook.py deleted file mode 100644 index 899990a..0000000 --- a/model/test/Test_notebook.py +++ /dev/null @@ -1,177 +0,0 @@ -from nose.tools import raises -from model.Notebook import Notebook -from model.Note import Note - - -class Test_notebook( object ): - def setUp( self ): - self.object_id = "17" - self.trash_id = "18" - self.name = u"my notebook" - self.trash_name = u"trash" - - self.trash = Notebook( self.trash_id, self.trash_name ) - self.notebook = Notebook( self.object_id, self.name, self.trash ) - self.note = Note( "19", u"

title

blah" ) - - def test_create( self ): - assert self.notebook.object_id == self.object_id - assert self.notebook.name == self.name - assert self.notebook.trash - assert self.notebook.trash.object_id == self.trash_id - assert self.notebook.trash.name == self.trash_name - - def test_set_name( self ): - new_name = u"my new notebook" - previous_revision = self.notebook.revision - self.notebook.name = new_name - - assert self.notebook.name == new_name - assert self.notebook.revision > previous_revision - - def test_add_and_lookup_note( self ): - previous_revision = self.notebook.revision - self.notebook.add_note( self.note ) - assert self.notebook.revision > previous_revision - - note = self.notebook.lookup_note( self.note.object_id ) - assert note == self.note - - def test_lookup_unknown_note( self ): - note = self.notebook.lookup_note( self.note.object_id ) - assert note == None - - def test_add_and_lookup_note_by_title( self ): - previous_revision = self.notebook.revision - self.notebook.add_note( self.note ) - assert self.notebook.revision > previous_revision - - note = self.notebook.lookup_note_by_title( self.note.title ) - assert note == self.note - - def test_lookup_unknown_note_by_title( self ): - note = self.notebook.lookup_note( self.note.title ) - assert note == None - - def test_remove_note( self ): - previous_revision = self.notebook.revision - self.notebook.add_note( self.note ) - result = self.notebook.remove_note( self.note ) - assert result == True - assert self.notebook.revision > previous_revision - - note = self.notebook.lookup_note( self.note.object_id ) - assert note == None - - note = self.notebook.lookup_note_by_title( self.note.title ) - assert note == None - - assert not note in self.notebook.startup_notes - - def test_remove_unknown_note( self ): - revision = self.notebook.revision - result = self.notebook.remove_note( self.note ) - assert result == False - assert self.notebook.revision == revision - - note = self.notebook.lookup_note( self.note.object_id ) - assert note == None - - def test_update_note( self ): - self.notebook.add_note( self.note ) - old_title = self.note.title - - new_title = u"new title" - new_contents = u"

%s

new blah" % new_title - previous_revision = self.notebook.revision - self.notebook.update_note( self.note, new_contents ) - - assert self.note.contents == new_contents - assert self.note.title == new_title - assert self.notebook.revision > previous_revision - - note = self.notebook.lookup_note( self.note.object_id ) - assert note == self.note - - note = self.notebook.lookup_note_by_title( old_title ) - assert note == None - - note = self.notebook.lookup_note_by_title( new_title ) - assert note == self.note - - @raises( Notebook.UnknownNoteError ) - def test_update_unknown_note( self ): - new_contents = u"

new title

new blah" - self.notebook.update_note( self.note, new_contents ) - - def test_add_startup_note( self ): - self.notebook.add_note( self.note ) - - previous_revision = self.notebook.revision - self.notebook.add_startup_note( self.note ) - - assert self.note in self.notebook.startup_notes - assert self.notebook.revision > previous_revision - - def test_add_duplicate_startup_note( self ): - self.notebook.add_note( self.note ) - - previous_revision = self.notebook.revision - self.notebook.add_startup_note( self.note ) - - assert self.note in self.notebook.startup_notes - assert self.notebook.revision > previous_revision - - revision = self.notebook.revision - self.notebook.add_startup_note( self.note ) - - assert self.notebook.startup_notes.count( self.note ) == 1 - assert self.notebook.revision == revision - - @raises( Notebook.UnknownNoteError ) - def test_add_unknown_startup_note( self ): - self.notebook.add_startup_note( self.note ) - - def test_remove_startup_note( self ): - self.notebook.add_note( self.note ) - self.notebook.add_startup_note( self.note ) - - previous_revision = self.notebook.revision - result = self.notebook.remove_startup_note( self.note ) - - assert result == True - assert not self.note in self.notebook.startup_notes - assert self.notebook.revision > previous_revision - - def test_remove_unknown_startup_note( self ): - self.notebook.add_note( self.note ) - - revision = self.notebook.revision - result = self.notebook.remove_startup_note( self.note ) - - assert result == False - assert not self.note in self.notebook.startup_notes - assert self.notebook.revision == revision - - def test_to_dict( self ): - d = self.notebook.to_dict() - - assert d.get( "name" ) == self.name - assert d.get( "trash" ) == self.trash - assert d.get( "read_write" ) == True - assert d.get( "object_id" ) == self.notebook.object_id - assert d.get( "revision" ) - assert d.get( "revisions_list" ) == None - - def test_to_dict_with_startup_notes( self ): - self.notebook.add_note( self.note ) - self.notebook.add_startup_note( self.note ) - - d = self.notebook.to_dict() - - assert d.get( "name" ) == self.name - assert d.get( "trash" ) == self.trash - assert d.get( "read_write" ) == True - assert d.get( "object_id" ) == self.notebook.object_id - assert d.get( "revision" ) - assert d.get( "revisions_list" ) == None diff --git a/model/test/Test_password_reset.py b/model/test/Test_password_reset.py deleted file mode 100644 index 9e953c3..0000000 --- a/model/test/Test_password_reset.py +++ /dev/null @@ -1,38 +0,0 @@ -from model.User import User -from model.Password_reset import Password_reset - - -class Test_password_reset( object ): - def setUp( self ): - self.object_id = u"17" - self.email_address = u"bob@example.com" - - self.password_reset = Password_reset( self.object_id, self.email_address ) - - def test_create( self ): - assert self.password_reset.object_id == self.object_id - assert self.password_reset.email_address == self.email_address - assert self.password_reset.redeemed == False - - def test_redeem( self ): - previous_revision = self.password_reset.revision - self.password_reset.redeemed = True - - assert self.password_reset.redeemed == True - assert self.password_reset.revision > previous_revision - - def test_redeem_twice( self ): - self.password_reset.redeemed = True - current_revision = self.password_reset.revision - self.password_reset.redeemed = True - - assert self.password_reset.redeemed == True - assert self.password_reset.revision == current_revision - - def test_unredeem( self ): - self.password_reset.redeemed = True - previous_revision = self.password_reset.revision - self.password_reset.redeemed = False - - assert self.password_reset.redeemed == False - assert self.password_reset.revision > previous_revision diff --git a/model/test/Test_persistent.py b/model/test/Test_persistent.py deleted file mode 100644 index 9fa651f..0000000 --- a/model/test/Test_persistent.py +++ /dev/null @@ -1,51 +0,0 @@ -from datetime import datetime, timedelta -from model.Persistent import Persistent - - -class Test_persistent( object ): - def setUp( self ): - self.object_id = "17" - self.obj = Persistent( self.object_id ) - self.delta = timedelta( seconds = 1 ) - - def test_create( self ): - assert self.obj.object_id == self.object_id - assert self.obj.secondary_id == None - assert datetime.now() - self.obj.revision < self.delta - - def test_revision_id( self ): - assert self.obj.revision_id() == "%s %s" % ( self.object_id, self.obj.revision ) - - def test_make_revision_id( self ): - assert self.obj.revision_id() == Persistent.make_revision_id( self.object_id, self.obj.revision ) - - def test_update_revision( self ): - previous_revision = self.obj.revision - self.obj.update_revision() - assert self.obj.revision > previous_revision - assert datetime.now() - self.obj.revision < self.delta - - previous_revision = self.obj.revision - self.obj.update_revision() - assert self.obj.revision > previous_revision - assert datetime.now() - self.obj.revision < self.delta - - def test_to_dict( self ): - d = self.obj.to_dict() - - assert d.get( "object_id" ) == self.object_id - assert d.get( "revision" ) == self.obj.revision - assert d.get( "revisions_list" ) == self.obj.revisions_list - - -class Test_persistent_with_secondary_id( object ): - def setUp( self ): - self.object_id = "17" - self.secondary_id = u"foo" - self.obj = Persistent( self.object_id, self.secondary_id ) - self.delta = timedelta( seconds = 1 ) - - def test_create( self ): - assert self.obj.object_id == self.object_id - assert self.obj.secondary_id == self.secondary_id - assert datetime.now() - self.obj.revision < self.delta diff --git a/model/test/Test_read_only_notebook.py b/model/test/Test_read_only_notebook.py deleted file mode 100644 index d3bc062..0000000 --- a/model/test/Test_read_only_notebook.py +++ /dev/null @@ -1,78 +0,0 @@ -from nose.tools import raises -from model.Notebook import Notebook -from model.Read_only_notebook import Read_only_notebook -from model.Note import Note - - -class Test_read_only_notebook( object ): - def setUp( self ): - self.object_id = "17" - self.read_only_id = "22" - self.trash_id = 18 - self.name = u"my notebook" - self.trash_name = u"trash" - - self.trash = Notebook( self.trash_id, self.trash_name ) - self.notebook = Notebook( self.object_id, self.name, self.trash ) - self.note = Note( "19", u"

title

blah" ) - self.notebook.add_note( self.note ) - self.notebook.add_startup_note( self.note ) - - self.read_only = Read_only_notebook( self.read_only_id, self.notebook ) - - def test_create( self ): - assert self.read_only.object_id == self.read_only_id - assert self.read_only.name == self.name - assert self.read_only.trash == None - assert self.read_only.notes == [ self.note ] - assert self.read_only.startup_notes == [ self.note ] - - @raises( AttributeError ) - def test_set_name( self ): - self.read_only.name = u"my new notebook" - - @raises( AttributeError ) - def test_add_note( self ): - self.read_only.add_note( self.note ) - - def test_lookup_note( self ): - note = self.read_only.lookup_note( self.note.object_id ) - assert note == self.note - - def test_lookup_unknown_note( self ): - note = self.read_only.lookup_note( "55" ) - assert note == None - - def test_lookup_note_by_title( self ): - note = self.read_only.lookup_note_by_title( self.note.title ) - assert note == self.note - - def test_lookup_unknown_note_by_title( self ): - note = self.read_only.lookup_note( self.note.title ) - assert note == None - - @raises( AttributeError ) - def test_remove_note( self ): - self.read_only.remove_note( self.note ) - - @raises( AttributeError ) - def test_update_note( self ): - new_title = u"new title" - new_contents = u"

%s

new blah" % new_title - self.read_only.update_note( self.note, new_contents ) - - @raises( AttributeError ) - def test_add_startup_note( self ): - self.read_only.add_startup_note( self.note ) - - @raises( AttributeError ) - def test_remove_startup_note( self ): - self.read_only.remove_startup_note( self.note ) - - def test_to_dict( self ): - d = self.read_only.to_dict() - - assert d.get( "object_id" ) == self.read_only_id - assert d.get( "name" ) == self.name - assert d.get( "trash" ) == None - assert d.get( "read_write" ) == False diff --git a/model/test/Test_user.py b/model/test/Test_user.py deleted file mode 100644 index 6522caa..0000000 --- a/model/test/Test_user.py +++ /dev/null @@ -1,134 +0,0 @@ -from nose.tools import raises -from model.User import User -from model.Notebook import Notebook - - -class Test_user( object ): - def setUp( self ): - self.object_id = u"17" - self.username = u"bob" - self.password = u"foobar" - self.email_address = u"bob@example.com" - - self.user = User( self.object_id, self.username, self.password, self.email_address ) - - def test_create( self ): - assert self.user.username == self.username - assert self.user.email_address == self.email_address - assert self.user.notebooks == [] - assert self.user.storage_bytes == 0 - assert self.user.rate_plan == 0 - - def test_check_correct_password( self ): - assert self.user.check_password( self.password ) == True - - def test_check_incorrect_password( self ): - assert self.user.check_password( u"wrong" ) == False - - def test_set_password( self ): - previous_revision = self.user.revision - new_password = u"newpass" - self.user.password = new_password - - assert self.user.check_password( self.password ) == False - assert self.user.check_password( new_password ) == True - assert self.user.revision > previous_revision - - def test_set_none_password( self ): - previous_revision = self.user.revision - new_password = None - self.user.password = new_password - - assert self.user.check_password( self.password ) == False - assert self.user.check_password( new_password ) == False - assert self.user.revision > previous_revision - - def test_set_notebooks( self ): - previous_revision = self.user.revision - notebook_id = u"33" - notebook = Notebook( notebook_id, u"my notebook" ) - self.user.notebooks = [ notebook ] - - assert len( self.user.notebooks ) == 1 - assert self.user.notebooks[ 0 ].object_id == notebook_id - assert self.user.revision > previous_revision - - def test_set_storage_bytes( self ): - previous_revision = self.user.revision - storage_bytes = 44 - self.user.storage_bytes = storage_bytes - - assert self.user.storage_bytes == storage_bytes - assert self.user.revision > previous_revision - - def test_set_rate_plan( self ): - previous_revision = self.user.revision - rate_plan = 2 - self.user.rate_plan = rate_plan - - assert self.user.rate_plan == rate_plan - assert self.user.revision > previous_revision - - -class Test_user_with_notebooks( object ): - def setUp( self ): - self.object_id = u"17" - self.username = u"bob" - self.password = u"foobar" - self.email_address = u"bob@example.com" - trash = Notebook( u"32", u"trash" ) - - self.notebooks = [ - Notebook( u"33", u"my notebook", trash ), - Notebook( u"34", u"my other notebook" ), - ] - - self.user = User( self.object_id, self.username, self.password, self.email_address, self.notebooks ) - - def test_create( self ): - assert self.user.username == self.username - assert self.user.email_address == self.email_address - assert self.user.notebooks == self.notebooks - - def test_set_existing_notebooks( self ): - previous_revision = self.user.revision - self.user.notebooks = [ self.notebooks[ 1 ] ] - - assert len( self.user.notebooks ) == 1 - assert self.user.notebooks[ 0 ].object_id == self.notebooks[ 1 ].object_id - assert self.user.revision > previous_revision - - def test_set_new_notebooks( self ): - previous_revision = self.user.revision - notebook_id = u"35" - notebook = Notebook( notebook_id, u"my new notebook" ) - self.user.notebooks = [ notebook ] - - assert len( self.user.notebooks ) == 1 - assert self.user.notebooks[ 0 ].object_id == notebook_id - assert self.user.revision > previous_revision - - def test_has_access_true( self ): - assert self.user.has_access( self.notebooks[ 0 ].object_id ) == True - - def test_has_access_false( self ): - notebook_id = u"35" - notebook = Notebook( notebook_id, u"my new notebook" ) - assert self.user.has_access( notebook.object_id ) == False - - def test_has_access_to_trash_true( self ): - assert self.user.has_access( self.notebooks[ 0 ].trash.object_id ) == True - - def test_has_access_to_trash_false( self ): - notebook_id = u"35" - trash_id = u"36" - trash = Notebook( trash_id, u"trash" ) - notebook = Notebook( notebook_id, u"my new notebook", trash ) - assert self.user.has_access( notebook.object_id ) == False - - def test_to_dict( self ): - d = self.user.to_dict() - - assert d.get( "username" ) == self.username - assert d.get( "storage_bytes" ) == self.user.storage_bytes - assert d.get( "rate_plan" ) == self.user.rate_plan diff --git a/model/test/Test_user_list.py b/model/test/Test_user_list.py deleted file mode 100644 index f7a1885..0000000 --- a/model/test/Test_user_list.py +++ /dev/null @@ -1,57 +0,0 @@ -from model.User import User -from model.User_list import User_list - - -class Test_user_list( object ): - def setUp( self ): - self.object_id = u"17" - self.secondary_id = u"mylist" - - self.user_list = User_list( self.object_id, self.secondary_id ) - self.user = User( u"18", u"bob", u"pass", u"bob@example.com" ) - self.user2 = User( u"19", u"rob", u"pass2", u"rob@example.com" ) - - def test_create( self ): - assert self.user_list.object_id == self.object_id - assert self.user_list.secondary_id == self.secondary_id - assert self.user_list.users == [] - - def test_add_user( self ): - previous_revision = self.user_list.revision - self.user_list.add_user( self.user ) - - assert self.user_list.users == [ self.user ] - assert self.user_list.revision > previous_revision - - def test_add_user_twice( self ): - self.user_list.add_user( self.user ) - current_revision = self.user_list.revision - self.user_list.add_user( self.user ) - - assert self.user_list.users == [ self.user ] - assert self.user_list.revision == current_revision - - def test_add_two_users( self ): - previous_revision = self.user_list.revision - self.user_list.add_user( self.user ) - self.user_list.add_user( self.user2 ) - - assert self.user_list.users == [ self.user, self.user2 ] - assert self.user_list.revision > previous_revision - - def test_remove_user( self ): - self.user_list.add_user( self.user ) - previous_revision = self.user_list.revision - self.user_list.remove_user( self.user ) - - assert self.user_list.users == [] - assert self.user_list.revision > previous_revision - - def test_remove_user_twice( self ): - self.user_list.add_user( self.user ) - self.user_list.remove_user( self.user ) - current_revision = self.user_list.revision - self.user_list.remove_user( self.user ) - - assert self.user_list.users == [] - assert self.user_list.revision == current_revision diff --git a/new_model/test/Test_password_reset.py b/new_model/test/Test_password_reset.py index da0c6bb..80c2b4f 100644 --- a/new_model/test/Test_password_reset.py +++ b/new_model/test/Test_password_reset.py @@ -1,4 +1,4 @@ -from model.User import User +from new_model.User import User from new_model.Password_reset import Password_reset diff --git a/tools/convertdb.py b/tools/convertdb.py deleted file mode 100755 index a2129b9..0000000 --- a/tools/convertdb.py +++ /dev/null @@ -1,167 +0,0 @@ -#!/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() diff --git a/tools/verifyconvertdb.py b/tools/verifyconvertdb.py deleted file mode 100755 index 8e2dcf9..0000000 --- a/tools/verifyconvertdb.py +++ /dev/null @@ -1,186 +0,0 @@ -#!/usr/bin/python2.5 - -import os -import os.path -import psycopg2 as psycopg -from controller.Old_database import Old_database -from controller.Scheduler import Scheduler - - -def quote( value ): - if value is None: - return "null" - - value = unicode( value ) - return "'%s'" % value.replace( "'", "''" ).replace( "\\", "\\\\" ) - - -class Verifier( object ): - """ - Verifies a conversion of a Luminotes database from bsddb to PostgreSQL that was performed with - convertdb.py. - """ - 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.verify_database() - self.scheduler.add( thread ) - self.scheduler.wait_for( thread ) - - def verify_database( self ): - inserts = set() - - 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": - self.verify_notebook( value ) - elif class_name == "Note": - self.cursor.execute( - "select * from note where id = %s and revision = %s;" % ( quote( value.object_id ), quote( value.revision ) ) - ) - - for row in self.cursor.fetchmany(): - assert row[ 0 ] == value.object_id - assert row[ 1 ].replace( tzinfo = None ) == value.revision - assert row[ 2 ] == ( value.title and value.title.encode( "utf8" ) or None ) - assert row[ 3 ] == ( value.contents and value.contents.encode( "utf8" ) or None ) - # not checking for existence of row 4 (notebook_id), because notes deleted from the trash don't have a notebook id - assert row[ 5 ] is not None - assert row[ 6 ] == ( value.deleted_from or None ) - if row[ 5 ] is True: # if this is a startup note, it should have a rank - assert row[ 7 ] is not None - elif class_name == "User": - # skip demo users - if value.username is None: continue - - self.cursor.execute( - "select * from luminotes_user where id = %s and revision = %s;" % ( quote( value.object_id ), quote( value.revision ) ) - ) - - for row in self.cursor.fetchmany(): - assert row[ 0 ] == value.object_id - assert row[ 1 ].replace( tzinfo = None ) == value.revision - assert row[ 2 ] == value.username - assert row[ 3 ] == value._User__salt - assert row[ 4 ] == value._User__password_hash - assert row[ 5 ] == value.email_address - assert row[ 6 ] == value.storage_bytes - assert row[ 7 ] == value.rate_plan - - for notebook in value.notebooks: - if notebook is None: continue - - read_write = ( notebook.__class__.__name__ == "Notebook" ) - - self.cursor.execute( - "select * from user_notebook where user_id = %s and notebook_id = %s;" % ( quote( value.object_id ), quote( notebook.object_id ) ) - ) - - for row in self.cursor.fetchmany(): - assert row[ 0 ] == value.object_id - assert row[ 1 ] == notebook.object_id - assert row[ 2 ] == read_write - - if notebook.trash: - self.cursor.execute( - "select * from user_notebook where user_id = %s and notebook_id = %s;" % ( quote( value.object_id ), quote( notebook.trash.object_id ) ) - ) - - for row in self.cursor.fetchmany(): - assert row[ 0 ] == value.object_id - assert row[ 1 ] == notebook.trash.object_id - assert row[ 2 ] == read_write - - self.verify_notebook( notebook ) - - elif class_name == "Read_only_notebook": - self.verify_notebook( value._Read_only_notebook__wrapped ) - elif class_name == "Password_reset": - # skip password resets that are already redeemed - if value.redeemed: continue - - self.cursor.execute( - "select * from password_reset where id = %s;" % quote( value.object_id ) - ) - - for row in self.cursor.fetchmany(): - assert row[ 0 ] == value.object_id - assert row[ 1 ] == value.email_address - assert row[ 2 ] == False - elif class_name == "User_list": - pass - else: - raise Exception( "Unverified value of type %s" % class_name ) - - self.conn.commit() - yield None - - def verify_notebook( self, value ): - self.cursor.execute( - "select * from notebook where id = %s and revision = %s;" % ( quote( value.object_id ), quote( value.revision ) ) - ) - - for row in self.cursor.fetchmany(): - assert row[ 0 ] == value.object_id - assert row[ 1 ].replace( tzinfo = None ) == value.revision - assert row[ 2 ] == value.name - if value.trash: - assert row[ 3 ] == value.trash.object_id - else: - assert row[ 3 ] == None - - startup_note_ids = [ note.object_id for note in value.startup_notes ] - for note in value.notes: - self.cursor.execute( - "select * from note where id = %s and revision = %s;" % ( quote( note.object_id ), quote( value.revision ) ) - ) - - for row in self.cursor.fetchmany(): - assert row[ 0 ] == note.object_id - assert row[ 1 ].replace( tzinfo = None ) == note.revision - assert row[ 2 ] == note.title - assert row[ 3 ] == note.contents - assert row[ 4 ] == value.object_id - assert row[ 5 ] == ( note.object_id in startup_note_ids ) - assert row[ 6 ] == note.deleted_from - if row[ 5 ] is True: # if this is a startup note, it should have a rank - assert row[ 7 ] is not None - - for note in value.startup_notes: - self.cursor.execute( - "select * from note where id = %s and revision = %s order by rank;" % ( quote( note.object_id ), quote( value.revision ) ) - ) - - rank = 0 - for row in self.cursor.fetchmany(): - assert row[ 0 ] == note.object_id - assert row[ 1 ].replace( tzinfo = None ) == note.revision - assert row[ 2 ] == note.title - assert row[ 3 ] == note.contents - assert row[ 4 ] == value.object_id - assert row[ 5 ] == True - assert row[ 6 ] == note.deleted_from - assert row[ 7 ] == rank - rank += 1 - - -def main(): - scheduler = Scheduler() - database = Old_database( scheduler, "data.db" ) - initializer = Verifier( scheduler, database ) - scheduler.wait_until_idle() - - -if __name__ == "__main__": - main()