mirror of
https://github.com/neocities/neocities.git
synced 2025-04-25 01:32:36 +02:00
First implementation of advanced stats
This commit is contained in:
parent
dc01d795f3
commit
51140856c5
3 changed files with 362 additions and 0 deletions
44
app/site.rb
44
app/site.rb
|
@ -32,6 +32,50 @@ get '/site/:username/?' do |username|
|
||||||
erb :'site', locals: {site: site, is_current_site: site == current_site}
|
erb :'site', locals: {site: site, is_current_site: site == current_site}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
get '/site/:username/stats' do
|
||||||
|
@site = Site[username: params[:username]]
|
||||||
|
not_found if @site.nil?
|
||||||
|
|
||||||
|
@stats = {}
|
||||||
|
|
||||||
|
%i{referrers locations paths}.each do |stat|
|
||||||
|
@stats[stat] = @site.send("stat_#{stat}_dataset".to_sym).order(:views.desc).limit(100).all
|
||||||
|
end
|
||||||
|
|
||||||
|
@stats[:locations].collect! do |location|
|
||||||
|
location_name = ''
|
||||||
|
|
||||||
|
location_name += location.city_name if location.city_name
|
||||||
|
|
||||||
|
if location.region_name
|
||||||
|
# Some of the region names are numbers for some reason.
|
||||||
|
begin
|
||||||
|
Integer(location.region_name)
|
||||||
|
rescue
|
||||||
|
location_name += ', ' unless location_name == ''
|
||||||
|
location_name += location.region_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if location.country_code2 && !$country_codes[location.country_code2].nil?
|
||||||
|
location_name += ', ' unless location_name == ''
|
||||||
|
location_name += $country_codes[location.country_code2]
|
||||||
|
end
|
||||||
|
|
||||||
|
location_hash = {name: location_name, views: location.views}
|
||||||
|
if location.latitude && location.longitude
|
||||||
|
location_hash.merge! latitude: location.latitude, longitude: location.longitude
|
||||||
|
end
|
||||||
|
location_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
@stats[:stat_days] = @site.stats_dataset.order(:created_at.desc).limit(7).all.reverse
|
||||||
|
|
||||||
|
@multi_tooltip_template = "<%= datasetLabel %> - <%= value %>"
|
||||||
|
|
||||||
|
erb :'site/stats', locals: {site: @site}
|
||||||
|
end
|
||||||
|
|
||||||
post '/site/:username/set_editor_theme' do
|
post '/site/:username/set_editor_theme' do
|
||||||
require_login
|
require_login
|
||||||
current_site.editor_theme = params[:editor_theme]
|
current_site.editor_theme = params[:editor_theme]
|
||||||
|
|
|
@ -131,3 +131,11 @@ if ENV['RACK_ENV'] != 'development'
|
||||||
# Sass::Plugin.options[:never_update] = true
|
# Sass::Plugin.options[:never_update] = true
|
||||||
Sass::Plugin.options[:full_exception] = false
|
Sass::Plugin.options[:full_exception] = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require 'csv'
|
||||||
|
|
||||||
|
$country_codes = {}
|
||||||
|
|
||||||
|
CSV.foreach("./files/country_codes.csv") do |row|
|
||||||
|
$country_codes[row.last] = row.first
|
||||||
|
end
|
||||||
|
|
310
views/site/stats.erb
Normal file
310
views/site/stats.erb
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
<div class="header-Outro with-columns">
|
||||||
|
<div class="row content">
|
||||||
|
<div class="col col-66">
|
||||||
|
<h3>Your Stats</h3>
|
||||||
|
<div class="feed-filter">
|
||||||
|
<% if !@events.empty? && (site.followings_dataset.count > 0) %>
|
||||||
|
<a href="/" <% if params[:activity].nil? %>class="selected"<% end %>>All</a>
|
||||||
|
<a href="/?activity=mine" <% if params[:activity] == 'mine' %>class="selected"<% end %>>Profile Activity</a>
|
||||||
|
<% end %>
|
||||||
|
<a href="/activity">Global Activity</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col col-32">
|
||||||
|
<h3>Your Site</h3>
|
||||||
|
<a href="/dashboard" class="btn-Action edit"><i class="fa fa-edit" title="Edit"></i>Edit Site</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container news-feed">
|
||||||
|
<div class="content misc-page columns right-col">
|
||||||
|
<div class="col-left">
|
||||||
|
<div class="col col-66">
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col col-50 globe">
|
||||||
|
<h2>Latest Visitors</h2>
|
||||||
|
<div id="earth_div"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col col-50" style="padding-right: 0;">
|
||||||
|
<table class="table table-striped" id="latest-visitors">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">San Francisco, CA</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-link"></i> neocities.org</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM</span>
|
||||||
|
<span class="paths"><a href="">/index</a>, <a href="">/links</a>, <a href="">/art</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">Portland, OR</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-search"></i> Google search</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM</span>
|
||||||
|
<span class="paths"><a href="">/index</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">London, UK</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-link"></i> Twitter URL</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM</span>
|
||||||
|
<span class="paths"><a href="">/index</a>, <a href="">/about</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">Hong Kong, China</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-search"></i> Google search</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM</span>
|
||||||
|
<span class="paths"><a href="">/index</a>, <a href="">/links</a>, <a href="">/art</a>, <a href="">/music</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">San Francisco, CA</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-link"></i> Facebook URL</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM - 4/27/15</span>
|
||||||
|
<span class="paths"><a href="">/index</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">San Francisco, CA</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-link"></i> neocities.org</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM - 4/27/15</span>
|
||||||
|
<span class="paths"><a href="">/index</a>, <a href="">/links</a>, <a href="">/art</a>, <a href="">/music</a>, <a href="">/about</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">Portland, OR</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-search"></i> Google search</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM - 4/27/15</span>
|
||||||
|
<span class="paths"><a href="">/index</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">London, UK</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-link"></i> Twitter URL</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM - 4/27/15</span>
|
||||||
|
<span class="paths"><a href="">/index</a>, <a href="">/about</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">Hong Kong, China</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-search"></i> Google search</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM - 4/27/15</span>
|
||||||
|
<span class="paths"><a href="">/index</a>, <a href="">/links</a>, <a href="">/art</a>, <a href="">/music</a>, <a href="">/tech</a>, <a href="">/about</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<span class="location">Hong Kong, China</span>
|
||||||
|
<a class="referrer" href=""><i class="fa fa-search"></i> Google search</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="time">7:11PM - 4/27/15</span>
|
||||||
|
<span class="paths"><a href="">/index</a>, <a href="">/links</a>, <a href="">/art</a>, <a href="">/music</a></span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2>Last 7 Days</h2>
|
||||||
|
<p>(<a href="/plan">Upgrade</a> to see up to see stats for all time)</p>
|
||||||
|
<ul class="nav h-Nav">
|
||||||
|
<li><a href="">Month</a></li>
|
||||||
|
<li><a href="">3 months</a></li>
|
||||||
|
<li><a href="">1 Year</a></li>
|
||||||
|
<li><a href="">All time</a></li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<canvas id="myChart" style="width:100%;height:300px;display:block"></canvas>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col col-50">
|
||||||
|
<h2>Top Paths</h2>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Path</th>
|
||||||
|
<th>Visits</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @stats[:paths].each do |path| %>
|
||||||
|
<tr>
|
||||||
|
<td><a href="<%= @site.uri+path.name %>" target="_blank"><%= path.name.gsub(/\?.+/i, '') %></a></td>
|
||||||
|
<td><%= path.views %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<h2>Top Locations</h2>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>City</th>
|
||||||
|
<th>Visits</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @stats[:locations].each do |location| %>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<%= location[:name] %>
|
||||||
|
</td>
|
||||||
|
<td><%= location[:views] %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col col-50">
|
||||||
|
|
||||||
|
<h2>Top Referrers</h2>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Referrer</th>
|
||||||
|
<th>Visits</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% @stats[:referrers].each do |referrer| %>
|
||||||
|
<tr>
|
||||||
|
<td><%= referrer.url %></td>
|
||||||
|
<td><%= referrer.views %></td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col col-33">
|
||||||
|
<div class="news-site-info">
|
||||||
|
<p class="site-url"><a href="<%= current_site.uri %>" target="_blank"><%= site.title %></a></p>
|
||||||
|
<div class="stats">
|
||||||
|
<div class="col col-50">
|
||||||
|
<% if site.updated_at %>
|
||||||
|
Last updated<br><strong><%= site.updated_at.ago %></strong>
|
||||||
|
<% else %>
|
||||||
|
Your new website!<br><strong><a href="/dashboard"><i class="fa fa-edit" title="Edit"></i> Start Building</a></strong>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="col col-50">
|
||||||
|
<div><strong><%= site.views.format_large_number %></strong> views</div>
|
||||||
|
<% follows_count = site.follows_dataset.count %>
|
||||||
|
<div><strong><%= follows_count.format_large_number %></strong> follower<%= follows_count == 1 ? '' : 's' %></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a href="<%= site.uri %>" class="large-portrait" style="background-image:url(<%= site.screenshot_url('index.html', '540x405') %>);"></a>
|
||||||
|
|
||||||
|
<div class="news-profile-button">
|
||||||
|
<a href="/site/<%= site.username %>" class="btn-Action"><i class="fa fa-user"></i> Profile</a>
|
||||||
|
<a href="#" id="shareButton" class="btn-Action" data-container="body" data-toggle="popover" data-placement="bottom" data-content='<%== erb :'_share', layout: false, locals: {site: current_site} %>'><i class="fa fa-share-alt"></i> Share</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%== erb :'_follows', layout: false, locals: {site: site, is_current_site: site == current_site} %>
|
||||||
|
|
||||||
|
<%== erb :'_tags', layout: false, locals: {site: site, is_current_site: site == current_site} %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="http://www.webglearth.com/v2/api.js"></script>
|
||||||
|
<script src="/js/Chart.min.js"></script>
|
||||||
|
<script>
|
||||||
|
//OpenGL globe
|
||||||
|
$(document).ready(function() {
|
||||||
|
var earth = new WE.map('earth_div');
|
||||||
|
earth.setView([20, -100], 2.07);
|
||||||
|
WE.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
|
||||||
|
attribution: '© OpenStreetMap'
|
||||||
|
}).addTo(earth);
|
||||||
|
|
||||||
|
<% @stats[:locations].each do |location| %>
|
||||||
|
var marker = WE.marker([<%= location[:latitude] %>, <%= location[:longitude] %>]).addTo(earth);
|
||||||
|
marker.bindPopup("<b><%= location[:name] %></b><br><%= location[:views] %> views", {maxWidth: 150, closeButton: true})
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Start a simple rotation animation
|
||||||
|
var before = null;
|
||||||
|
requestAnimationFrame(function animate(now) {
|
||||||
|
var c = earth.getPosition();
|
||||||
|
var elapsed = before? now - before: 0;
|
||||||
|
before = now;
|
||||||
|
earth.setCenter([c[0], c[1] + 0.1*(elapsed/30)]);
|
||||||
|
requestAnimationFrame(animate);
|
||||||
|
});
|
||||||
|
|
||||||
|
//chart.js
|
||||||
|
var data = {
|
||||||
|
labels: <%== @stats[:stat_days].collect {|s| s.created_at.strftime("%b %-d")}.to_json %>,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Hits",
|
||||||
|
fillColor: "rgba(220,220,220,0.2)",
|
||||||
|
strokeColor: "rgba(220,220,220,1)",
|
||||||
|
pointColor: "rgba(220,220,220,1)",
|
||||||
|
pointStrokeColor: "#fff",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(220,220,220,1)",
|
||||||
|
data: <%== @stats[:stat_days].collect {|s| s.hits}.to_json %>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Unique Visits",
|
||||||
|
fillColor: "rgba(151,187,205,0.2)",
|
||||||
|
strokeColor: "rgba(151,187,205,1)",
|
||||||
|
pointColor: "rgba(151,187,205,1)",
|
||||||
|
pointStrokeColor: "#fff",
|
||||||
|
pointHighlightFill: "#fff",
|
||||||
|
pointHighlightStroke: "rgba(151,187,205,1)",
|
||||||
|
data: <%== @stats[:stat_days].collect {|s| s.views}.to_json %>
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
// Get context with jQuery - using jQuery's .get() method.
|
||||||
|
var ctx = $("#myChart").get(0).getContext("2d");
|
||||||
|
// This will get the first returned node in the jQuery collection.
|
||||||
|
//var myNewChart = new Chart(ctx);
|
||||||
|
var myLineChart = new Chart(ctx).Line(data, {
|
||||||
|
bezierCurve: false,
|
||||||
|
multiTooltipTemplate: "<%== @multi_tooltip_template %>"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
Add table
Reference in a new issue