witten
/
luminotes
Archived
1
0
Fork 0

Fixed several edge cases with re-ranking notes during a reorder. Now updating several notes' ranks on every reorder instead of making successively more precise fractional ranks.

This commit is contained in:
Dan Helfman 2009-02-10 17:05:03 -08:00
parent 5446eeec5d
commit a8af8b3fb3
6 changed files with 193 additions and 10 deletions

View File

@ -391,6 +391,13 @@ class Database( object ):
cache.delete( obj.cache_key )
def uncache_many( self, Object_type, obj_ids ):
cache = self.__get_cache_connection()
if not cache: return
for obj_id in obj_ids:
cache.delete( Persistent.make_cache_key( Object_type, obj_id ) )
@staticmethod
def generate_id():
int_id = random.getrandbits( Database.ID_BITS )

View File

@ -726,12 +726,26 @@ class Notebooks( object ):
position_before = None
position_after = None
def calculate_rank( position_after, position_before ):
def update_rank( position_after, position_before ):
after_note = position_after and self.__database.load( Note, position_after ) or None
before_note = position_before and self.__database.load( Note, position_before ) or None
if after_note and before_note:
return ( float( after_note.rank ) + float( before_note.rank ) ) / 2.0
new_rank = float( after_note.rank ) + 1.0
# if necessary, increment the rank of all subsequent notes to make "room" for this note
if new_rank >= before_note.rank:
# clear the cache of before_note and all notes with subsequent rank
self.__database.uncache_many(
Note,
self.__database.select_many(
unicode,
notebook.sql_load_note_ids_starting_from_rank( before_note.rank )
)
)
self.__database.execute( notebook.sql_increment_rank( before_note.rank ), commit = False )
return new_rank
elif after_note:
return float( after_note.rank ) + 1.0
elif before_note:
@ -750,7 +764,7 @@ class Notebooks( object ):
note.startup = startup
if position_after or position_before:
note.rank = calculate_rank( position_after, position_before )
note.rank = update_rank( position_after, position_before )
elif note.rank is None:
note.rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1
@ -783,7 +797,7 @@ class Notebooks( object ):
# otherwise, create a new note
else:
if position_after or position_before:
rank = calculate_rank( position_after, position_before )
rank = update_rank( position_after, position_before )
else:
rank = self.__database.select_one( float, notebook.sql_highest_note_rank() ) + 1

View File

