Skip to content

Instantly share code, notes, and snippets.

@cmcculloh-kr
Forked from scottw-kr/Autofill Oneview Timesheets.md
Last active February 10, 2025 18:49
Show Gist options
  • Save cmcculloh-kr/f5643f9d7a1b03a7bf09a6c1de3d21a6 to your computer and use it in GitHub Desktop.
Save cmcculloh-kr/f5643f9d7a1b03a7bf09a6c1de3d21a6 to your computer and use it in GitHub Desktop.

Auto-fill OneView Timesheets

Forked from Michael Bruno

Install

  1. Download a plugin called Tamper Monkey.
  2. Navigate to http://oneview.kroger.com/
  3. Click the Tampermonkey extension icon in your browser, and select "create new script"
  4. Paste the script into the function body.

Configure

  1. Update the task list with the tasks you have and the values you'd like to enter for them.
  2. Adjust the following default values to suit your needs:
    	// Fill in your default week here. "A" will be "Auto" assigned any remaining hours, assuming an 8 hour day.
     const defaultWeek = {
     	work: ["A", "A", "A", "A", "A"],
     	plan: [1.5, 1.5, 1.5, 1.5, 1],
     	admin: [2.0, 0.5, 0.5, 0.5, 0.5],
     	train: [0, 0, 0, 0, 0],
     	pto: [0, 0, 0, 0, 0],
     	hol: [0, 0, 0, 0, 0],
     };
    

Using

  1. Open Oneview
  2. Click "populate"
  3. refresh page
  4. Adjust numbers as necessary (leave "A" in whatever category should be the catch-all for that day)
  5. Click "autofill"
  6. Click "Submit for Approval"
  7. Close the dumn page
