From 80c68194a63ac1535479b3cfc67e1df4bca4594c Mon Sep 17 00:00:00 2001 From: Jordan Wright Date: Sun, 18 Jun 2017 17:32:18 -0500 Subject: [PATCH] Updated dashboard to show full final statuses instead of just "Successful" vs "Unsuccessful". --- static/js/dist/app/dashboard.min.js | 2 +- static/js/src/app/dashboard.js | 255 +++++++++++++++++++--------- templates/dashboard.html | 12 +- 3 files changed, 183 insertions(+), 86 deletions(-) diff --git a/static/js/dist/app/dashboard.min.js b/static/js/dist/app/dashboard.min.js index 59e1e6a7..e2d37f7f 100644 --- a/static/js/dist/app/dashboard.min.js +++ b/static/js/dist/app/dashboard.min.js @@ -1 +1 @@ -function deleteCampaign(a){confirm("Delete "+campaigns[a].name+"?")&&api.campaignId.delete(campaigns[a].id).success(function(a){successFlash(a.message),location.reload()})}var campaigns=[],labels={"In progress":"label-primary",Queued:"label-info",Completed:"label-success","Emails Sent":"label-success",Error:"label-danger"};$(document).ready(function(){api.campaigns.summary().success(function(a){if($("#loading").hide(),campaigns=a.campaigns,campaigns.length>0){$("#dashboard").show();var t={labels:[],series:[[]]},e={series:[]},s={axisX:{showGrid:!1},showArea:!0,plugins:[],low:0,high:100},i={donut:!0,donutWidth:40,chartPadding:0,showLabel:!1},o=0;campaignTable=$("#campaignTable").DataTable({columnDefs:[{orderable:!1,targets:"no-sort"}],order:[[1,"desc"]]}),$.each(campaigns,function(a,e){var s,i=moment(e.created_date).format("MMMM Do YYYY, h:mm:ss a"),r=labels[e.status]||"label-default";if(moment(e.launch_date).isAfter(moment())){s="Scheduled to start: "+moment(e.launch_date).format("MMMM Do YYYY, h:mm:ss a");var l=s+"

Number of recipients: "+e.stats.total}else{s="Launch Date: "+moment(e.launch_date).format("MMMM Do YYYY, h:mm:ss a");var l=s+"

Number of recipients: "+e.stats.total+"

Emails opened: "+e.stats.opened+"

Emails clicked: "+e.stats.clicked+"

Submitted Credentials: "+e.stats.submitted_data+"

Errors : "+e.stats.error}campaignTable.row.add([escapeHtml(e.name),i,''+e.status+"","
"]).draw(),$('[data-toggle="tooltip"]').tooltip(),e.y=0,e.y+=e.stats.clicked+e.stats.submitted_data,e.y=Math.floor(e.y/e.stats.total*100),o+=e.y,t.labels.push(i),t.series[0].push({meta:a,value:e.y})}),o=Math.floor(o/a.total),e.series.push({meta:"Unsuccessful Phishes",value:100-o}),e.series.push({meta:"Successful Phishes",value:o});new Chartist.Pie("#average_chart",e,i),new Chartist.Line("#overview_chart",t,s);$piechart=$("#average_chart");var r=$piechart.append('
').find(".chartist-tooltip").hide();$piechart.on("mouseenter",".ct-slice-donut",function(){var a=$(this);value=a.attr("ct:value"),label=a.attr("ct:meta"),r.html(label+": "+value.toString()+"%").show()}),$piechart.on("mouseleave",".ct-slice-donut",function(){r.hide()}),$piechart.on("mousemove",function(a){r.css({left:(a.offsetX||a.originalEvent.layerX)-r.width()/2-10,top:(a.offsetY+40||a.originalEvent.layerY)-r.height()-80})}),$chart=$("#overview_chart");var l=$chart.append('
').find(".chartist-tooltip").hide();$chart.on("mouseenter",".ct-point",function(){var a=$(this);value=a.attr("ct:value")||0,cidx=a.attr("ct:meta"),l.html(campaigns[cidx].name+"
Successes: "+value.toString()+"%").show()}),$chart.on("mouseleave",".ct-point",function(){l.hide()}),$chart.on("mousemove",function(a){l.css({left:(a.offsetX||a.originalEvent.layerX)-l.width()/2-10,top:(a.offsetY+40||a.originalEvent.layerY)-l.height()-40})}),$("#overview_chart").on("click",".ct-point",function(a){$(this).attr("ct:meta");window.location.href="/campaigns/"+campaigns[cidx].id})}else $("#emptyMessage").show()}).error(function(){errorFlash("Error fetching campaigns")})}); \ No newline at end of file +function deleteCampaign(e){confirm("Delete "+campaigns[e].name+"?")&&api.campaignId.delete(campaigns[e].id).success(function(e){successFlash(e.message),location.reload()})}function generateStatsPieChart(e){var t={donut:!0,donutWidth:40,chartPadding:0,showLabel:!1},a={},i={series:[]},n=0;$.each(e,function(e,t){$.each(t.stats,function(e,t){return"total"==e?(n+=t,!0):void(a[e]?a[e]+=t:a[e]=t)})}),$.each(a,function(e,t){status_label=statsMapping[e],i.series.push({meta:status_label,value:Math.floor(t/n*100)}),$("#stats_chart_legend").append('
  • '+status_label+"
  • ")});new Chartist.Pie("#stats_chart",i,t);$piechart=$("#stats_chart");var s=$piechart.append('
    ').find(".chartist-tooltip").hide();$piechart.get(0).__chartist__.on("draw",function(e){e.element.addClass(statuses[e.meta].slice)}),$piechart.get(0).__chartist__.update(i),$piechart.on("mouseenter",".ct-slice-donut",function(){var e=$(this);value=e.attr("ct:value"),label=e.attr("ct:meta"),s.html(label+": "+value.toString()+"%").show()}),$piechart.on("mouseleave",".ct-slice-donut",function(){s.hide()}),$piechart.on("mousemove",function(e){s.css({left:(e.offsetX||e.originalEvent.layerX)-s.width()/2-10,top:(e.offsetY+40||e.originalEvent.layerY)-s.height()-80})})}var campaigns=[],statuses={"Email Sent":{slice:"ct-slice-donut-sent",legend:"ct-legend-sent",label:"label-success",icon:"fa-envelope",point:"ct-point-sent"},"Email Sent":{slice:"ct-slice-donut-sent",legend:"ct-legend-sent",label:"label-success",icon:"fa-envelope",point:"ct-point-sent"},"Email Opened":{slice:"ct-slice-donut-opened",legend:"ct-legend-opened",label:"label-warning",icon:"fa-envelope",point:"ct-point-opened"},"Clicked Link":{slice:"ct-slice-donut-clicked",legend:"ct-legend-clicked",label:"label-clicked",icon:"fa-mouse-pointer",point:"ct-point-clicked"},Success:{slice:"ct-slice-donut-success",legend:"ct-legend-success",label:"label-danger",icon:"fa-exclamation",point:"ct-point-clicked"},Error:{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-times",point:"ct-point-error"},"Error Sending Email":{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-times",point:"ct-point-error"},"Submitted Data":{slice:"ct-slice-donut-success",legend:"ct-legend-success",label:"label-danger",icon:"fa-exclamation",point:"ct-point-clicked"},Unknown:{slice:"ct-slice-donut-error",legend:"ct-legend-error",label:"label-default",icon:"fa-question",point:"ct-point-error"},Sending:{slice:"ct-slice-donut-sending",legend:"ct-legend-sending",label:"label-primary",icon:"fa-spinner",point:"ct-point-sending"},"Campaign Created":{label:"label-success",icon:"fa-rocket"}},statsMapping={sent:"Email Sent",opened:"Email Opened",clicked:"Clicked Link",submitted_data:"Submitted Data",error:"Error"};$(document).ready(function(){api.campaigns.summary().success(function(e){if($("#loading").hide(),campaigns=e.campaigns,campaigns.length>0){$("#dashboard").show();var t={labels:[],series:[[]]},a={axisX:{showGrid:!1},showArea:!0,plugins:[],low:0,high:100};campaignTable=$("#campaignTable").DataTable({columnDefs:[{orderable:!1,targets:"no-sort"}],order:[[1,"desc"]]}),$.each(campaigns,function(e,a){var i,n=moment(a.created_date).format("MMMM Do YYYY, h:mm:ss a"),s=statuses[a.status]||"label-default";if(moment(a.launch_date).isAfter(moment())){i="Scheduled to start: "+moment(a.launch_date).format("MMMM Do YYYY, h:mm:ss a");var l=i+"

    Number of recipients: "+a.stats.total}else{i="Launch Date: "+moment(a.launch_date).format("MMMM Do YYYY, h:mm:ss a");var l=i+"

    Number of recipients: "+a.stats.total+"

    Emails opened: "+a.stats.opened+"

    Emails clicked: "+a.stats.clicked+"

    Submitted Credentials: "+a.stats.submitted_data+"

    Errors : "+a.stats.error}campaignTable.row.add([escapeHtml(a.name),n,''+a.status+"","
    "]).draw(),$('[data-toggle="tooltip"]').tooltip(),a.y=0,a.y+=a.stats.clicked+a.stats.submitted_data,a.y=Math.floor(a.y/a.stats.total*100),t.labels.push(n),t.series[0].push({meta:e,value:a.y})}),generateStatsPieChart(campaigns);new Chartist.Line("#overview_chart",t,a);$chart=$("#overview_chart");var i=$chart.append('
    ').find(".chartist-tooltip").hide();$chart.on("mouseenter",".ct-point",function(){var e=$(this);value=e.attr("ct:value")||0,cidx=e.attr("ct:meta"),i.html(campaigns[cidx].name+"
    Successes: "+value.toString()+"%").show()}),$chart.on("mouseleave",".ct-point",function(){i.hide()}),$chart.on("mousemove",function(e){i.css({left:(e.offsetX||e.originalEvent.layerX)-i.width()/2-10,top:(e.offsetY+40||e.originalEvent.layerY)-i.height()-40})}),$("#overview_chart").on("click",".ct-point",function(e){$(this).attr("ct:meta");window.location.href="/campaigns/"+campaigns[cidx].id})}else $("#emptyMessage").show()}).error(function(){errorFlash("Error fetching campaigns")})}); \ No newline at end of file diff --git a/static/js/src/app/dashboard.js b/static/js/src/app/dashboard.js index 9b3e0efb..64ec4f65 100644 --- a/static/js/src/app/dashboard.js +++ b/static/js/src/app/dashboard.js @@ -1,12 +1,89 @@ var campaigns = [] - // labels is a map of campaign statuses to - // CSS classes -var labels = { - "In progress": "label-primary", - "Queued": "label-info", - "Completed": "label-success", - "Emails Sent": "label-success", - "Error": "label-danger" + +// statuses is a helper map to point result statuses to ui classes +var statuses = { + "Email Sent": { + slice: "ct-slice-donut-sent", + legend: "ct-legend-sent", + label: "label-success", + icon: "fa-envelope", + point: "ct-point-sent" + }, + "Email Sent": { + slice: "ct-slice-donut-sent", + legend: "ct-legend-sent", + label: "label-success", + icon: "fa-envelope", + point: "ct-point-sent" + }, + "Email Opened": { + slice: "ct-slice-donut-opened", + legend: "ct-legend-opened", + label: "label-warning", + icon: "fa-envelope", + point: "ct-point-opened" + }, + "Clicked Link": { + slice: "ct-slice-donut-clicked", + legend: "ct-legend-clicked", + label: "label-clicked", + icon: "fa-mouse-pointer", + point: "ct-point-clicked" + }, + "Success": { + slice: "ct-slice-donut-success", + legend: "ct-legend-success", + label: "label-danger", + icon: "fa-exclamation", + point: "ct-point-clicked" + }, + "Error": { + slice: "ct-slice-donut-error", + legend: "ct-legend-error", + label: "label-default", + icon: "fa-times", + point: "ct-point-error" + }, + "Error Sending Email": { + slice: "ct-slice-donut-error", + legend: "ct-legend-error", + label: "label-default", + icon: "fa-times", + point: "ct-point-error" + }, + "Submitted Data": { + slice: "ct-slice-donut-success", + legend: "ct-legend-success", + label: "label-danger", + icon: "fa-exclamation", + point: "ct-point-clicked" + }, + "Unknown": { + slice: "ct-slice-donut-error", + legend: "ct-legend-error", + label: "label-default", + icon: "fa-question", + point: "ct-point-error" + }, + "Sending": { + slice: "ct-slice-donut-sending", + legend: "ct-legend-sending", + label: "label-primary", + icon: "fa-spinner", + point: "ct-point-sending" + }, + "Campaign Created": { + label: "label-success", + icon: "fa-rocket" + } +} + +var statsMapping = { + "sent": "Email Sent", + "opened": "Email Opened", + "clicked": "Clicked Link", + "submitted_data": "Submitted Data", + "error": "Error" } function deleteCampaign(idx) { @@ -19,6 +96,75 @@ function deleteCampaign(idx) { } } +function generateStatsPieChart(campaigns) { + var stats_opts = { + donut: true, + donutWidth: 40, + chartPadding: 0, + showLabel: false + } + var stats_series_data = {} + var stats_data = { + series: [] + } + var total = 0 + + $.each(campaigns, function(i, campaign) { + $.each(campaign.stats, function(status, count) { + if (status == "total") { + total += count + return true + } + if (!stats_series_data[status]) { + stats_series_data[status] = count; + } else { + stats_series_data[status] += count; + } + }) + }) + $.each(stats_series_data, function(status, count) { + // I don't like this, but I guess it'll have to work. + // Turns submitted_data into Submitted Data + status_label = statsMapping[status] + stats_data.series.push({ + meta: status_label, + value: Math.floor((count / total) * 100) + }) + $("#stats_chart_legend").append('
  • ' + status_label + '
  • ') + }) + + var stats_chart = new Chartist.Pie("#stats_chart", stats_data, stats_opts) + + $piechart = $("#stats_chart") + var $pietoolTip = $piechart + .append('
    ') + .find('.chartist-tooltip') + .hide(); + + $piechart.get(0).__chartist__.on('draw', function(data) { + data.element.addClass(statuses[data.meta].slice) + }) + // Update with the latest data + $piechart.get(0).__chartist__.update(stats_data) + + $piechart.on('mouseenter', '.ct-slice-donut', function() { + var $point = $(this) + value = $point.attr('ct:value') + label = $point.attr('ct:meta') + $pietoolTip.html(label + ': ' + value.toString() + "%").show(); + }); + + $piechart.on('mouseleave', '.ct-slice-donut', function() { + $pietoolTip.hide(); + }); + $piechart.on('mousemove', function(event) { + $pietoolTip.css({ + left: (event.offsetX || event.originalEvent.layerX) - $pietoolTip.width() / 2 - 10, + top: (event.offsetY + 40 || event.originalEvent.layerY) - $pietoolTip.height() - 80 + }); + }); +} + $(document).ready(function() { api.campaigns.summary() .success(function(data) { @@ -33,9 +179,6 @@ $(document).ready(function() { [] ] } - var average_data = { - series: [] - } var overview_opts = { axisX: { showGrid: false @@ -45,13 +188,6 @@ $(document).ready(function() { low: 0, high: 100 } - var average_opts = { - donut: true, - donutWidth: 40, - chartPadding: 0, - showLabel: false - } - var average = 0 campaignTable = $("#campaignTable").DataTable({ columnDefs: [{ orderable: false, @@ -62,19 +198,19 @@ $(document).ready(function() { ] }); $.each(campaigns, function(i, campaign) { - var campaign_date = moment(campaign.created_date).format('MMMM Do YYYY, h:mm:ss a') - var label = labels[campaign.status] || "label-default"; - //section for tooltips on the status of a campaign to show some quick stats - var launchDate; - if (moment(campaign.launch_date).isAfter(moment())) { - launchDate = "Scheduled to start: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a') - var quickStats = launchDate + "

    " + "Number of recipients: " + campaign.stats.total - } else { - launchDate = "Launch Date: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a') - var quickStats = launchDate + "

    " + "Number of recipients: " + campaign.stats.total + "

    " + "Emails opened: " + campaign.stats.opened + "

    " + "Emails clicked: " + campaign.stats.clicked + "

    " + "Submitted Credentials: " + campaign.stats.submitted_data + "

    " + "Errors : " + campaign.stats.error - } - // Add it to the table - campaignTable.row.add([ + var campaign_date = moment(campaign.created_date).format('MMMM Do YYYY, h:mm:ss a') + var label = statuses[campaign.status] || "label-default"; + //section for tooltips on the status of a campaign to show some quick stats + var launchDate; + if (moment(campaign.launch_date).isAfter(moment())) { + launchDate = "Scheduled to start: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a') + var quickStats = launchDate + "

    " + "Number of recipients: " + campaign.stats.total + } else { + launchDate = "Launch Date: " + moment(campaign.launch_date).format('MMMM Do YYYY, h:mm:ss a') + var quickStats = launchDate + "

    " + "Number of recipients: " + campaign.stats.total + "

    " + "Emails opened: " + campaign.stats.opened + "

    " + "Emails clicked: " + campaign.stats.clicked + "

    " + "Submitted Credentials: " + campaign.stats.submitted_data + "

    " + "Errors : " + campaign.stats.error + } + // Add it to the table + campaignTable.row.add([ escapeHtml(campaign.name), campaign_date, "" + campaign.status + "", @@ -85,54 +221,21 @@ $(document).ready(function() { \ " ]).draw() - $('[data-toggle="tooltip"]').tooltip() - // Add it to the chart data - campaign.y = 0 - campaign.y += campaign.stats.clicked + campaign.stats.submitted_data - campaign.y = Math.floor((campaign.y / campaign.stats.total) * 100) - average += campaign.y - // Add the data to the overview chart - overview_data.labels.push(campaign_date) - overview_data.series[0].push({ - meta: i, - value: campaign.y - }) - }) - average = Math.floor(average / data.total); - average_data.series.push({ - meta: "Unsuccessful Phishes", - value: 100 - average - }) - average_data.series.push({ - meta: "Successful Phishes", - value: average + $('[data-toggle="tooltip"]').tooltip() + // Add it to the chart data + campaign.y = 0 + campaign.y += campaign.stats.clicked + campaign.stats.submitted_data + campaign.y = Math.floor((campaign.y / campaign.stats.total) * 100) + // Add the data to the overview chart + overview_data.labels.push(campaign_date) + overview_data.series[0].push({ + meta: i, + value: campaign.y + }) }) // Build the charts - var average_chart = new Chartist.Pie("#average_chart", average_data, average_opts) + generateStatsPieChart(campaigns) var overview_chart = new Chartist.Line('#overview_chart', overview_data, overview_opts) - // Setup the average chart listeners - $piechart = $("#average_chart") - var $pietoolTip = $piechart - .append('
    ') - .find('.chartist-tooltip') - .hide(); - - $piechart.on('mouseenter', '.ct-slice-donut', function() { - var $point = $(this) - value = $point.attr('ct:value') - label = $point.attr('ct:meta') - $pietoolTip.html(label + ': ' + value.toString() + "%").show(); - }); - - $piechart.on('mouseleave', '.ct-slice-donut', function() { - $pietoolTip.hide(); - }); - $piechart.on('mousemove', function(event) { - $pietoolTip.css({ - left: (event.offsetX || event.originalEvent.layerX) - $pietoolTip.width() / 2 - 10, - top: (event.offsetY + 40 || event.originalEvent.layerY) - $pietoolTip.height() - 80 - }); - }); // Setup the overview chart listeners $chart = $("#overview_chart") diff --git a/templates/dashboard.html b/templates/dashboard.html index f0ba3079..ce798175 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -45,15 +45,9 @@

    Average Phishing Results

    -
    -
    -
      -
    • - Successful Phishes -
    • -
    • - Unsuccessful Phishes -
    • +
      +
      +