Komalis / PigeonLibre

// ==UserScript==
// @name PigeonLibre
// @description Statistique sur les achats PayUTC
// @author Benjamin BONVARLET
// @author Paul LESUR
// @match https://payutc.nemopay.net/*
// @run-at document-end
// @require https://code.jquery.com/jquery-3.3.1.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.3/Chart.min.js
// @version 1.8.3
// @licence MIT
// ==/UserScript==

// Initialisation et chargement des données utilisateurs
if(localStorage.getItem("maxClassement") == undefined)
{
  localStorage.setItem("maxClassement", 10);
}
if(localStorage.getItem("stopList") == undefined)
{
  localStorage.setItem("stopList", JSON.stringify(["Rechargement"]));
}

let debut, fin;
let maxClassement = localStorage.getItem("maxClassement");
let stopList = JSON.parse(localStorage.getItem("stopList"));

var totalSpend = 0;
var rechargementTotal = 0;

/*
	On récupère la totalité des articles et on les traites pour obtenir un tableau d'objets avec les informations suivantes
  name : String
  quantity : Int
  date : Date
  type : String
  price : Float
*/

let items = [];
$("tr", $("table")).each((key, tr) => 
{	
	let parsedItem = $(".expand", tr).text().replace(/ {2,}/g, " ").replace(/\n /g, "").split(" ");
  
  let formattedDate = $("td", tr).first().text().replace(/ +/g, "").replace("\n", "").substr(0, 8);

  let parsedDate = new Date("20" + formattedDate.split("/")[2] + "-" + formattedDate.split("/")[1] + "-" + formattedDate.split("/")[0]);

  if(parsedItem[parsedItem.length - 1] == "")
  {
    parsedItem = parsedItem.slice(0, -1);
  }

  let item = {
    quantity : ($(tr).attr("class") === "cancelled") ? 0 : parseInt(parsedItem[0]),
    name : parsedItem.slice(1).join(" "),
    date : parsedDate,
    cancelled : $(tr).attr("class") === "cancelled"
  }

  let parsedDebit = $(".debit", tr).text().replace(/.*?(\d+,\d+).*/, "$1").replace(/\n */g, "").replace(",", ".");
  let parsedCredit = $(".credit", tr).text().replace(/.*?(\d+,\d+).*/, "$1").replace(/\n */g, "").replace(",", ".");

  if(parsedDebit != "")
  {
    item.type = "debit";
    item.price = ($(tr).attr("class") === "cancelled") ? 0 : parseFloat(parsedDebit);
  }
  else
  {
    item.type = "credit";
    item.price = ($(tr).attr("class") === "cancelled") ? 0 : parseFloat(parsedCredit);
  }
  items.push(item);
});

/* Permet le filtre du tableau des articles entre deux dates */
if(localStorage.getItem("debut") == undefined)
{
  debut = +items[items.length - 1].date;
}
else
{
  debut = localStorage.getItem("debut");
}

if(localStorage.getItem("fin") == undefined)
{
	fin = +new Date();
}
else
{
  fin = localStorage.getItem("fin");
}

items = items.filter((a) => +a.date >= +debut && +a.date <= +fin);

var totalSpend = 0;
var rechargementTotal = 0;
var hashMapItemsQuantity = {};
var hashMapItemsPrice = {};
var hashMapItemsCredit = {};

items.forEach((item) => 
{
  	/* Création d'un dictionnaire Item -> Quantité Total */
    if($.inArray(item.name, stopList) === -1)
    {
        if(hashMapItemsQuantity[item.name] === undefined)
        {
            hashMapItemsQuantity[item.name] = 0;
        }
        hashMapItemsQuantity[item.name] += item.quantity;
    }
	  /* Création d'un dictionnaire Item -> Prix Total Débit */
  	if($.inArray(item.name, stopList) === -1 && item.type === "debit")
    {
        if(hashMapItemsPrice[item.name] === undefined)
        {
            hashMapItemsPrice[item.name] = 0;
        }
        hashMapItemsPrice[item.name] += item.price;
    }
	  /* Création d'un dictionnaire Item -> Prix Total Crédit */
  	else if($.inArray(item.name, stopList) === -1 && item.type === "credit")
    {
        if(hashMapItemsCredit[item.name] === undefined)
        {
            hashMapItemsCredit[item.name] = 0;
        }
        hashMapItemsCredit[item.name] += item.price;
    }
	  /* Récupération des dépenses total */
    if(item.type === "debit")
    {
        totalSpend += item.price;
    }
  	else if(item.type === "credit")
    {
      	if(item.quantity < 0)
        {
          totalSpend -= item.price;
        }
      	else
        {
          rechargementTotal += item.price;
        }
    }
});