// ==UserScript==
// @name Autofill OneView Timesheet
// @namespace http://tampermonkey.net/
// @version 1.1.5
// @description Help you save some time filling out your timesheet
// @author mbruno-kr, scottw-kr, cmcculloh-kr
// @match http://oneview.kroger.com/niku/nu
// @icon https://www.google.com/s2/favicons?sz=64&domain=kroger.com
// @grant none
// ==/UserScript==
(function () {
("use strict");
const taskTypes = [
{
id: "work",
description: "KDS",
guidance: "This is your primary billable category.",
},
{
id: "plan",
description: "OKR Discovery & Agile Ceremonies",
guidance:
"All team events such as refinement, scrum events, standup, retro etc.",
},
{
id: "admin",
description: "General Admin",
guidance:
"Town Hall, OKR Kickoff, 1:1, Interviews, Web-Guild, organizational emails/newsletters, Machine updates, On-boarding, Town Halls, All-hands, Benefits sessions, Company Webinars, Quarterly/Annual review, VTO, Jury Duty, Travel",
},
{
id: "train",
description: "Non Project Training",
guidance:
"L&D Day, MyInfo learning, LUMA, Pluralsight and other Kroger related",
},
{
id: "pto",
description: "PTO/ Vacation/ Approved Absences",
guidance:
"Health & Wellness days, Vacation, Approve Absence, Personal days, Bereavement.",
},
{
id: "hol",
description: "Holiday",
guidance: "Corporate Holidays",
},
];
// Fill in your default week here. "A" will be "Auto" assigned any remaining hours, assuming an 8 hour day.
const defaultWeek = {
work: ["A", "A", "A", "A", "A"],
plan: [1.5, 1.5, 1.5, 1.5, 1],
admin: [2.0, 0.5, 0.5, 0.5, 0.5],
train: [0, 0, 0, 0, 0],
pto: [0, 0, 0, 0, 0],
hol: [0, 0, 0, 0, 0],
};
const expectedOutcome = {
work: [5.5, 5.5, 7, 4.5, 0],
plan: [1.5, 1, 0, 3, 1],
admin: [1, 1.5, 1, 0.5, 1.5],
train: [0, 0, 0, 0, 0],
pto: [0, 0, 0, 0, 0],
hol: [0, 0, 0, 0, 6],
};
const appendGuidance = () => {
taskTypes.forEach((taskType) => {
const link = document.querySelector(
`a[title*="${taskType.description}"]`
);
if (link) {
link.innerHTML += ` - ${taskType.guidance}`;
}
});
};
const totalForDayAcrossCategories = (week, day) => {
let total = 0;
Object.keys(week).forEach((category) => {
console.log("week, category", category, day, week[category][day]);
total += +week[category][day] > 0 ? +week[category][day] : 0;
});
console.log("totalForDayAcrossCategories", total);
return total;
};
const doAutoCalculation = (week) => {
const autoWeek = {};
Object.keys(week).forEach((category, i) => {
autoWeek[category] = [];
for (let i = 0; i < week[category].length; i++) {
autoWeek[category][i] =
week[category][i] === "A"
? 8 - totalForDayAcrossCategories(week, i)
: week[category][i];
}
return autoWeek[category];
});
return autoWeek;
};
// UNIT TESTS
// console.log("expectedOutcome", expectedOutcome);
// console.assert(compareWeeks(finalizedWeek, expectedOutcome), "Weeks do not match");
// console.assert(totalForDayAcrossCategories(finalizedWeek, 0) === 5, "Total for day 0 is not 5");
// console.assert(false, "This is a test");
// const compareWeeks = (week1, week2) => {
// let result = true;
// Object.keys(week1).forEach((category) => {
// for (let i = 0; i < week1[category].length; i++) {
// if (week1[category][i] !== week2[category][i]) {
// console.log(
// "Weeks do not match at " +
// category +
// " " +
// i +
// " " +
// week1[category][i] +
// " " +
// week2[category][i]
// );
// result = false;
// }
// }
// });
// return result;
// };
function getDays() {
let days = [];
document.querySelectorAll('[data-columnid="day"]').forEach((e) => {
let content = e.innerText;
if (content.length > 0) {
days.push(content.replace(/\n/g, " ").trim().replace(" ", ", "));
}
});
return days;
}
const getCurrentValues = () => {
let days = getDays();
let weekDays = days.slice(1, 6);
// For each weekday, fill each task's values
let weekValues = {};
taskTypes.forEach((task) => {
weekValues[task.id] = [];
});
weekDays.forEach((dayOfWeek) => {
taskTypes.forEach((task) => {
let day = document.querySelector(
`input[alt*='${dayOfWeek}'][title*='${task.description}']`
);
weekValues[task.id].push(day.value);
});
});
console.log("weekValues", weekValues);
return weekValues;
};
const getWeekValues = () => {
const mergedWeek = getCurrentValues();
const finalizedWeek = doAutoCalculation(mergedWeek);
return finalizedWeek;
};
const autoFillWithDefaultValues = () => {
autoFill(defaultWeek);
};
const autoFillWithOverrides = () => {
const weekValues = getWeekValues();
autoFill(weekValues);
};
function autoFill(weekvalues) {
let days = getDays();
let weekDays = days.slice(1, 6);
// For each weekday, fill each task's values
weekDays.forEach((dayOfWeek) => {
taskTypes.forEach((task) => {
let day = document.querySelector(
`input[alt*='${dayOfWeek}'][title*='${task.description}']`
);
if (!day) {
const msg = `Cannot find activity "${task.description}" on ${dayOfWeek}`;
alert(msg);
throw Error(msg);
}
// Add weekvalues value for day & id
const newValueForDay = weekvalues[task.id][weekDays.indexOf(dayOfWeek)];
day.value = newValueForDay;
});
});
// submitForm("page", "timeadmin.saveTimesheet");
}
function addAutofillButton() {
let container = document.querySelector('div[class*="ppm_button_bar"]');
if (!container) {
setTimeout(main, 200);
return;
}
if (isEntryPage()) {
// if there is no button
if (!container.querySelector('button[id="auto-fill"]')) {
let button = document.createElement("button");
button.id = "auto-fill";
button.innerText = "Auto Fill";
button.className = "ppm_button";
button.style = "box-shadow: 0 0 15px 3px rgb(182 148 92, 1);";
button.onclick = autoFillWithOverrides;
container.appendChild(button);
} else {
const input = document.querySelectorAll(
'input[name="actuals_hours"]'
)[1];
// if the form is still empty
if (input && input.value === "") {
const xpath = "//button[text()='Auto Fill']";
const matchingElement = document.evaluate(
xpath,
document,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue;
matchingElement.click();
}
}
}
}
const isEntryPage = () =>
document.querySelectorAll('img[title="Delete"]')[0] !== undefined;
function main() {
const onTimeSheetPage =
window.location.hash.indexOf("editTimesheet") !== -1;
if (onTimeSheetPage) {
setTimeout(() => {
console.log("AutoFill Running");
setTimeout(appendGuidance, 100);
setTimeout(addAutofillButton, 100);
// autofill with default values
setTimeout(autoFillWithDefaultValues, 100);
}, 500);
}
}
main();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment