Skip to content

Instantly share code, notes, and snippets.

@PabloWestphalen
Created December 3, 2013 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save PabloWestphalen/7772625 to your computer and use it in GitHub Desktop.
Save PabloWestphalen/7772625 to your computer and use it in GitHub Desktop.
/*
* TODO FOR TODAY: date fix, filter by highlight, tabs
*
* background-color: transparent // normal
background-color: rgb(xxx, xxx, xxx) //highlighted
*
* adicionar a variável currentFilteredTasks, que será
*
* nofilter deve bloquear todas as dropdowns. caso cnotrário, isso quebra a minha regra de inversão
*
*
* Total WDLS: ~16
* 1 poll a cada 10 minutos
* daria 768 requests por dia, em média
*
* gerson quer muito o negócio do sumário. o sumário vai mostrar quantas tasks cada membro tem + quem sabe alguns detalhes bacanas
*
* filtrar por região (DIO). usar o highlight do summarizer
*
* PTT tem caching? seria bom se tivesse. aí é só fazer o cache por 10 minutos
*
* caso contrário, posso criar um proxy que faz o cache
* esse proxy pode devolver em JSON pra já facilitar!!!!
*
TODO: * Custom filter presets / save current config as a preset / Maybe custom presets as tabs :O
* Idea: implement tabs that hold the table; "All, Search, Preset 1 Name, Preset 2 Name, ..." the onclick of these tabs just run the appropriate filter
* Botão de save no ptt que na verdade faz o reassign pra si mesmo por de baixo dos panos
* TODO: get rid of button and apply the filters on each component's change
* Um script que mostra o "quando tempo posso levar nessa task" ao lado do estimate time of build step
* Add the "as (role)" field? I sure could, but you know, may not need to
* Definir pra que a url seja allinqueue.php?Enhanced=true
* Create an exception for the project manager filter. it WILL have to go through the standard way of filtering
* Make summary tab that shows: Total tasks. And total tasks with each member.
For each member, show total tasks by role
* Fazer a interface, merge com a funcionalidade de highlight do outro
* Fazer com que o metodo parse receba uma TR, assim fica abstraido aceitando trs individuais
* Adicionar um filter by difficulty que permita lt e gt. Isso significa migrar a propriedade difficulty pra um map, onde difficulty[2] retornaria medium
* Adicionar um filter by publishing date igualsinho ao due WPL, e imediatamente em baixo do mesmo
DONE: * Filter by step name -> filter([crit("step eq", "Build")])
* Filter by builder name done -> filter([crit("assignedTo eq", "C. Zhou")])
* Filter by assigned -> filter([crit("_fn isAssigned eq", false)])
* Filber by role -> filter([crit("role eq", "Pub.")]); // role
* Filter by difficulty -> filter([crit("difficulty eq", "Simple")])
* Filter by overtracked -> filter([crit("isOverTracked eq", true)]);
* Filter by task title equal -> filter([crit("title eq", "[Split:WR975950NG]P4, set 3 V17 migration 6/7")])
* Filter by description like x -> filter([crit("description like", "AP Globalisation")]);
* Filter by platform -> filter([crit("platform eq", "EMEA SWG Import Export")])
* Filter by Slack Time/Total Time Estimate/Tracked Hours/Step Duration -> hideTasks(filter([crit("totalTimeEstimate gt", 50)]))
* Filter by due date WPL -> hideTasks(filter([crit("dueWPL gte", new Date())]))
* Transformar os controles nativos de RADIO pra CHECKBOX. a não ser que sejam mutuamente exclusivos, nesse caso
fazer um disabled. "EX: caso se coloque unassigned, o campo pra escolher o assignedTo fica disabled"
* Create filter types that are compatible with numbers and date [<, <=, >, >=, ==]
* Hide tasks from UI: hideTasks(filter([crit("_fn isAssigned eq", true)]))
* Idea: add the dom element directly as a property of each task
CONSID: * "role has" e "role hasNot" filtram por assigned
* manager não tem como filtrar =/
* filter([crit("assignedTo eq", "A. Luiz Jacovenze")]).length ---> mostra todas as tasks do amaury
* Exemplo de chained filter: filter([crit("assignedTo eq", "A. Luiz Jacovenze"), crit("role eq", "Pub.")]).length
* publishing due date -> só aparece pra PL
total time estimate -> só aparece pra PL
tracked hours - só aparece pra PL
*/
/* Declare a tasks variable along with static ones */
var people = new Array();//["A. Luiz Jacovenze", "A. Wang", "C. Bordac", "C. Gimenez", "C. Ochoa Calvillo", "C. Wang", "C. Zhou", "D. Souza", "E. Hinojosa Ortega", "F. Hornak", "G. Ornelas Uribe", "G. Tubias dos Santos", "I. Rabcan", "J. Coronado Guadarrama", "J. Garcia Gomez Luna", "J. Kmet", "J. Navarro Gonzalez", "J. Ramírez Palacios", "J. Stefanek", "J. Zhang", "L. Fernandez Diaz", "L. Mesina Davalos", "L. Silva", "L. Sun", "L. Zatarain Aguilera", "M. Li", "M. Slabej", "M. Valkovec", "N. Kissova", "P. Wainberg", "Q. Wang", "R. Chrabacka", "R. Smondrk", "S. Arauz Aceves", "S. Mondelo", "S. Santibanez Coronado", "S. Sierra Magallanes", "S. Vrablik", "T. Calaca", "T. Hecht", "T. Li", "T. Stana", "V. Bentui", "V. Kostal", "X. Bai", "X. Liu", "X. Wang", "Y. Liu"];
var roles = ["Pub.", "Web Bldr.", "WDL", "QA Spec."];
var steps = ["WDL Assignment / Tech guidance", "Build", "Quality assurance", "Publish"];
var availablePlatforms = new Array();
var Task = function(){};
Task.prototype.isAssigned = function() {
return !this.assignedTo.match(/Pub\.|Web Bldr\.|WDL|QA Spec\./);
}
var tasks = new Array(),
cl = new Object(),
now = new Date(),
headers = ".ibm-data-table.ibm-sortable-table.ibm-alternate-two thead th",
taskRows = ".ibm-data-table.ibm-sortable-table.ibm-alternate-two tbody tr",
reportArea = $(".ibm-data-table.ibm-sortable-table.ibm-alternate-two caption"),
reportCount,
form = $("form[name='filterForm']"),
filterByAssign = $("#ibm-ptt-filter-by-assign"),
filterByStepName = $("#ibm-ptt-filter-by-step-name"),
filterByWBQAName = $("#ibm-ptt-filter-by-wbqa-name"),
filterByPlatform = $("#ibm-ptt-filter-by-pi-name"),
filterByDueWPL,
filterByOvertracked,
assiUnassignedFilter = $("#assiUnassignedFilter"),
stepNameFilter = $("#stepNameFilter"),
WBQANameFilter = $("#wbQaNameFilter"),
platformFilter = $("#piNameFilter"),
dueWPLOption,
dueWPLFilterMonth,
dueWPLFilterDay;
function setHeaders() {
$(headers).each(function(index) {
index--; // to compensate for the first cell, which is actually a TH. this decreases the index of following cells by 1
switch(this.textContent) {
case "Attachment":
cl.attachment = index;
return true;
case "P":
cl.priority = index;
return true;
case "Assigned to":
cl.assignedTo = index;
return true;
case "Due back to WPL":
cl.dueWPL = index;
return true;
case "Publishing due date":
cl.duePublishing = index;
return true;
case "Slack time(hrs)":
cl.slackTime = index;
return true;
case "Tracked(hrs)":
cl.trackedHours = index;
return true;
case "Step duration(hrs)":
cl.stepDuration = index;
case "Step name":
cl.stepName = index;
return true;
case "PI/Complexity":
cl.difficulty = index;
return true;
case "Total time estimate(hrs)":
cl.totalTimeEstimate = index;
return true;
}
if (this.textContent.indexOf("Task Title/Task") === 0) {
cl.taskTitle = index;
return true;
}
if (this.textContent.indexOf("IET") === 0) {
cl.IET = index;
return true;
}
});
}
function estimateToMinutes(t) {
isNegative = t.charAt(0) == "-";
if(isNegative) {
totalMinutes = (parseFloat((t * -1) * 60));
} else {
totalMinutes = parseFloat(t * 60);
}
hours = Math.floor(totalMinutes / 60);
hours = hours < 10 ? "0" + hours : hours;
minutes = ((totalMinutes / 60).toString().replace(/^[^\.]+/,'')) * 60;
minutes = minutes < 10 ? "0" + minutes : minutes;
return (isNegative? "-" : "") + hours + ":" + minutes;
}
function parseTasks() {
var taskNum = 0;
$(taskRows).each(function(rowIndex) {
$(this).addClass("task" + taskNum); // deprecate this in the future?
nt = new Task();
taskNum++;
$(this).find("td").each(function(col) {
nt.row = this.parentNode;
if(col === cl.priority) {
nt.priority = parseInt($(this).text());
} else if(col === cl.taskTitle) {
nt.title = $(this).find("a").text();
nt.description = $(this).find("span").text();
nt.isHighlighted = !$(this).find("span").eq(0).css("backgroundColor") === "rgba(0, 0, 0, 0)";
console.log("isHighlighted = " + nt.isHighlighted);
//filter([crit("isHighlighted eq", true)]);
} else if(col === cl.assignedTo) {
str = $(this).text();
pattern = /(.+) (as (Pub\.|Web Bldr\.|WDL|QA Spec\.))/;
match = str.match(pattern);
if(match) {
nt.assignedTo = str.match(pattern)[1];
nt.role = str.match(pattern)[3];
if($.inArray(nt.assignedTo, people) == -1) {
people.push(nt.assignedTo);
}
} else {
nt.assignedTo = str;
}
} else if(col === cl.dueWPL) {
//console.log("col [" + cl + "] value = " + $(this).text());
ds = $(this).text();//2013-11-15<br><font style="font-size:9px;">16:59 BRST</font>
year = parseInt(ds.substr(0, 4));
month = parseInt(ds.substr(5, 2)) - 1;
day = parseInt(ds.substr(8, 2));
hour = parseInt(ds.substr(10, 2));
minute = parseInt(ds.substr(13, 2));
//console.log("taskNum = [" + taskNum + "]" + year + ' ' + month + ' ' + day + ' ' + hour + " " + minute);
//console.log("'" + year + '-' + month + '-' + day + ' ' + hour + ":" + minute);
nt.dueWPL = new Date(year, month, day, hour, minute, 0);
} else if(col === cl.slackTime) {
nt.slack = parseFloat($(this).text());
var img = $(this).find("img")[0];
if(img && img.getAttribute("alt").indexOf("time recorded on this task is greater than estimation.") > -1) {
nt.isOverTracked = true;
}
} else if(col === cl.totalTimeEstimate) {
nt.totalTimeEstimate = parseFloat($(this).text());
} else if(col == cl.trackedHours) {
nt.trackedHours = parseFloat($(this).text());
} else if(col === cl.stepDuration) {
nt.duration = parseFloat($(this).text());//estimateToMinutes($(this).text());
} else if(col === cl.stepName) {
nt.step = $(this).text();
} else if(col === cl.difficulty) {
difficulty = $(this).text().match(/(Simple|Medium|Complex|-)/);
if(difficulty) {
if(difficulty === "-") {
nt.difficulty = "Not set";
}
nt.difficulty = difficulty[0];
nt.platform = findPlatform($(this).text());
} else {
nt.difficulty = "unknown";
}
}
//console.log("column [" + i + "] " + $(this).html());
});
tasks.push(nt);
});
console.log("Parsed " + tasks.length + " task total.");
reportArea.append(
$('<span>', {
id: 'ibm-ptt-report-area',
name: 'ibm-ptt-report-area',
html: ' | Displaying: <strong id="ibm-ptt-report-count">' + tasks.length + '</strong> tasks'
})
);
reportCount = $("#ibm-ptt-report-count");
people.sort();
}
function findAvailablePlatforms() {
$("#piNameFilter option").each(function() {
var platform = $(this).text();
availablePlatforms.push(platform);
});
availablePlatforms.sort(function(a, b) {
return b.length - a.length;
});
}
function findPlatform(text) {
for(var i = 0; i < availablePlatforms.length; i++) {
if(text.indexOf(availablePlatforms[i]) > -1) {
return availablePlatforms[i];
}
}
return "unknown";
}
function resetTasks() {
$(taskRows).show();
}
function hideTasks(tasks) {
filteredTasks = tasks;
$(taskRows).hide(); // hide all tasks....
$(filteredTasks).each(function() { //except those matched by the filter
$(this.row).show();
});
}
function exec(f /*, args */) {
var args = Array.prototype.slice.call(arguments).splice(1);
return window[f].apply(this, args);
}
function filter() { // args é um array de objetos com o nome do header, o valor a comparar e o tipo da função
var args = Array.prototype.slice.call(arguments).splice(0)[0];
var filteredTasks = tasks;
for(i = 0; i < args.length; i++) {
with(args[i]) {
filteredTasks = filteredTasks.filter(function(t) { return exec(type, t, field, value) });
}
}
return filteredTasks;
}
function crit(q, v) {
c = q.split(/\s/);
if(c[0] == "_fn") { // ex; _fn isAssigned eq
return {"field" : c[1], "type" : c[2], "value" : v };
} else {
return {"field" : c[0], "type" : c[1], "value" : v };
}
}
function isDate(/* args... */) {
var args = Array.prototype.slice.call(arguments);
for(var i = 0; i < args.length; i++) {
if(!args[i].getTime) {
return false;
}
}
return true;
}
function eq(t, f, a) { // task element, field to search, value to compare
//console.log("t = " + t + " f = " + f + " a = " + a);
if(t[f] && isDate(t[f], a)) {
return t[f].getTime() == a.getTime();
} else {
if(typeof t[f] == "function") {
return t[f]() == a;
}
return t[f] == a;
}
}
function notEq(t, f, a) {
return !eq(t, f, a);
}
function gt(t, f, a) {
if(t[f] && isDate(t[f], a)) {
return t[f].getTime() > a.getTime();
} else {
return t[f] > a;
}
}
function gte(t, f, a) {
if(t[f] && isDate(t[f], a)) {
return t[f].getTime() >= a.getTime();
} else {
return t[f] >= a;
}
}
function lt(t, f, a) {
if(t[f] && isDate(t[f], a)) {
return t[f].getTime() < a.getTime();
} else {
return t[f] < a;
}
}
function lte(t, f, a) {
if(t[f] && isDate(t[f], a)) {
return t[f].getTime() <= a.getTime();
} else {
return t[f] <= a;
}
}
function contains(t, f, a) { // task element, field to search, value to compare
if(typeof t[f] === "string" && typeof a === "string") {
return t[f].toLowerCase().indexOf(a.toLowerCase()) > -1;
}
}
function has(t, f, a) {
return t[f] ? true : false;
}
function hasNot(t, f, a) {
return t[f] ? false : true;
}
function scrollToResults() {
/*$('html, body').animate({
scrollTop: $("#ibm-ptt-report-area").offset().top - 150
}, 400);*/
// this can be turned on by uncommenting the above code
}
function hijackNativeFilter() {
//Remove original events
$(form).children().off();
$(form).find("input[onclick]").removeAttr("onclick");
$(form).find("input[type='submit']").attr("type", "button");
$(form).find("input[type='button']").attr("id", "filterButton");
//Change elements from radio to checkbox
$(form).find("input[type='radio']").attr("type", "checkbox");
//Add new handlers
//When you check the filter checkbox, uncheck all the other checkboxes
$("#ibm-ptt-no-filter").change(function() {
$(form).find("input[type='checkbox']").each(function() {
if($(this).attr("id") !== "ibm-ptt-no-filter") {
$(this).prop("checked",false);
}
});
if(this.checked) {
resetTasks();
$(reportCount).text(tasks.length);
scrollToResults();
}
});
// Toggle the disabled state of the isAssigned dropdown according to the check state of the isAssigned filter
$(filterByAssign).change(function() {
$(assiUnassignedFilter).prop("disabled",!$(assiUnassignedFilter).prop("disabled")); // As seen on http://stackoverflow.com/questions/4702000/toggle-input-disabled-attribute-using-jquery
});
$(assiUnassignedFilter).change(function() {
if($(this).find("option:selected").text() == "unassigned") {
$(filterByWBQAName).prop("checked", false);
}
});
//Remove the project manager filter temporarily
$("#ibm-ptt-filter-by-pm").next().next().remove();
$("#ibm-ptt-filter-by-pm").next().remove();
$("#ibm-ptt-filter-by-pm").remove();
$("#projManagerFilter").prev().remove();
$("#projManagerFilter").remove();
// Toggle the disabled state of the stepName dropdown according to the check state of the stepName filter
$(filterByStepName).change(function() {
$(stepNameFilter).prop("disabled",!$(stepNameFilter).prop("disabled")); // As seen on http://stackoverflow.com/questions/4702000/toggle-input-disabled-attribute-using-jquery
});
// Toggle the disabled state of the stepName dropdown according to the check state of the stepName filter
$(filterByWBQAName).change(function() {
$(WBQANameFilter).prop("disabled",!$(WBQANameFilter).prop("disabled")); // As seen on http://stackoverflow.com/questions/4702000/toggle-input-disabled-attribute-using-jquery
$(assiUnassignedFilter).val("1");
});
rewriteWBQANameList();
// Toggle the disabled state of the platform dropdown according to the check state of the platoform filter
$(filterByPlatform).change(function() {
$(platformFilter).prop("disabled",!$(platformFilter).prop("disabled")); // As seen on http://stackoverflow.com/questions/4702000/toggle-input-disabled-attribute-using-jquery
});
}
// Return an array of jquery objects. one for the month pulldown and one for the day pulldown
function getDatePulldowns(id) {
var dayDD,
dayOpts = new Array(),
monthDD,
monthOpts = new Array(),
hourDD,
hourOps = new Array();
//Build day dropdown
dayDD = $("<select>", {
disabled: "disabled",
"class": "tiny",
id: id + "-day",
name: id + "-day",
});
for(var i = 1; i <= 31; i++) {
var opt = $("<option>", {
label: i,
value: i,
text: i
});
if(i === now.getDay()) {
opt.prop("selected", true);
}
dayOpts.push(opt);
}
dayDD.append(dayOpts);
//Build month dropdown
months = "January, February, March, April, May, June, July, August, September, October, November, December".split(", ");
monthDD = $("<select>", {
disabled: "disabled",
"class": "small",
id: id + "-month",
name: id + "-month",
});
for(var i = 0; i < months.length; i++) {
var opt = $("<option>", {
label: months[i],
value: i,
text: months[i]
});
if(i === now.getMonth()) {
opt.prop("selected", true);
}
monthOpts.push(opt);
}
monthDD.append(monthOpts);
return [monthDD, dayDD];
}
function getFilterOptionPullDown(id, types) {
var types = types.split(", "),
typeOpts = new Array(),
typeText;
optDD = $("<select>", {
disabled: "disabled",
id: id,
name: id,
});
for(var i = 0; i < types.length; i++) {
switch(types[i]) {
case "lt":
typeText = "<";
break;
case "lte":
typeText = "<=";
break;
case "eq":
typeText = "Equals";
break;
case "notEq":
typeText = "Does not equal";
break;
case "contains":
typeText = "Contains";
break;
case "gt":
typeText = ">";
break;
case "gte":
typeText = ">=";
break;
}
typeOpts.push($("<option>", {
label: typeText,
value: types[i],
text: typeText
}));
}
optDD.append(typeOpts);
return optDD;
}
function createCustomElements() {
// step 2b -> add comparison dropdowns, but these depend on the field, i can specify by an argument array of strings like "eq, contains"
// Adds our custom filtering to the button
$(form).find("input, select").change(function() {
applyFilters();
});
//$("#filterButton").on("click", applyFilters);
var checkParent = $("#ibm-ptt-no-filter"),
optionParent = $("#piNameFilter");
dd1 = $("<input>", {
name: "filterchk",
id: "ibm-ptt-filter-by-due-wpl",
type: "checkbox"
}),
dd2 = $("<label>", {
"for": "ibm-ptt-filter-by-due-wpl",
text: " Filter the tasks by Due date to WPL"
});
dd3 = $("<br>"),
dd4 = $("<label>", {
"for": "dueWPLOption",
"class": "ibm-access ibm-label-disabled",
text: "Due to WPL ",
}),
dd5 = getFilterOptionPullDown("dueWPLOption", "lt, lte, eq, notEq, gt, gte"),
ddOpts = getDatePulldowns("dueWPLFilter");
/***** overtracked ****/
ot1 = $("<input>", {
name: "filterchk",
id: "ibm-ptt-filter-by-overtracked",
type: "checkbox"
}),
ot2 = $("<label>", {
"for": "ibm-ptt-filter-by-overtracked",
text: " Filter the Overtracked tasks"
});
ot3 = $("<br>"),
/**** #overtracked ****/
checkParent.before([dd1, dd2, dd3, ot1, ot2, ot3]);
optionParent.after([$("<br>"), dd4, dd5, ddOpts[0], ddOpts[1]]);
//Set new elements references
filterByDueWPL = $("#ibm-ptt-filter-by-due-wpl"),
filterByOvertracked = $("#ibm-ptt-filter-by-overtracked"),
dueWPLOption = $("#dueWPLOption"),
dueWPLFilterMonth = $("#dueWPLFilter-month"),
dueWPLFilterDay = $("#dueWPLFilter-day");
//Add events
//When you check any of the checkboxes, uncheck the nofilter checkbox
$(form).find("input[type='checkbox']").change(function() {
if($(this).attr("id") !== "ibm-ptt-no-filter") {
if(this.checked) {
$("#ibm-ptt-no-filter").prop("checked", false);
}
}
});
$(filterByDueWPL).change(function() {
$(dueWPLOption).prop("disabled",!$(dueWPLOption).prop("disabled"));
$(dueWPLFilterMonth).prop("disabled",!$(dueWPLFilterMonth).prop("disabled"));
$(dueWPLFilterDay).prop("disabled",!$(dueWPLFilterDay).prop("disabled"));
});
}
function applyFilters() {
var crits = new Array();
//First, handle the easiest one -> no filter
if($("#ibm-ptt-no-filter").prop("checked")) {
resetTasks();
return;
}
if(filterByAssign.prop("checked")) {
if(assiUnassignedFilter.val() == 1) {
crits.push(crit("_fn isAssigned eq", true));
} else {
crits.push(crit("_fn isAssigned eq", false));
}
}
if(filterByStepName.prop("checked")) {
crits.push(crit("step eq", stepNameFilter.find("option:selected").text()));
}
if(filterByWBQAName.prop("checked")) {
crits.push(crit("assignedTo eq", WBQANameFilter.find("option:selected").text()));
}
if(filterByPlatform.prop("checked")) {
crits.push(crit("platform eq", platformFilter.find("option:selected").text()));
}
if(filterByDueWPL.prop("checked")) {
var dueWPLCompareDate = new Date(now.getFullYear(), dueWPLFilterMonth.val(), dueWPLFilterDay.val(), 0, 0, 0);
//nt.dueWPL = new Date(year, month, day, hour, minute, 0);
xxx = dueWPLCompareDate;
console.log("Comparing if now " + dueWPLOption.val() + " " + dueWPLCompareDate);
crits.push(crit("dueWPL " + dueWPLOption.val(), dueWPLCompareDate));
//nt.dueWPL = ;
}
if(filterByOvertracked.prop("checked")) {
crits.push(crit("isOverTracked eq", true));
}
console.log("Crits are as following: ");
console.log(crits);
var filteredTasks = filter(crits);
hideTasks(filteredTasks);
$(reportCount).text(filteredTasks.length);
scrollToResults();
}
function rewriteWBQANameList() {
$("#wbQaNameFilter option").remove();
for(var i = 0; i < people.length; i++) {
$("#wbQaNameFilter").append($("<option>", {
label: people[i],
value: people[i],
text: people[i]
}));
}
}
setHeaders();
findAvailablePlatforms();
parseTasks();
hijackNativeFilter();
createCustomElements();
/*result =
console.log(result);*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment