Skip to content

Commit

Permalink
[WIP] added filtering and sorting in cytoscape visualization (#9954)
Browse files Browse the repository at this point in the history
* added filtering and sorting in cytoscape visualization

* fix subscription sort bug

* Cytoscape Refinements
  • Loading branch information
17sushmita authored Aug 10, 2021
1 parent e8a33a8 commit cff3581
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 42 deletions.
2 changes: 1 addition & 1 deletion app/controllers/tag_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ def graph
end

def graph_data
render json: params.key?(:limit) ? Tag.graph_data(params[:limit].to_i) : Tag.graph_data
render json: params.key?(:limit) ? Tag.graph_data(params[:limit].to_i, params[:type].to_s, params[:weight].to_i) : Tag.graph_data
end

def stats
Expand Down
46 changes: 33 additions & 13 deletions app/models/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -374,22 +374,42 @@ def self.related(tag_name, count = 5)
end

# for Cytoscape.js http://js.cytoscape.org/
def self.graph_data(limit = 250)
Rails.cache.fetch("graph-data/#{limit}/new", expires_in: 1) do
def self.graph_data(limit = 250, type = 'nodes', weight = 0)
Rails.cache.fetch("graph-data/#{limit}/#{type}/#{weight}", expires_in: 1.weeks) do
data = {}
data["tags"] = []
Tag.joins(:node)
.group(:tid)
.where('node.status': 1)
.where('term_data.name NOT LIKE (?)', '%:%')
.where.not(name: 'first-time-poster')
.order(count: :desc)
.limit(limit).each do |tag|
data["tags"] << {
"name" => tag.name,
"count" => tag.count
}
if type == 'nodes' # notes
Tag.joins(:node)
.group(:tid)
.where('node.status': 1)
.where('term_data.name NOT LIKE (?)', '%:%')
.where.not(name: 'first-time-poster')
.order(count: :desc)
.having("count >= ?", weight)
.limit(limit).each do |tag|
data["tags"] << {
"name" => tag.name,
"count" => tag.count
}
end
elsif type == 'subscribers' # subscribers
Tag.select("name, count(tag_selections.tid) as subcount")
.joins("LEFT OUTER JOIN tag_selections ON tag_selections.tid = term_data.tid")
.group('term_data.name')
.where('term_data.name NOT LIKE (?)', '%:%')
.where.not(name: 'first-time-poster')
.order(subcount: :desc)
.having("subcount >= ?", weight)
.limit(limit).each do |tag|
unless tag.name.strip.empty?
data["tags"] << {
"name" => tag.name,
"count" => tag.subcount
}
end
end
end

data["edges"] = []
data["tags"].each do |tag|
Tag.related(tag["name"], 10).each do |related_tag|
Expand Down
201 changes: 173 additions & 28 deletions app/views/tag/graph.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,69 @@ html, body {
width: 100%;
height: 100%;
display: block;
z-index: 999;
}

.toppanel {
height: auto; /* Specify a height */
position: absolute; /* Stay in place */
z-index: 1000; /* Stay on top */
top: 0;
left: 0;
padding-left: 1em;
padding-top: 10px; /* Place content 10px from the top */
padding-bottom: 10px; /* Place content 10px from the bottom */
}

label{
top: 12px;
}
input[type="range" i] {
position: relative;
top: 12px;
}
}

#toolbar > * {
display: block;

}
.center-screen {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
min-height: 100vh;
z-index: 1000000;
}

@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}


.spinner-grow {
0%{-webkit-transform:scale(0);transform:scale(0)}
50%{opacity:1;-webkit-transform:none;transform:none}
}

@keyframes spinner-grow{
0%{-webkit-transform:scale(0);transform:scale(0)}
50%{opacity:1;-webkit-transform:none;transform:none}
}

.spinner-grow{
display:inline-block;
width:2rem;
height:2rem;
vertical-align:text-bottom;
background-color:currentColor;
border-radius:50%;
opacity:0;
-webkit-animation:spinner-grow .75s linear infinite;
animation:spinner-grow .75s linear infinite
}
</style>