/* Création d'un tableau trié par rapport à la quantité total d'un article */
var sortedItemsQuantity = Object.keys(hashMapItemsQuantity).sort((a, b) => hashMapItemsQuantity[b] - hashMapItemsQuantity[a]);

/* Création d'un tableau trié par rapport au prix total d'un article */
var sortedItemsPrice = Object.keys(hashMapItemsPrice).sort((a, b) => hashMapItemsPrice[b] - hashMapItemsPrice[a]);

/* Création d'un tableau trié par rapport au credit total d'un article */
var sortedItemsCredit = Object.keys(hashMapItemsCredit).sort((a, b) => hashMapItemsCredit[b] - hashMapItemsCredit[a]);

/*
	Création de l'UI composé de :
  - Un onglet classement, avec des informations en rapport avec les données que nous avons.
  - Un onglet de configuration pour paramétrer PigeonLibre.
*/

$("h2").eq(3).after('<ul class="nav nav-tabs"><li class="active"><a id="historiqueTab">Historique</a></li><li><a id="consommation">Classement</a></li><li><a id="configuration">Configuration</a></li></ul>');

$("table#historique").parent().attr("id", "historiqueDiv");

$("#historiqueTab").on("click", () =>
{
    if($("#historiqueTab").parent().attr("class") != "active")
    {
        $(".active").first().attr("class", "");
        $("#historiqueTab").parent().attr("class", "active");
        $("#historiqueDiv").show();
        $("#configurationDiv").hide();
        $("#consommationDiv").hide();
    }
});

$("#consommation").on("click", () =>
{
    if($("#consommation").parent().attr("class") != "active")
    {
        $(".active").first().attr("class", "");
        $("#consommation").parent().attr("class", "active");
        $("#historiqueDiv").hide();
        $("#configurationDiv").hide();
        $("#consommationDiv").show();
    }
});

$("#configuration").on("click", () =>
{
    if($("#configuration").parent().attr("class") != "active")
    {
        $(".active").first().attr("class", "");
        $("#configuration").parent().attr("class", "active");
        $("#historiqueDiv").hide();
        $("#consommationDiv").hide();
        $("#configurationDiv").show();
    }
});