@ -1987,7 +1987,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == 0.5
assert result[ "rank" ] == 1.0
# make sure the old title can no longer be loaded
result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2011,7 +2011,7 @@ class Test_notebooks( Test_controller ):
assert note.contents == new_note_contents
assert note.startup == False
assert note.user_id == self.user.object_id
assert note.rank == 0.5
assert note.rank == 1.0
# make sure that the correct revisions are returned and are in chronological order
result = self.http_post( "/notebooks/load_note_revisions/", dict(
@ -2029,6 +2029,15 @@ class Test_notebooks( Test_controller ):
assert revisions[ 2 ].user_id == self.user.object_id
assert revisions[ 2 ].username == self.username
# the position_before note should have its rank incremented to make way for the new note
result = self.http_post( "/notebooks/load_note/", dict(
notebook_id = self.notebook.object_id,
note_id = before_note_id,
), session_id = self.session_id )
note = result[ "note" ]
assert note.rank == 2.0
def test_save_note_in_notebook_with_read_write_for_own_notes( self, after_note_id = None, before_note_id = None ):
self.login()
@ -2914,7 +2923,7 @@ class Test_notebooks( Test_controller ):
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == 0.5
assert result[ "rank" ] == 1.0
# make sure the new title is now loadable
result = self.http_post( "/notebooks/load_note_by_title/", dict(
@ -2929,7 +2938,108 @@ class Test_notebooks( Test_controller ):
assert note.contents == new_note.contents
assert note.startup == False
assert note.user_id == self.user.object_id
assert note.rank == 0.5
assert note.rank == 1.0
# the position_before note should have its rank incremented to make way for the new note
result = self.http_post( "/notebooks/load_note/", dict(
notebook_id = self.notebook.object_id,
note_id = before_note_id,
), session_id = self.session_id )
note = result[ "note" ]
assert note.rank == 2.0
def test_save_new_note_with_position_after_and_before_and_gap( self ):
self.login()
temp_note_id = u"someid0"
new_note_contents = u"<h3>temp</h3>"
result = self.http_post( "/notebooks/save_note/", dict(
notebook_id = self.notebook.object_id,
note_id = temp_note_id,
contents = new_note_contents,
startup = False,
previous_revision = None,
), session_id = self.session_id )
assert result[ "rank" ] == 0.0
after_note_id = u"someid1"
new_note_contents = u"<h3>after this</h3>"
result = self.http_post( "/notebooks/save_note/", dict(
notebook_id = self.notebook.object_id,
note_id = after_note_id,
contents = new_note_contents,
startup = False,
previous_revision = None,
position_before = temp_note_id
), session_id = self.session_id )
assert result[ "rank" ] == -1.0
before_note_id = u"someid2"
new_note_contents = u"<h3>before this</h3>"
result = self.http_post( "/notebooks/save_note/", dict(
notebook_id = self.notebook.object_id,
note_id = before_note_id,
contents = new_note_contents,
startup = False,
previous_revision = None,
), session_id = self.session_id )
assert result[ "rank" ] == 1.0
self.http_post( "/notebooks/delete_note/", dict(
notebook_id = self.notebook.object_id,
note_id = temp_note_id,
), session_id = self.session_id )
# save a completely new note
new_note = Note.create( "55", u"<h3>newest title</h3>foo" )
previous_revision = new_note.revision
result = self.http_post( "/notebooks/save_note/", dict(
notebook_id = self.notebook.object_id,
note_id = new_note.object_id,
contents = new_note.contents,
startup = False,
previous_revision = None,
position_after = after_note_id,
position_before = before_note_id,
), session_id = self.session_id )
assert result[ "new_revision" ]
assert result[ "new_revision" ] != previous_revision
assert result[ "new_revision" ].user_id == self.user.object_id
assert result[ "new_revision" ].username == self.username
assert result[ "previous_revision" ] == None
user = self.database.load( User, self.user.object_id )
assert user.storage_bytes > 0
assert result[ "storage_bytes" ] == user.storage_bytes
assert result[ "rank" ] == 0.0
# make sure the new title is now loadable
result = self.http_post( "/notebooks/load_note_by_title/", dict(
notebook_id = self.notebook.object_id,
note_title = new_note.title,
), session_id = self.session_id )
note = result[ "note" ]
assert note.object_id == new_note.object_id
assert note.title == new_note.title
assert note.contents == new_note.contents
assert note.startup == False
assert note.user_id == self.user.object_id
assert note.rank == 0.0
# the position_before note should have its rank incremented to make way for the new note
result = self.http_post( "/notebooks/load_note/", dict(
notebook_id = self.notebook.object_id,
note_id = before_note_id,
), session_id = self.session_id )
note = result[ "note" ]
assert note.rank == 1.0
def test_save_new_note_in_notebook_with_read_write_for_own_notes( self, after_note_id = None, before_note_id = None ):
self.login()

View File

@ -2,6 +2,8 @@ import re
from copy import copy
from Note import Note
from Persistent import Persistent, quote, quote_fuzzy
from datetime import datetime
from pytz import utc
class Notebook( Persistent ):
@ -357,6 +359,41 @@ class Notebook( Persistent ):
order by tag.name;
""" % ( quote( self.object_id ), quote( user_id ) )
def sql_load_note_ids_starting_from_rank( self, start_note_rank ):
"""
Return a SQL string to load a list of all the note ids with rank greater than or equal to the
given rank.
"""
return \
"""
select
id
from
note_current
where
notebook_id = %s and
rank is not null and
rank >= %s;
""" % ( quote( self.object_id ), start_note_rank )
def sql_increment_rank( self, start_note_rank ):
"""
Return a SQL string to increment the rank for every note in this notebook (in rank order)
starting from the given note rank. Notes before the given note rank are not updated.
"""
return \
"""
update
note_current
set
rank = rank + 1,
revision = %s
where
notebook_id = %s and
rank is not null and
rank >= %s;
""" % ( quote( datetime.now( tz = utc ) ), quote( self.object_id ), start_note_rank )
def to_dict( self ):
d = Persistent.to_dict( self )

View File

@ -332,8 +332,12 @@ Editor.prototype.add_selection_bookmark = function () {
// if the current range is not within this editor's static note div, then bail
if ( range.startContainer == document || range.endContainer == document )
return null;
if ( !isChildNode( range.startContainer.parentNode, this.div ) || !isChildNode( range.endContainer.parentNode, this.div ) )
try {
if ( !isChildNode( range.startContainer.parentNode, this.div ) || !isChildNode( range.endContainer.parentNode, this.div ) )
return null;
} catch ( e ) {
return null;
}
// mark the nodes that are start and end containers for the current range. we have to mark the
// parent node instead of the start/end container itself, because text nodes can't have classes

View File

@ -1046,6 +1046,16 @@ Wiki.prototype.editor_focused = function ( editor, synchronous, remove_empty ) {
Wiki.prototype.editor_moved = function ( editor, position_after, position_before ) {
this.save_editor( editor, false, null, null, null, position_after, position_before );
// reset the revision for each open editor. this is because the server is updating the revisions
// on the server while reordering the ntoes. and we don't want to have a stale idea of what the
// current revision is for a given editor
var divs = getElementsByTagAndClassName( "div", "static_note_div" );
for ( var i in divs ) {
var editor = divs[ i ].editor;
editor.revision = null;
editor.user_revisions = new Array();
}
}
Wiki.prototype.make_byline = function ( username, creation, note_id ) {
@ -1760,7 +1770,8 @@ Wiki.prototype.update_editor_revisions = function ( result, editor ) {
// if the server's idea of the previous revision doesn't match the client's, then someone has
// gone behind our back and saved the editor's note from another window
if ( result.previous_revision && result.previous_revision.revision != client_previous_revision ) {
if ( result.previous_revision && client_previous_revision &&
result.previous_revision.revision != client_previous_revision ) {
var compare_button = createDOM( "input", {
"type": "button",
"class": "message_button",