Expand All @@ -29,6 +92,39 @@ html, body {

<script type="text/javascript" src="/lib/jlouvain/jLouvain.js"></script>

<div id="toppanel" class="toppanel">
<a class="btn btn-primary" data-toggle="collapse" href="#toolbar" aria-expanded="false" aria-controls="toolbar">
<i class="fa fa-filter"></i>
</a>

<div class="collapse" id="toolbar">
<span>
<label class="form-label" id="filterlabel" for="filter">Min. no. of Nodes</label>
<input class="form-range" id="filter" name="filter" onchange="rangevalue.value=value" value="0" type="range" min="0">
<span class="highlight"></span>
<output id="rangevalue">0</output>
</span>
<br>
<span>
<select id="sort" class="form-control">
<option id="defaultsort" value="nodes" selected>Sort By</option>
<option value="nodes">Notes</option>
<option value="subscribers">Subscribers</option>
</select>
</span>
<br>
<%# <span>
<input type="text" class="form-control" id="search" placeholder="Search">
</span> %>
</div>

</div>

<div id="loader" class="center-screen" role="status">
<div class="spinner-grow">
</div>
</div>

<div id="cytoscape"></div>

<script>
Expand All @@ -39,23 +135,6 @@ html, body {
var mobile = window.matchMedia("(max-width: 400px)")
var desktop = window.matchMedia("(min-width: 400px)")

if (mobile.matches){
$.ajax({
url : '/tag/graph.json?limit=' + <%= params[:limit] || 100 %>,
type: 'GET',
success: function(response){
setup_cytoscape(response);
}
});
} else{
$.ajax({
url : '/tag/graph.json?limit=' + <%= params[:limit] || 250 %>,
type: 'GET',
success: function(response){
setup_cytoscape(response);
}
});
}

function parse_cyto_data(data) {
var output = {
Expand Down Expand Up @@ -87,7 +166,65 @@ html, body {
return output;
}


// function to sort or filter
function sort_or_filter(type, limit, weight) {
$.ajax({
url : '/tag/graph.json?limit=' + limit + '&type=' + type + '&weight=' + weight,
beforeSend: function(){
$('#cytoscape').hide();
$('#loader').show();
},
success:function(response){
$('#loader').hide();
$('#cytoscape').show();

max_weight = response.tags[0].count;

setup_cytoscape(response);
}
});
}

if (mobile.matches){
l = ("<%= params[:limit] %>" == "") ? 100 : "<%= params[:limit] %>"
}
else{
l = ("<%= params[:limit] %>" == "") ? 250 : "<%= params[:limit] %>"
}


$('#sort').change(function () {
$('#defaultsort').attr('disabled', 'true');
if ($(this).val() == 'nodes') {
$('#filterlabel').text('Min. no. of Notes');
} else {
$('#filterlabel').text('Min. no. of Subscribers');
}
$('#filter').val(0);
$('#rangevalue').val(0);

var sort = ("<%= params[:type] %>" == "") ? $('#sort').val() : "<%= params[:type] %>"

var weight = 0;
sort_or_filter(sort, l, weight);

})

$('#filter').change(function() {
var sort = ("<%= params[:type] %>" == "") ? $('#sort').val() : "<%= params[:type] %>"

var weight = ("<%= params[:weight] %>" == "") ? $('#filter').val() : "<%= params[:filter] %>"

sort_or_filter(sort, l, weight);
});

sort_or_filter("nodes", <%= params[:limit] || 100 %>, <%= params[:weight] || 0 %>);

function setup_cytoscape(data) {
max_weight = data.tags[0].count;
$('#filter').attr('max', max_weight);


var data = parse_cyto_data(data),
weights = data.nodes.map(function(aa){ return aa.data.weight });
Expand All @@ -108,10 +245,17 @@ html, body {
name: 'cose',

// Called on `layoutready`
ready: function(){},
ready: function(){
$('html').attr('background-color', 'black');
$('#toolbar').attr('background-color', 'black');

},

// Called on `layoutstop`
stop: function(){},
stop: function(){
$('#toolbar').attr('background-color', 'black');

},

// Whether to animate while running the layout
// true : Animate continuously as the layout is running
Expand Down Expand Up @@ -189,7 +333,6 @@ html, body {
// Pass a reference to weaver to use threads for calculations
weaver: false
},

style: [
{
selector: 'node',
Expand Down Expand Up @@ -223,7 +366,9 @@ html, body {

});


cy.minZoom( 0.5 );
cy.maxZoom( 20 );

if (mobile.matches) { // If media query matches
cy.on('taphold', 'node', function(e){
cy.nodes().style('opacity', 1);
Expand Down Expand Up @@ -293,13 +438,13 @@ html, body {
.style('border-color', 'white');
});

cy.on('zoom', function(e){
//cy.nodes()//.style('font-size', 20/cy.zoom())
//.style('text-outline-width', 1.5/cy.zoom())
//.style('border-width', 3/cy.zoom())
// .style('width', 'mapData(weight / ' + cy.zoom() + ', ' + min + ',' + max + ', 30, 150)')
// .style('height', 'mapData(weight / ' + cy.zoom() + ', ' + min + ',' + max + ', 30, 150)')
});
// cy.on('zoom', function(e){
// cy.nodes()//.style('font-size', 20/cy.zoom())
// .style('text-outline-width', 1.5/cy.zoom())
// .style('border-width', 3/cy.zoom())
// .style('width', 'mapData(weight / ' + cy.zoom() + ', ' + min + ',' + max + ', 30, 150)')
// .style('height', 'mapData(weight / ' + cy.zoom() + ', ' + min + ',' + max + ', 30, 150)')
// });


/*
Expand Down

0 comments on commit cff3581

Please sign in to comment.