let classementDiv = $(`<div id="consommationDiv">
<h3>Information concernant vos articles</h3>
<style>
.myCard{
	padding: 10px;
}

.myCard-content{
	text-align:center;
	padding:10px;
	min-height: 150px;
	border-radius:5px;
	color:white;
}

.myCard-green{
	background: rgb(81,187,63);
	background: linear-gradient(0deg, rgba(25,171,0,1) 0%, rgba(81,187,63,1) 100%);
}

.myCard-red{
	background: rgb(81,187,63);
	background: linear-gradient(0deg, rgba(171,0,0,1) 0%, rgba(187,63,63,1) 100%);
}
.myCard-purple{
	background: rgb(187,63,150);
	background: linear-gradient(0deg, rgba(187,63,150,1) 0%, rgba(113,0,171,1) 100%);
}

.myCard-blue{
	background: rgb(74,63,187);
	background: linear-gradient(0deg, rgba(74,63,187,1) 0%, rgba(0,171,156,1) 100%);
}

</style>

<div class="row">
	<div class="col-md-3 myCard">
		<div class="myCard-content myCard-green">
			<h4>
			<i class="glyphicon glyphicon-euro glyphicon-white"></i>
			</h4>
			<h5>Rechargements</h5>
			<h4><span id="rechargementTotal"></span>€</h4>
		</div>
	</div>
	<div class="col-md-3 myCard">
	<div class="myCard-content myCard-red">
			<h4>
			<i class="glyphicon glyphicon-shopping-cart glyphicon glyphicon-white"></i>
			</h4>
			<h5>Dépenses</h5>
			<h4><span id="total"></span>€</h4>
		</div>
	</div>
  <div class="col-md-3 myCard">
    <div class="myCard-content myCard-purple">
        <h4>
        <i class="glyphicon glyphicon-heart glyphicon glyphicon-white"></i>
        </h4>
        <h5>Le plus consommé</h5>
        <h4><span id="mostQuantity"></span></h4>
      </div>
    </div>
  <div class="col-md-3 myCard">
    <div class="myCard-content myCard-blue">
        <h4>
        <i class="glyphicon glyphicon-fire glyphicon glyphicon-white"></i>
        </h4>
        <h5>Le plus onéreux</h5>
        <h4><span id="mostPrice"></span></h4>
      </div>
    </div>
</div>
<h3>Classement des articles par quantité</h3>
<div id="quantityChartDiv"> 
	<canvas id="quantityChart"></canvas>
</div>
<h3>Classement des articles par débit</h3>
<div id="priceChartDiv">
<canvas id="priceChart"></canvas>
</div>
<h3>Classement des articles par crédit</h3>
<div id="creditChartDiv">
<canvas id="creditChart"></canvas>
</div>
</div>`);


let configurationDiv = $(`<div style="display: none;" id="configurationDiv">
<h3>Filtre de début et de fin du classement</h3>
Début : <input class="form-control" id="debut" name="debut" placeholder="YYYY-MM-DD" type="text">
<br>
Fin : <input class="form-control" id="fin" name="fin" placeholder="YYYY-MM-DD" type="text">
<h3>Filtre des articles du classement</h3>
<style>
.subject-info-box-1,
.subject-info-box-2 {
    float: left;
    width: 45%;
}
.subject-info-arrows {
    float: left;
    width: 10%;
    input {
        width: 70%;
        margin-bottom: 5px;
    }
}

#itemList, #stopList {
    height: 400px;
}

</style>
Nombre d'articles dans le classement : <input name="maxClassement" type="number" id="maxClassement" placeholder="0" class="form-control amount-selector" min="0" value="" step="1">
<br>
<div class="subject-info-box-1">
    Filtrer certains articles :
  <select multiple="multiple" id='itemList' class="form-control">
  </select>
  <br>
</div>
<div class="subject-info-arrows text-center">
  <br>
  <input type="button" id="addStopList" value=">" class="btn btn-default" /><br /><br>
  <input type="button" id="removeStopList" value="<" class="btn btn-default" /><br />
  <br>
</div>
<div class="subject-info-box-2">
  <br>
  <select multiple="multiple" id='stopList' class="form-control">
  </select>
  <br>
</div>
<div style="text-align: center;">
<input type="button" id="validate" value="Valider" class="btn btn-default" />
</div>
<br>
Pour signaler un bug ou me faire part de suggestion : <a href="mailto:benjamin.bonvarlet@etu.utc.fr">cliquez-ici</a>.
</div>`)

$("#historiqueDiv").after(classementDiv);

$("#quantityChartDiv").css("height", (25 * maxClassement) + "px");
$("#priceChartDiv").css("height", (27 * maxClassement) + "px");
$("#creditChartDiv").css("height", (32 * maxClassement) + "px");

$("#total").text(totalSpend.toFixed(2));
$("#mostQuantity").text(sortedItemsQuantity[0]);
$("#mostPrice").text(sortedItemsPrice[0]);
$("#mostPriceValue").text(hashMapItemsPrice[sortedItemsQuantity[0]].toFixed(2));
$("#rechargementTotal").text(rechargementTotal.toFixed(2));

function getRandomInt(max) {
  return Math.floor(Math.random() * Math.floor(max));
}

let colors = [];
for(var i = 0 ; i < maxClassement ; i++)
{
  colors.push('rgba(' + getRandomInt(255) + ',' + getRandomInt(255) + ',' + getRandomInt(255) + ')');
}


