package stemmaweb::Controller::Stexaminer;
use Moose;
use namespace::autoclean;
+use Encode qw/ decode_utf8 /;
use File::Temp;
use JSON;
use Text::Tradition::Analysis qw/ run_analysis wit_stringify /;
+use Text::Tradition::Collation;
+use Text::Tradition::Stemma;
BEGIN { extends 'Catalyst::Controller' }
=head1 METHODS
+=head2 index
+
GET stexaminer/$textid
Renders the application for the text identified by $textid.
-=head2 index
-
=cut
sub index :Path :Args(1) {
my $tradition = $m->tradition( $textid );
if( $tradition->stemma_count ) {
my $stemma = $tradition->stemma(0);
- # TODO Think about caching the stemma in a session
- $c->stash->{svg} = $stemma->as_svg;
+ $c->stash->{svg} = $stemma->as_svg( { size => [ 600, 350 ] } );
+ $c->stash->{graphdot} = $stemma->editable({ linesep => ' ' });
$c->stash->{text_title} = $tradition->name;
$c->stash->{template} = 'stexaminer.tt';
# TODO Run the analysis as AJAX from the loaded page.
}
}
+=head2 graphsvg
+
+ POST stexaminer/graphsvg
+ dot: <stemmagraph dot string>
+ layerwits: [ <a.c. witnesses ]
+
+Returns an SVG string of the given graph, extended to include the given
+layered witnesses.
+
+=cut
+
+sub graphsvg :Local {
+ my( $self, $c ) = @_;
+ my $dot = $c->request->param('dot');
+ my @layerwits = $c->request->param('layerwits[]');
+ open my $stemma_fh, '<', \$dot;
+ binmode( $stemma_fh, ':encoding(UTF-8)' );
+ my $emptycoll = Text::Tradition::Collation->new();
+ my $tempstemma = Text::Tradition::Stemma->new(
+ collation => $emptycoll, 'dot' => $stemma_fh );
+ my $svgopts = { size => [ 600, 350 ] };
+ if( @layerwits ) {
+ $svgopts->{'layerwits'} = \@layerwits;
+ }
+ $c->stash->{'result'} = $tempstemma->as_svg( $svgopts );
+ $c->forward('View::SVG');
+}
+
=head2 end
Attempt to render a view, if needed.
#variants_table {
float: left;
width: 90%;
- height: 90px;
+ height: 190px;
border: 1px #c6dcf1 solid;
margin-bottom: 20px;
overflow: auto;
}
#stemma_graph {
- height: 450px;
+ height: 350px;
clear: both;
float: left;
- width: 700px;
+ width: 600px;
text-align: center;
border: 1px #c6dcf1 solid;
}
position: relative;
top: -15px;
}
+.reading_statistics {
+ margin: 7pt;
+ border-bottom: 1px solid #488dd2;
+}
+.readinglabel {
+ font-weight: bold;
+}
+.readingroots {
+ font-weight: bold;
+ color: #488dd2;
+}
+.reading_copied {
+ color: #33dd33;
+}
+.reading_changed {
+ color: #dd3333;
+}
.template {
display: none;
}
var colors = ['#ffeeaa','#afc6e9','#d5fff6','#ffccaa','#ffaaaa','#e5ff80','#e5d5ff','#ffd5e5'];
var row_triggered = false;
+var original_svg;
+
+function handle_row_click( row ) {
+ var ridx = row.parent().parent().index()
+ var rs = readingstats[ridx];
+ var imghtml = '<img src="../images/ajax-loader.gif" alt="Loading SVG..."/>'
+ $('#stemma_graph').empty();
+ $('#stemma_graph').append( imghtml );
+ if( rs.layerwits ) {
+ var stemma_form = { 'dot': graphdot, 'layerwits': rs.layerwits };
+ $('#stemma_graph').load( 'graphsvg', stemma_form, function() {
+ color_row( row );
+ show_stats( rs );
+ });
+ } else {
+ $('#stemma_graph').empty();
+ $('#stemma_graph').append( original_svg );
+ color_row( row );
+ show_stats( rs );
+ }
+}
function color_row( row ) {
row_triggered = true;
});
}
-function show_stats( row_index ) {
- var rs = readingstats[row_index];
+function show_stats( rs ) {
var rshtml = $('#stats_template').clone();
rshtml.find('#statrank').append( rs.id );
$.each( rs.readings, function( idx, rdghash ) {
var readinglabel = rdghash.readingid;
if( rdghash.text ) {
- readinglabel += ' (' + rdghash.text + ')';
+ readinglabel = rdghash.text;
}
var readingroots = rdghash.independent_occurrence.join( ', ' );
var rdgstats = $('#reading_template').clone();
$('#row_statistics').empty();
$('#row_statistics').append( rshtml.contents() );
};
+
+// Save the original unextended SVG for when we need it.
+$(document).ready(function () {
+ original_svg = $('#stemma_graph > svg').clone();
+});
%]
<script type="text/javascript">
var readingstats = [% reading_statistics %];
+var graphdot = '[% graphdot %]';
</script>
[% END -%]
<h1>Stexaminer</h1>
[% END -%]
</table>
</div>
- <div id="stemma_graph">
- [% svg %]
- </div>
- <div id="row_statistics">
- <h3>Aggregate text statistics</h3>
- <ul>
- <li>Total number of variant locations analyzed: [% total %]</li>
- <li>Number of fully genealogical locations: [% genealogical %]</li>
- <li>Number of readings that conflict with the stemma: [% conflict %]</li>
- <li>Genealogical reading transitions by relationship type: [% todo %]</li>
- </ul>
- <p>(Choose a row in the table to display statistics about individual readings.)</p>
+ <div>
+ <div id="stemma_graph">
+ [% svg %]
+ </div>
+ <div id="row_statistics">
+ <h3>Aggregate text statistics</h3>
+ <ul>
+ <li>Total number of variant locations analyzed: [% total %]</li>
+ <li>Number of fully genealogical locations: [% genealogical %]</li>
+ <li>Number of readings that conflict with the stemma: [% conflict %]</li>
+ <li>Genealogical reading transitions by relationship type: [% todo %]</li>
+ </ul>
+ <p>(Choose a row in the table to display statistics about individual readings.)</p>
+ </div>
</div>
<div id="stats_template" class="template">
<!-- reading_template will be appended here for each reading -->
</div>
- <div id="reading_template" class="template">
+ <div id="reading_template" class="template">
<div class="reading_statistics">
- <span class="readinglabel"></span> - copied <span class="reading_copied"></span> times, changed <span class="reading_changed"></span> times<br/>
+ <span class="readinglabel"></span> - copied <span class="reading_copied"></span> time(s), changed <span class="reading_changed"></span> time(s)<br/>
Reading root(s) at <span class="readingroots"></span><br/>
<!-- reading_parent_template will be appended here if there are parents -->
</div>
[% SET rowclass = 'class="genealogical"' IF row.genealogical -%]
[% SET rowclass = 'class="coincidental"' UNLESS row.genealogical -%]
<tr [% rowclass %]>
- <th><span class="rowid" onclick="color_row($(this));show_stats($(this).parent().parent().index())">[% row.id %]</span></th>
+ <th><span class="rowid" onclick="handle_row_click($(this))">[% row.id %]</span></th>
[% FOREACH reading IN row.readings -%]
[% SET cellclass = 'clickable conflict' IF reading.conflict -%]
[% SET cellclass = 'clickable' IF !reading.conflict -%]