From: tla Date: Tue, 12 Nov 2013 10:38:02 +0000 (+0100) Subject: Implement JSON call to re-root stemma. #29 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=6aabefa3b8ad3b5746b5e8d857e3992b9abdf3da;p=scpubgit%2Fstemmaweb.git Implement JSON call to re-root stemma. #29 --- diff --git a/lib/stemmaweb/Controller/Root.pm b/lib/stemmaweb/Controller/Root.pm index 0315867..eebf21a 100644 --- a/lib/stemmaweb/Controller/Root.pm +++ b/lib/stemmaweb/Controller/Root.pm @@ -249,7 +249,7 @@ sub textinfo :Local :Args(1) { $tradition->name( $newname ); $changed = 1; } catch { - return _json_error( $c, 500, "Error setting name to $newname" ); + return _json_error( $c, 500, "Error setting name to $newname: $@" ); } } } @@ -261,7 +261,7 @@ sub textinfo :Local :Args(1) { $tradition->language( $langval ); $changed = 1; } catch { - return _json_error( $c, 500, "Error setting language to $langval" ); + return _json_error( $c, 500, "Error setting language to $langval: $@" ); } } @@ -318,18 +318,14 @@ sub textinfo :Local :Args(1) { owner => $tradition->user ? $tradition->user->email : undef, witnesses => [ map { $_->sigil } $tradition->witnesses ], }; + ## TODO Make these into callbacks in the other controllers maybe? if( $tradition->can('language') ) { $textinfo->{'language'} = $tradition->language; } if( $tradition->can('stemweb_jobid') ) { $textinfo->{'stemweb_jobid'} = $tradition->stemweb_jobid || 0; } - my @stemmasvg = map { { - name => $_->identifier, - directed => _json_bool( !$_->is_undirected ), - svg => $_->as_svg() } } - $tradition->stemmata; - map { $_ =~ s/\n/ /mg } @stemmasvg; + my @stemmasvg = map { _stemma_info( $_ ) } $tradition->stemmata; $textinfo->{stemmata} = \@stemmasvg; $c->stash->{'result'} = $textinfo; $c->forward('View::JSON'); @@ -356,6 +352,22 @@ sub variantgraph :Local :Args(1) { $c->stash->{'result'} = $collation->as_svg; $c->forward('View::SVG'); } + +sub _stemma_info { + my( $stemma, $sid ) = @_; + my $ssvg = $stemma->as_svg(); + $ssvg =~ s/\n/ /mg; + my $sinfo = { + name => $stemma->identifier, + directed => _json_bool( !$stemma->is_undirected ), + svg => $ssvg }; + if( $sid ) { + $sinfo->{stemmaid} = $sid; + } + return $sinfo; +} + +## TODO Separate stemma manipulation functionality into its own controller. =head2 stemma @@ -430,7 +442,6 @@ sub stemma :Local :Args(2) { if( !$stemma && $tradition->stemma_count > $stemmaid ) { $stemma = $tradition->stemma( $stemmaid ); } - my $stemma_xml = $stemma ? $stemma->as_svg() : ''; # What was requested, XML or JSON? my $return_view = 'SVG'; if( my $accept_header = $c->req->header('Accept') ) { @@ -446,15 +457,10 @@ sub stemma :Local :Args(2) { } } if( $return_view eq 'SVG' ) { - $c->stash->{'result'} = $stemma_xml; + $c->stash->{'result'} = $stemma->as_svg(); $c->forward('View::SVG'); } else { # JSON - $stemma_xml =~ s/\n/ /mg; - $c->stash->{'result'} = { - 'stemmaid' => $stemmaid, - 'name' => $stemma->identifier, - 'directed' => _json_bool( !$stemma->is_undirected ), - 'svg' => $stemma_xml }; + $c->stash->{'result'} = { _stemma_info( $stemma, $stemmaid ) }; $c->forward('View::JSON'); } } @@ -485,6 +491,41 @@ sub stemmadot :Local :Args(2) { $c->forward('View::JSON'); } +=head2 stemmaroot + + POST /stemmaroot/$textid/$stemmaseq, { root: } + +Orients the given stemma so that the given node is the root (archetype). Returns the +information structure for the new stemma. + +=cut + +sub stemmaroot :Local :Args(2) { + my( $self, $c, $textid, $stemmaid ) = @_; + my $m = $c->model('Directory'); + my $tradition = $m->tradition( $textid ); + unless( $tradition ) { + return _json_error( $c, 404, "No tradition with ID $textid" ); + } + my $ok = _check_permission( $c, $tradition ); + if( $ok eq 'full' ) { + my $stemma = $tradition->stemma( $stemmaid ); + try { + $stemma->root_graph( $c->req->param('root') ); + $m->save( $tradition ); + } catch( Text::Tradition::Error $e ) { + return _json_error( $c, 400, $e->message ); + } catch { + return _json_error( $c, 500, "Error re-rooting stemma: $@" ); + } + $c->stash->{'result'} = _stemma_info( $stemma ); + $c->forward('View::JSON'); + } else { + return _json_error( $c, 403, + 'You do not have permission to update stemmata for this tradition' ); + } +} + =head2 download GET /download/$textid diff --git a/lib/stemmaweb/Controller/Stemweb.pm b/lib/stemmaweb/Controller/Stemweb.pm index 2a31903..8f7aa4e 100644 --- a/lib/stemmaweb/Controller/Stemweb.pm +++ b/lib/stemmaweb/Controller/Stemweb.pm @@ -89,9 +89,9 @@ sub available :Local :Args(0) { my $ua = LWP::UserAgent->new(); my $resp = $ua->get( $STEMWEB_BASE_URL . '/algorithms/available' ); if( $resp->is_success ) { - $c->stash->{'result'} = $resp->content; + $c->stash->{'result'} = decode_json( $resp->content ); } else { - $c->stash->{'result'} = '{}'; + $c->stash->{'result'} = {}; } $c->forward('View::JSON'); } @@ -160,7 +160,6 @@ sub _process_stemweb_result { # Check all stemmata for the given jobid and return them. @$stemmata = grep { $_->came_from_jobid && $_->from_jobid eq $answer->{jobid} } $tradition->stemmata; } - $DB::single = 1; if( @$stemmata ) { # If we got here, success! my @steminfo = map { { diff --git a/root/js/componentload.js b/root/js/componentload.js index 06a8706..61ab006 100644 --- a/root/js/componentload.js +++ b/root/js/componentload.js @@ -1,6 +1,7 @@ // Global state variables var selectedTextID; var selectedTextInfo; +var selectedTextEditable; var selectedStemmaID = -1; var stemmata = []; @@ -29,6 +30,7 @@ function refreshDirectory () { // view pane. Calls load_textinfo. function loadTradition( textid, textname, editable ) { selectedTextID = textid; + selectedTextEditable = editable; // First insert the placeholder image and register an error handler $('#textinfo_load_status').empty(); $('#stemma_graph').empty(); @@ -69,7 +71,7 @@ function loadTradition( textid, textname, editable ) { } else { selectedStemmaID = -1; } - load_stemma( selectedStemmaID, editable ); + load_stemma( selectedStemmaID ); // Set up the relationship mapper button $('#run_relater').attr( 'action', _get_url([ "relation", textid ]) ); // Set up the download button @@ -110,31 +112,31 @@ function load_textinfo() { } // Enable / disable the appropriate buttons for paging through the stemma. -function show_stemmapager ( editable ) { +function show_stemmapager () { $('.pager_left_button').unbind('click').addClass( 'greyed_out' ); $('.pager_right_button').unbind('click').addClass( 'greyed_out' ); if( selectedStemmaID > 0 ) { $('.pager_left_button').click( function () { - load_stemma( selectedStemmaID - 1, editable ); + load_stemma( selectedStemmaID - 1, selectedTextEditable ); }).removeClass( 'greyed_out' ); } if( selectedStemmaID + 1 < stemmata.length ) { $('.pager_right_button').click( function () { - load_stemma( selectedStemmaID + 1, editable ); + load_stemma( selectedStemmaID + 1, selectedTextEditable ); }).removeClass( 'greyed_out' ); } } // Load a given stemma SVG into the stemmagraph box. -function load_stemma( idx, editable ) { +function load_stemma( idx ) { // Load the stemma at idx selectedStemmaID = idx; - show_stemmapager( editable ); + show_stemmapager( selectedTextEditable ); $('#open_stemma_edit').hide(); $('#run_stexaminer').hide(); $('#stemma_identifier').empty(); // Add the relevant Stemweb functionality - if( editable ) { + if( selectedTextEditable ) { if( selectedTextInfo.stemweb_jobid == 0 ) { $('#open_stemweb_ui').show(); $('#query_stemweb_ui').hide(); @@ -146,7 +148,7 @@ function load_stemma( idx, editable ) { if( idx > -1 ) { // Load the stemma and its properties var stemmadata = stemmata[idx]; - if( editable ) { + if( selectedTextEditable ) { $('#open_stemma_edit').show(); } if( stemmadata.directed ) { @@ -227,32 +229,48 @@ function loadSVG(svgData) { } function set_stemma_interactive( svg_element ) { - $( "#root_tree_dialog_button_ok" ).click( function() { - // AJAX call goes here - } ); - $.each( $( 'ellipse', svg_element ), function(index) { - var ellipse = $(this); - var g = ellipse.parent( 'g' ); - g.click( function(evt) { - if( typeof root_tree_dialog_timeout !== 'undefined' ) { clearTimeout( root_tree_dialog_timeout ) }; - g.unbind( 'mouseleave' ); - var dialog = $( '#root_tree_dialog' ); - dialog.hide(); - dialog.css( 'top', evt.pageY + 3 ); - dialog.css( 'left', evt.pageX + 3 ); - dialog.show(); - root_tree_dialog_timeout = setTimeout( function() { - $( '#root_tree_dialog' ).hide(); - ellipse.removeClass( 'stemma_node_highlight' ); - g.mouseleave( function() { ellipse.removeClass( 'stemma_node_highlight' ) } ); - }, 3000 ); - } ); - g.mouseenter( function() { - $( 'ellipse.stemma_node_highlight' ).removeClass( 'stemma_node_highlight' ); - ellipse.addClass( 'stemma_node_highlight' ) - } ); - g.mouseleave( function() { ellipse.removeClass( 'stemma_node_highlight' ) } ); - } ); + if( selectedTextEditable ) { + $( "#root_tree_dialog_button_ok" ).click( function() { + var requrl = _get_url([ "stemmaroot", selectedTextID, selectedStemmaID ]); + var targetnode = $('ellipse.stemma_node_highlight').parent().attr('id'); + $.post( requrl, { root: targetnode }, function (data) { + // Reload the new stemma + stemmata[selectedStemmaID] = data; + load_stemma( selectedStemmaID ); + // Put away the dialog + $('#root_tree_dialog').hide(); + } ); + } ).ajaxError( function(event, jqXHR, ajaxSettings, thrownError) { + if( ajaxSettings.url.indexOf( 'stemmaroot' ) > -1 + && ajaxSettings.type == 'POST' ) { + display_error( jqXHR, $("#stemma_load_status") ); + } + } ); + // TODO Clear error at some appropriate point + $.each( $( 'ellipse', svg_element ), function(index) { + var ellipse = $(this); + var g = ellipse.parent( 'g' ); + g.click( function(evt) { + if( typeof root_tree_dialog_timeout !== 'undefined' ) { clearTimeout( root_tree_dialog_timeout ) }; + g.unbind( 'mouseleave' ); + var dialog = $( '#root_tree_dialog' ); + dialog.hide(); + dialog.css( 'top', evt.pageY + 3 ); + dialog.css( 'left', evt.pageX + 3 ); + dialog.show(); + root_tree_dialog_timeout = setTimeout( function() { + $( '#root_tree_dialog' ).hide(); + ellipse.removeClass( 'stemma_node_highlight' ); + g.mouseleave( function() { ellipse.removeClass( 'stemma_node_highlight' ) } ); + }, 3000 ); + } ); + g.mouseenter( function() { + $( 'ellipse.stemma_node_highlight' ).removeClass( 'stemma_node_highlight' ); + ellipse.addClass( 'stemma_node_highlight' ) + } ); + g.mouseleave( function() { ellipse.removeClass( 'stemma_node_highlight' ) } ); + } ); + } } // General-purpose error-handling function. @@ -387,7 +405,7 @@ function _get_url( els ) { return basepath + els.join('/'); } - +// TODO Attach unified ajaxError handler to document $(document).ready( function() { // See if we have the browser functionality we need // TODO Also think of a test for SVG readiness diff --git a/root/src/index.tt b/root/src/index.tt index 1aaaddc..e7d9925 100644 --- a/root/src/index.tt +++ b/root/src/index.tt @@ -94,6 +94,7 @@ var textOnLoad = "[% withtradition %]"; +