Contentratings includes a migration mechanism to allow for changes of
the storage mechanism used by a given rating category.  This is a
generic mechanism which adapts an old storage instance and a new
(empty) storage instance.  Moving the data from one to the other.

The package includes two migrators for the contentratings <=0.3 storages.
The UserRating storage was a simple BTree stored directly in an annotation.
Let's see how one of those would be migrated.  First we create one of
these legacy storages:


    >>> from BTrees.OOBTree import OOBTree
    >>> orig_storage = OOBTree()
    >>> orig_storage['anon_average'] = 0.0
    >>> orig_ratings = orig_storage['ratings'] = OOBTree()
    >>> orig_ratings['user1'] = 6
    >>> orig_ratings['user2'] = 8

Now we need an empty instance of UserRatingStorage to migrate the data to::

    >>> from contentratings.storage import UserRatingStorage
    >>> new = UserRatingStorage()
    >>> from contentratings.migrations import UserRatingMigrator
    >>> migrator = UserRatingMigrator(orig_storage, new)
    >>> migrated = migrator.migrate()
    >>> migrated.averageRating
    7.0
    >>> migrated.numberOfRatings
    2
    >>> ratings = list(migrated.all_user_ratings())
    >>> ratings[0].userid
    'user1'
    >>> float(ratings[0])
    6.0
    >>> ratings[1].userid
    'user2'
    >>> float(ratings[1])
    8.0
    
The migrator also handles anonymous ratings.  In early versions, these
were stored individually.  We add three anonymous ratings::

    >>> orig_storage['anon_ratings'] = [7,8,9]
    >>> new = UserRatingStorage()
    >>> migrator = UserRatingMigrator(orig_storage, new)
    >>> migrated = migrator.migrate()
    >>> migrated.averageRating
    7.5999999999999996
    >>> migrated.numberOfRatings
    5
    >>> ratings = list(migrated.all_user_ratings(True))
    >>> ratings[0].userid
    'user1'
    >>> float(ratings[0])
    6.0
    >>> ratings[1].userid
    'user2'
    >>> float(ratings[1])
    8.0
    >>> ratings[2].userid
    >>> float(ratings[2])
    7.0
    >>> ratings[3].userid
    >>> float(ratings[3])
    8.0
    >>> ratings[4].userid
    >>> float(ratings[4])
    9.0
    
The newer legacy rating storage simply stores a count and an average.
The migrator stores this as a sequence of anonymous ratings each
having the average value::

    >>> del orig_storage['anon_ratings']
    >>> orig_storage['anon_count'] = 2
    >>> orig_storage['anon_average'] = 4.0
    >>> new = UserRatingStorage()
    >>> migrator = UserRatingMigrator(orig_storage, new)
    >>> migrated = migrator.migrate()
    >>> migrated.averageRating
    5.5
    >>> migrated.numberOfRatings
    4
    >>> ratings = list(migrated.all_user_ratings(True))
    >>> ratings[2].userid
    >>> float(ratings[2])
    4.0
    >>> ratings[3].userid
    >>> float(ratings[3])
    4.0

There is also a migrator for the Editorial rating, which was just a
simple attribute::

    >>> orig = 5.5
    >>> from contentratings.storage import EditorialRatingStorage
    >>> new = EditorialRatingStorage()
    >>> from contentratings.migrations import EditorialRatingMigrator
    >>> migrator = EditorialRatingMigrator(orig, new)
    >>> migrated = migrator.migrate()
    >>> float(migrated.rating)
    5.5
