diff --git a/analytics/__init__.py b/analytics/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/analytics/__init__.py diff --git a/analytics/admin.py b/analytics/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/analytics/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/analytics/apps.py b/analytics/apps.py new file mode 100644 index 0000000..2a2cf57 --- /dev/null +++ b/analytics/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AnalyticsConfig(AppConfig): + name = 'analytics' diff --git a/analytics/migrations/__init__.py b/analytics/migrations/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/analytics/migrations/__init__.py diff --git a/analytics/models.py b/analytics/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/analytics/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/analytics/static/.sass-cache/9bdb779ec82a4a96f72be09b83b7c997addd0129/general.sassc b/analytics/static/.sass-cache/9bdb779ec82a4a96f72be09b83b7c997addd0129/general.sassc new file mode 100644 index 0000000..9ac09a5 Binary files /dev/null and b/analytics/static/.sass-cache/9bdb779ec82a4a96f72be09b83b7c997addd0129/general.sassc differ diff --git a/analytics/static/analytics/general.css b/analytics/static/analytics/general.css new file mode 100644 index 0000000..35d1ca7 --- /dev/null +++ b/analytics/static/analytics/general.css @@ -0,0 +1,43 @@ +.report-body { + border: 1px solid #3ebeb4; + border-radius: 20px; + padding: 10px; } + +#general-report-header { + height: 10%; + display: block; } + #general-report-header h3 { + color: #009688; } + #general-report-header ul { + float: right; + display: inline-flex; + width: 50%; + height: 15%; } + #general-report-header ul li { + margin-right: 2%; + width: 50%; + color: white; } + #general-report-header ul li div.selected { + background-color: #4dcfbd; + width: 100%; + text-align: center; + border-radius: 20px; } + #general-report-header ul li div { + background-color: #0f8a9a; + width: 100%; + text-align: center; + border-radius: 20px; } + +#most-used-tags-header { + background: linear-gradient(to right, #0e8999, #6bf0ce); + height: 40px; + border-radius: 10px; + color: #ffffff; } + +div.tag-cloud { + width: 10%; + border-radius: 25px; + color: #ffffff; + background-color: #52b7bd; } + +/*# sourceMappingURL=general.css.map */ diff --git a/analytics/static/analytics/general.css.map b/analytics/static/analytics/general.css.map new file mode 100644 index 0000000..b659a69 --- /dev/null +++ b/analytics/static/analytics/general.css.map @@ -0,0 +1,7 @@ +{ +"version": 3, +"mappings": "AAAA,YAAY;EACX,MAAM,EAAE,iBAAiB;EACzB,aAAa,EAAE,IAAI;EACnB,OAAO,EAAE,IAAI;;AAGd,sBAAsB;EACrB,MAAM,EAAE,GAAG;EACX,OAAO,EAAE,KAAK;EACd,yBAAE;IACD,KAAK,EAAE,OAAO;EAEf,yBAAE;IACD,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,WAAW;IACpB,KAAK,EAAE,GAAG;IACV,MAAM,EAAE,GAAG;IAEX,4BAAE;MACD,YAAY,EAAE,EAAE;MAChB,KAAK,EAAE,GAAG;MACV,KAAK,EAAE,KAAK;MAEZ,yCAAY;QACX,gBAAgB,EAAE,OAAO;QACzB,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;QAClB,aAAa,EAAE,IAAI;MAEpB,gCAAG;QACF,gBAAgB,EAAE,OAAO;QACzB,KAAK,EAAE,IAAI;QACX,UAAU,EAAE,MAAM;QAClB,aAAa,EAAE,IAAI;;AAIvB,sBAAsB;EACrB,UAAU,EAAE,2CAA2C;EACvD,MAAM,EAAE,IAAI;EACZ,aAAa,EAAE,IAAI;EACnB,KAAK,EAAE,OAAO;;AAEf,aAAa;EACZ,KAAK,EAAE,GAAG;EACV,aAAa,EAAE,IAAI;EACnB,KAAK,EAAE,OAAO;EACd,gBAAgB,EAAE,OAAO", +"sources": ["general.sass"], +"names": [], +"file": "general.css" +} \ No newline at end of file diff --git a/analytics/static/analytics/general.sass b/analytics/static/analytics/general.sass new file mode 100644 index 0000000..7b1c963 --- /dev/null +++ b/analytics/static/analytics/general.sass @@ -0,0 +1,48 @@ +.report-body + border: 1px solid #3ebeb4 + border-radius: 20px + padding: 10px + + +#general-report-header + height: 10% + display: block + h3 + color: #009688 + + ul + float: right + display: inline-flex + width: 50% + height: 15% + + li + margin-right: 2% + width: 50% + color: white + + div.selected + background-color: #4dcfbd + width: 100% + text-align: center + border-radius: 20px + + div + background-color: #0f8a9a + width: 100% + text-align: center + border-radius: 20px + + + +#most-used-tags-header + background: linear-gradient(to right, #0e8999, #6bf0ce) + height: 40px + border-radius: 10px + color: #ffffff + +div.tag-cloud + width: 10% + border-radius: 25px + color: #ffffff + background-color: #52b7bd \ No newline at end of file diff --git a/analytics/static/analytics/js/charts.js b/analytics/static/analytics/js/charts.js new file mode 100644 index 0000000..17ad291 --- /dev/null +++ b/analytics/static/analytics/js/charts.js @@ -0,0 +1,378 @@ +/* + HOME CHARTS +*/ + +//Adding this code by @jshanley to create a filter to look for the last child + +d3.selection.prototype.first = function() { + return d3.select(this[0]); +}; +d3.selection.prototype.last = function() { + var last = this.size() - 1; + return d3.select(this[last]); +}; + +var charts = { + build_resource: function(url){ + $.get(url, function(dataset){ + + + var width = 600; + var height = 300; + var padding = 30; + var radius = Math.min(width, height) / 2 - padding; + + var color = d3.scaleOrdinal(d3.schemeCategory20); + var new_div = d3.select(".carousel-inner").append("div").attr("class","item"); + var svg = new_div.append("svg").attr("width", width).attr("height", height) + .style("margin","auto") + .style("display","block") + .append('g') + .attr('transform', 'translate(' + (width / 2) + + ',' + (height / 2 + padding ) + ')'); + + + + var donutInner = 20; + var arc = d3.arc() + .innerRadius(radius - donutInner) + .outerRadius(radius); + + svg.append("text") + .attr("text-anchor", "middle") + .attr("x",0 ) + .attr("y", -height/2 ) + .style("font-size", "30px") + .text("Recursos mais utilizados") + .attr("fill", "#003333") + .style("font-weight", "bold") + .style("font-style", "italic"); + + + var pie = d3.pie() + .value(function(d) { return d[1]; }) + .sort(null); + + + + var path = svg.selectAll('path') + .data(pie(dataset)) + .enter() + .append('path') + .attr('d', arc) + .attr('fill', function(d, i) { + return color(i); + }); + var labelArc = d3.arc() + .outerRadius(radius - donutInner + 30) + .innerRadius(radius + 20 ); + + //Adding tooltips to each part of the pie chart. + svg.selectAll("text.pie-tooltip") + .data(pie(dataset)) + .enter() + .append("text") + .attr("id", function(d){ + return d.data[0]; + }) + .attr("class","pie-tooltip") + .attr('fill',"#172121") + .attr("transform", function(d) { + c = labelArc.centroid(d); + return "translate(" + (c[0]*1.0 - 20) +"," + c[1]*0.8 + ")"; + }) + .attr("dy", ".25em") + .text(function(d) { return d.data[0] +'('+ d.data[1] +')'; }); + + + + }) // end of the get method + }, + + build_bubble_user: function(url){ + $.get(url, function(dataset){ + var width = 600; + var height = 300; + + + function min(){ + min = 100000000000; + for(var i = 0; i < dataset.length; i++){ + if (dataset[i]['count'] < min){ + min = dataset[i]['count']; + } + } + + return min + } + + function max(){ + max = 0; + for(var i = 0; i < dataset.length; i++){ + if (dataset[i]['count'] > max){ + max = dataset[i]['count']; + } + } + + return max + } + + + + var color = d3.scaleOrdinal(d3.schemeCategory20); + //adding new div to carousel-inner div + var new_div = d3.select(".carousel-inner").append("div").attr("class","item"); + var radiusScale = d3.scaleSqrt().domain([min(), max()]).range([10,50]); + var svg = new_div.append("svg").attr("width", width).attr("height", height) + .style("margin","auto") + .style("display","block") + .append('g') + .attr("transform", "translate(0,0)") + .attr("width", width) + .attr("height", height); + + //adding svg title + + svg.append("text") + .attr("text-anchor", "middle") + .attr("x", width/2 ) + .attr("y", 30) + .style("font-size", "30px") + .text("Usuários mais ativos no Amadeus") + .attr("fill", "#003333") + .style("font-weight", "bold") + .style("font-style", "italic"); + + var simulation = d3.forceSimulation() + .force("x", d3.forceX(width/2).strength(0.05)) + .force("y", d3.forceY(height/2).strength(0.05)) + .force("collide", d3.forceCollide(function(d){ + return radiusScale(d['count']); + })); + + //First I create as many groups as datapoints so + //it can hold all other objects (circle, texts, images) + var groups = svg.selectAll('.users-item') + .data(dataset) + .enter() + .append("g") + .attr("class",".user-dot"); + + //Create circles to be drawn + var circles = groups + .append('circle') + .attr("r", function(d){ + return radiusScale(d['count']); + }) + + .attr("fill", function(d){ + return 'url('+'#'+'user_'+d['user_id']+')'; + }); + + + + //Add texts to show user names + groups.append("text") + .text(function(d){ + return d['user'] +'('+ d['count'] + ')'; + }).attr("fill", "#FFFFFF") + .attr("id", function(d){ + return "user_tooltip_"+d['user_id']; + }).style("display", "none"); + + + groups.on("mouseover", function(d){ + $("#"+"user_tooltip_"+d['user_id']).show(); + }); + + + groups.on("mouseout", function(d){ + $("#"+"user_tooltip_"+d['user_id']).hide(); + }); + + var defs = groups.append('svg:defs'); + + //Attching images to bubbles + defs.append("svg:pattern") + .attr("id", function(d){ + return "user_"+d['user_id']; + }) + .attr("width", function(d){ + return radiusScale(d['count']); + }) + .attr("height", function(d){ + return radiusScale(d['count']); + }) + .append("svg:image") + .attr("xlink:href", function(d){ + return d['image']; + }) + .attr("width",function(d){ + return radiusScale(d['count'])*2; + }) + .attr("height", function(d){ + return radiusScale(d['count'])*2; + }) + .attr("x", 0) + .attr("y", 0); + + + + //simulation + simulation.nodes(dataset) + .on('tick', ticked); //so all data points are attached to it + + function ticked(){ + groups.attr("transform", function(d){ + return "translate(" + d.x + "," + d.y + ")"; + }) + } + }); + + + }, + + most_accessed_subjects: function(url){ + $.get(url, function(dataset){ + var width = 800; + var height = 300; + + var new_div = d3.select(".carousel-inner").append("div").attr("class","item"); + + var svg = new_div.append("svg").attr("width", "100%").attr("height", height) + .style("margin","auto") + .style("display","block"); + + var barPadding = 2 + var bottomPadding = 15; + var marginLeft = 170; //Margin Left for all bars inside the graph + + var yScale = d3.scaleLinear() + .domain([0, d3.max(dataset, function(d) { return d.count; })]) + .range([bottomPadding, 260]); + + var rects = svg.selectAll("rect") + .data(dataset) + .enter() + .append("g"); + + rects.append("rect") + .attr("x", function(d, i){ + return i * (width / dataset.length ) + barPadding + marginLeft ; + }) + .attr("y", function(d){ + return height - yScale(d.count) - bottomPadding; + }) + .attr("width", + width / dataset.length - barPadding + ) + .attr("height", function(d){ + return yScale(d.count); + }); + + + var tooltipDiv = new_div.append("div").attr("class","bar-tip"); + + rects.on("mouseover", function(d, i){ + $(this).attr("fill", "red"); + $("#accessed_subject_"+i).show(); //So the tooltip is hidden + tooltipDiv.transition() + .duration(200) + .style("opacity", .9); + tooltipDiv.html("
" + d.name + "( " + d.count + " )" +"
") + .style("left", (i * (width / dataset.length ) + barPadding + marginLeft) + "px") + .style("top", (height - yScale(d.count) - bottomPadding) + "px"); + }); + + rects.on("mouseout", function(d, i ){ + $(this).attr("fill", "black"); + + $("#accessed_subject_"+i).hide(); //So the tooltip shows up + + + tooltipDiv.transition() + .duration(500) + .style("opacity", 0); + + }); + + + + //Styling title + svg.append("text") + .attr("x", width/2) + .attr("y", 30) + .attr("text-anchor", "middle") + .style("font-size", "30px") + .text("Subjects mais acessados") + .attr("fill", "#003333") + .style("font-weight", "bold") + .style("font-style", "italic"); + + }); + }, + most_used_tags: function(url){ + $.get(url, function(dataset){ + //get most used tags across all amadeus + var width = 800; + var height = 300; + + function min(){ + min = 100000000000; + for(var i = 0; i < dataset.length; i++){ + if (dataset[i]['count'] < min){ + min = dataset[i]['count']; + } + } + + return min + } + + function max(){ + max = 0; + for(var i = 0; i < dataset.length; i++){ + if (dataset[i]['count'] > max){ + max = dataset[i]['count']; + } + } + + return max + } + + console.log(dataset); + + var container_div = d3.select("#most-used-tags-body"); + var svg = container_div.append("svg").attr("width", "100%").attr("height", height) + .style("margin","auto") + .style("display","block") + .style("background","#ddf8e7") + .append('g') + .attr("transform", "translate(0,0)") + .attr("width", width) + .attr("height", height); + + + + var radiusScale = d3.scaleSqrt().domain([min(), max()]).range([10,50]); + var tag_cloud = svg.selectAll('.tag-cloud-div') + .data(dataset) + .enter() + .append('g') + .attr("class", "data-container"); + + + var tag_divs = tag_cloud + .append('div') + .attr('class', 'tag-cloud') + .attr('text', function(d){ + return d['name']; + }); + + }); + } +} + +charts.most_used_tags('/analytics/most_used_tags'); +//charts.build_resource('/topics/count_resources/'); +//charts.build_bubble_user('/users/get_users_log/'); +//charts.most_accessed_subjects('/subjects/most_accessed_subjects'); \ No newline at end of file diff --git a/analytics/templates/analytics/general.html b/analytics/templates/analytics/general.html new file mode 100644 index 0000000..9dcebd6 --- /dev/null +++ b/analytics/templates/analytics/general.html @@ -0,0 +1,76 @@ +{% extends 'base.html' %} + +{% load static i18n pagination %} +{% load django_bootstrap_breadcrumbs %} +{% block style %} + +{% endblock style %} + +{% block javascript %} + +{% endblock javascript %} + +{% block breadcrumbs %} + {{ block.super }} + + {% trans 'Analytics General' as general %} + + {% breadcrumb general 'dashboard:view_general' %} +{% endblock %} + + +{% block content %} +