(function () { "use strict"; angular.module("shared") .service("ChartsDataFormatterService", ["DaysAgoDateService", "_", "moment", function(DaysAgoDateService, _, moment) { var service = this; service.numDays7DaysFilter = 7; service.numDays30DaysFilter = 30; service.numDays90DaysFilter = 90; service.defaultDateFormat = "YYYY-MM-DD"; service.dateFormat7DaysFilter = "MM/DD/YYYY"; service.dateFormat30DaysFilter = "MM/DD/YYYY"; service.dateFormat90DaysFilter = "MMMM"; service.dayUnitString = "day"; service.weekUnitString = "week"; function isEmpty(data) { return data == null || data.length === 0; } function getFormattedAverageQuizScoresData(data) { try { if (isEmpty(data)) { return data; } var numDaysBetweenStartDateAndEndDate = DaysAgoDateService.getNumDaysBetweenStartDateAndEndDate(); var intervals = getIntervals(numDaysBetweenStartDateAndEndDate); var groupedQuizScoresDataByInterval = getGroupedDataByInterval(data, numDaysBetweenStartDateAndEndDate); return getAverageQuizScoresDataByInterval(groupedQuizScoresDataByInterval, intervals); } catch (error) { console.debug(error); return null; } } function getFormattedLoginData(data) { try { if (isEmpty(data)) { return data; } var numDaysBetweenStartDateAndEndDate = DaysAgoDateService.getNumDaysBetweenStartDateAndEndDate(); var intervals = getIntervals(numDaysBetweenStartDateAndEndDate); var groupedLoginDataByInterval = getGroupedDataByInterval(data, numDaysBetweenStartDateAndEndDate); return getAggregateLoginDataByInterval(groupedLoginDataByInterval, intervals); } catch (error) { console.debug(error); return null; } } function getIntervals(numDaysBetweenStartDateAndEndDate) { var intervals = []; var currentDate = moment(DaysAgoDateService.getStartDateString(), service.defaultDateFormat); var endDate = moment(DaysAgoDateService.getEndDateString(), service.defaultDateFormat); while (!currentDate.isAfter(endDate, service.dayUnitString)) { var interval = getInterval(currentDate, numDaysBetweenStartDateAndEndDate); if (!intervalExists(intervals, interval)) { intervals.push(interval); } currentDate = currentDate.add(1, service.dayUnitString); } return intervals; } function intervalExists(intervals, queryInterval) { return intervals.some(function(interval) { return interval === queryInterval; }); } function getGroupedDataByInterval(data, numDaysBetweenStartDateAndEndDate) { return _.groupBy(data, function(datum) { var currentDate = moment(datum.date, service.defaultDateFormat); return getInterval(currentDate, numDaysBetweenStartDateAndEndDate); }); } function getInterval(currentDate, numDaysBetweenStartDateAndEndDate) { if (numDaysBetweenStartDateAndEndDate <= service.numDays7DaysFilter) { return currentDate.format(service.dateFormat7DaysFilter); } else if (numDaysBetweenStartDateAndEndDate <= service.numDays30DaysFilter) { var weekStart = currentDate.startOf(service.weekUnitString).format(service.dateFormat30DaysFilter); var weekEnd = currentDate.endOf(service.weekUnitString).format(service.dateFormat30DaysFilter); return weekStart + "-" + weekEnd; } else if (numDaysBetweenStartDateAndEndDate <= service.numDays90DaysFilter) { return currentDate.format(service.dateFormat90DaysFilter); } else { throw new Error("Date range not found"); } } function getAverageQuizScoresDataByInterval(groupedQuizScoresData, intervals) { var averageQuizScoresDataByInterval = []; for (var i = 0; i < intervals.length; ++i) { averageQuizScoresDataByInterval.push({interval: intervals[i]}); } Object.keys(groupedQuizScoresData).forEach(function(interval) { var numCorrectQuestions = 0; var numTotalQuestions = 0; for (var i = 0; i < groupedQuizScoresData[interval].length; ++i) { numCorrectQuestions += parseInt(groupedQuizScoresData[interval][i].quiz_question_correct); numTotalQuestions += parseInt(groupedQuizScoresData[interval][i].quiz_question_total); } var intervalIndex = getIntervalIndex(averageQuizScoresDataByInterval, interval); averageQuizScoresDataByInterval[intervalIndex].average = getPercentage(numCorrectQuestions, numTotalQuestions, true); }); return averageQuizScoresDataByInterval; } function getAggregateLoginDataByInterval(groupedLoginChartData, intervals) { var aggregateLoginDataByInterval = []; for (var i = 0; i < intervals.length; ++i) { aggregateLoginDataByInterval.push({interval: intervals[i]}); } Object.keys(groupedLoginChartData).forEach(function(interval) { var loginTime = 0; var incentiveTime = 0; for (var i = 0; i < groupedLoginChartData[interval].length; ++i) { loginTime += parseInt(groupedLoginChartData[interval][i].loginTime); incentiveTime += parseInt(groupedLoginChartData[interval][i].incentiveTime); } var intervalIndex = getIntervalIndex(aggregateLoginDataByInterval, interval); aggregateLoginDataByInterval[intervalIndex].loginTime = loginTime; aggregateLoginDataByInterval[intervalIndex].incentiveTime = incentiveTime; }); return aggregateLoginDataByInterval; } function getIntervalIndex(intervals, interval) { for (var i = 0; i < intervals.length; ++i) { if (intervals[i].hasOwnProperty("interval") && intervals[i].interval === interval) { return i; } } return -1; } function addPercentageCorrectProperty(data) { try { if (isEmpty(data)) { return data; } for (var i = 0; i < data.length; ++i) { data[i].percentage_correct = getPercentage(data[i].correct_answers, data[i].total_questions, false); } return data; } catch (error) { console.debug(error); return null; } } function getPercentage(numCorrectQuestions, numTotalQuestions, shouldRound) { var numCorrectQuestionsInt = parseInt(numCorrectQuestions); var numTotalQuestionsInt = parseInt(numTotalQuestions); if (!isNaN(numCorrectQuestionsInt) && !isNaN(numTotalQuestionsInt) && numTotalQuestionsInt > 0) { if (shouldRound) { return Math.round(numCorrectQuestionsInt / numTotalQuestionsInt * 100); } else { return numCorrectQuestionsInt / numTotalQuestionsInt * 100; } } else { return null; } } function getTopAndBottomComprehensionSkills(data) { try { data = getTopAndBottomComprehensionSkillsMapping(data); data = getDataWithoutNullPercentageCorrect(data); if (isEmpty(data)) { return { "topComprehensionSkills": null, "bottomComprehensionSkills": null } } data.sort(comprehensionsSkillsSortComparator); var numSkillsPerSection = 3; var topComprehensionSkills = data.splice(0, numSkillsPerSection); var bottomComprehensionSkills = null; if (data.length > 0) { bottomComprehensionSkills = data.splice(data.length - numSkillsPerSection, numSkillsPerSection); } return { "topComprehensionSkills": topComprehensionSkills, "bottomComprehensionSkills": bottomComprehensionSkills } } catch (error) { console.debug(error); return { "topComprehensionSkills": null, "bottomComprehensionSkills": null } } } function getTopAndBottomComprehensionSkillsMapping(data) { if (isEmpty(data)) { return data; } return data.map(function(datum) { datum.full_skill_name = datum.skill_name; if (datum.skill_name.length > 20) { datum.skill_name = datum.skill_name.substring(0, 20) + "..."; } datum.correct_answers = parseInt(datum.correct_answers); datum.total_questions = parseInt(datum.total_questions); if (!isNaN(datum.correct_answers) && !isNaN(datum.total_questions) && datum.total_questions > 0) { datum.percentage_correct = (datum.correct_answers * 100.0) / datum.total_questions; } else { datum.percentage_correct = null; } return datum; }); } function getDataWithoutNullPercentageCorrect(data) { if (isEmpty(data)) { return data; } return _.filter(data, function(datum) { return datum.hasOwnProperty("percentage_correct") && datum.percentage_correct != null; }); } function comprehensionsSkillsSortComparator(a, b) { if (a.percentage_correct < b.percentage_correct) { return 1; } else if (a.percentage_correct > b.percentage_correct) { return -1; } else if (a.skill_name < b.skill_name) { return -1; } else if (a.skill_name > b.skill_name) { return 1; } else { return 0; } } return { getFormattedAverageQuizScoresData: getFormattedAverageQuizScoresData, getFormattedLoginData: getFormattedLoginData, addPercentageCorrectProperty: addPercentageCorrectProperty, getTopAndBottomComprehensionSkills: getTopAndBottomComprehensionSkills } }]); })();