let ctxQuantityChart = $("#quantityChart");

// Data
let dataQuantity = {
    datasets: [{
        data: sortedItemsQuantity.slice(0, maxClassement).map((item) => hashMapItemsQuantity[item]),
      	backgroundColor: colors
    }],
  

    // These labels appear in the legend and in the tooltips when hovering different arcs
    labels: sortedItemsQuantity.slice(0, maxClassement)
};

// For a pie chart
let quantityChart = new Chart(ctxQuantityChart,{
    type: 'pie',
    data: dataQuantity,
  	options : {
      maintainAspectRatio: false,
    }
});

let ctxPriceChart = $("#priceChart");

// Data
let dataPrice = {
    datasets: [{
        data: sortedItemsPrice.slice(0, maxClassement).map((item) => hashMapItemsPrice[item]).map((item) => item.toFixed(2)),
      	backgroundColor: colors
    }],
  

    // These labels appear in the legend and in the tooltips when hovering different arcs
    labels: sortedItemsPrice.slice(0, maxClassement)
};

// For a pie chart
let priceChart = new Chart(ctxPriceChart,{
    type: 'pie',
    data: dataPrice,
    options : {
      maintainAspectRatio: false,
    }
});

let ctxCreditChart = $("#creditChart");

// Data
let dataCredit = {
    datasets: [{
        data: sortedItemsCredit.slice(0, maxClassement).map((item) => hashMapItemsCredit[item]).map((item) => item.toFixed(2)),
      	backgroundColor: colors
    }],
  
    // These labels appear in the legend and in the tooltips when hovering different arcs
    labels: sortedItemsCredit.slice(0, maxClassement)
};

// For a pie chart
let creditChart = new Chart(ctxCreditChart,{
    type: 'pie',
    data: dataCredit,
    options : {
      maintainAspectRatio: false,
    }
});

$("#historiqueDiv").next().after(configurationDiv);

if(localStorage.getItem("debut") != undefined)
{
  let debutDate = new Date(parseInt(localStorage.getItem("debut")));
  $("#debut").val(debutDate.getFullYear() + "-" + (debutDate.getMonth() + 1) + "-" + debutDate.getDate());
}

if(localStorage.getItem("fin") != undefined)
{
  let debutDate = new Date(parseInt(localStorage.getItem("fin")));
  $("#fin").val(debutDate.getFullYear() + "-" + (debutDate.getMonth() + 1) + "-" + debutDate.getDate());
}

$("#maxClassement").attr("max", sortedItemsQuantity.length);
$("#maxClassement").val(maxClassement);

$("#validate").on("click", () =>
{
  localStorage.setItem("maxClassement", $("#maxClassement").val());
  let newStopList = [];
  $("option", $("#stopList")).each((key, val) => newStopList.push($(val).val()));
  localStorage.setItem("stopList", JSON.stringify(newStopList));
  
  if($("#debut").val() === "")
  {
    localStorage.removeItem("debut");
  }
  else
  {
    localStorage.setItem("debut", +new Date($("#debut").val()));
  }
  
  if($("#fin").val() === "")
  {
    localStorage.removeItem("fin");
  }
  else
  {
    localStorage.setItem("fin", +new Date($("#fin").val()));
  }
  
  location.href = "https://payutc.nemopay.net/";
});

let whiteList = Object.keys(hashMapItemsQuantity).sort();
whiteList.forEach((item) => $("#itemList").append("<option>" + item + "</option>"));
stopList.forEach((item) => $("#stopList").append("<option>" + item + "</option>"));

$("#addStopList").on("click", () =>
{
	$("option:selected", $("#itemList")).each((key, val) => $("#stopList").append("<option>" + $(val).val() + "</option>"));
  $("option:selected", $("#itemList")).remove();
});

$("#removeStopList").on("click", () =>
{
	$("option:selected", $("#stopList")).each((key, val) => $("#itemList").append("<option>" + $(val).val() + "</option>"));
  $("option:selected", $("#stopList")).remove();
});


$("#configurationDiv").hide();
$("#consommationDiv").hide();