/// <summary> /// Walks each existing date in the calendar, and resets the dates based on the defaults. /// </summary> public void ResetDefaults() { var startDate = DataSourceBackend.Instance.SchoolDismissalSettingsBackend.GetDefault().DayFirst; //school year start date var endDate = DataSourceBackend.Instance.SchoolDismissalSettingsBackend.GetDefault().DayLast; //school year end date var today = UTCConversionsBackend.UtcToKioskTime(DateTimeHelper.Instance.GetDateTimeNowUTC()).Date; var currentDate = startDate; //loop variable while (currentDate.CompareTo(endDate) <= 0) { var currentCalendarModel = ReadDate(currentDate); //if the calendar model for that date does not exit yet, create a new one if (currentCalendarModel == null) { currentCalendarModel = new SchoolCalendarModel(currentDate); currentCalendarModel = SchoolCalendarBackendHelper.SetDefault(currentCalendarModel); //use current default settings Create(currentCalendarModel); } //if the calendar model for that date is not modified, and is after today, reset to default if (!currentCalendarModel.Modified && currentDate.CompareTo(today) > 0) { currentCalendarModel = SchoolCalendarBackendHelper.SetDefault(currentCalendarModel); //use current default settings Update(currentCalendarModel); } currentDate = currentDate.AddDays(1); } }
/// <summary> /// Generate a leaderboard. Rank students according to their attended minutes in this week. /// </summary> /// <returns></returns> public List <StudentModel> GenerateLeaderboard() { var dayNow = UTCConversionsBackend.UtcToKioskTime(DateTimeHelper.Instance.GetDateTimeNowUTC()).Date; //today's date var thisMonday = dayNow.AddDays(-((dayNow.DayOfWeek - DayOfWeek.Monday + 7) % 7)); //this Monday's date var studentList = DataSourceBackend.Instance.StudentBackend.Index(); //student list foreach (var student in studentList) { student.AttendedMinutesThisWeek = 0; //reset var currentDate = thisMonday; //loop variable while (currentDate.CompareTo(dayNow) < 0) //loop until today, don't include today { //get today's school calendar model var myToday = DataSourceBackend.Instance.SchoolCalendarBackend.ReadDate(currentDate); if (myToday != null && myToday.SchoolDay) { var myRange = student.Attendance .Where(m => UTCConversionsBackend.UtcToKioskTime(m.In).Date == currentDate.Date) .OrderByDescending(m => m.In).ToList(); if (myRange.Any()) { // a list containing all intervals in this day List <Interval> intervals = new List <Interval>(); //loop through all attendance records in my range foreach (var item in myRange) { TimeSpan timeIn = UTCConversionsBackend.UtcToKioskTime(item.In).TimeOfDay; TimeSpan timeOut = UTCConversionsBackend.UtcToKioskTime(item.Out).TimeOfDay; Interval inter = new Interval(timeIn, timeOut); intervals.Add(inter); } var earlyWindow = SchoolDismissalSettingsBackend.Instance.GetDefault().EarlyWindow; var lateWindow = SchoolDismissalSettingsBackend.Instance.GetDefault().LateWindow; //the time from which duration starts to count var start = myToday.TimeStart.Add(-earlyWindow); //the time that duration counts until var end = myToday.TimeEnd.Add(lateWindow); //Calculate hours attended on this day var dailyTotalTime = CalculateHoursAttended(intervals, start, end); student.AttendedMinutesThisWeek += (int)dailyTotalTime.TotalMinutes; } } currentDate = currentDate.AddDays(1); } } //sort var leaderboard = studentList.OrderByDescending(m => m.AttendedMinutesThisWeek).ToList(); return(leaderboard); }
/// <summary> /// Sets the student to be logged In /// </summary> /// <param name="id">The Student ID</param> public void SetLogIn(StudentModel data) { if (data == null) { return; } data.Status = StudentStatusEnum.In; var currentTime = DateTimeHelper.Instance.GetDateTimeNowUTC(); // Update the Attendance Log var temp = new AttendanceModel { In = currentTime, Emotion = data.EmotionCurrent, StudentId = data.Id }; var currentKioskDate = UTCConversionsBackend.UtcToKioskTime(currentTime).Date; //the school day model var schoolDay = DataSourceBackend.Instance.SchoolCalendarBackend.ReadDate(currentKioskDate); //set auto punch-out time if (schoolDay == null) //if today is not a school day, use the default dismissal time as punch out time { var defaultEndTime = SchoolDismissalSettingsBackend.Instance.GetDefault().EndNormal; temp.Out = UTCConversionsBackend.KioskTimeToUtc(currentKioskDate.Add(defaultEndTime)); } else { temp.Out = UTCConversionsBackend.KioskTimeToUtc(currentKioskDate.Add(schoolDay.TimeEnd)); schoolDay.HasAttendance = true; //Save the school calendar change DataSourceBackend.Instance.SchoolCalendarBackend.Update(schoolDay); } data.Attendance.Add(temp); Update(data); }
///// <summary> ///// Update token ///// </summary> ///// <param name="id"></param> //public void UpdateToken(string id) //{ // if (string.IsNullOrEmpty(id)) // { // return; // } // var data = DataSource.Read(id); // if (data == null) // { // return; // } // //get the list of new attendances, for which token amount has not been added yet. // var newLogIns = data.Attendance.Where(m => m.IsNew); // //for each new attendance, calculate effective duration and according collected tokens, // //add to current tokens of the student. // foreach (var attendance in newLogIns) // { // var effectiveDuration = CalculateEffectiveDuration(attendance); // //todo: since hours attended is rounded up, need to prevent the case where consecutive check-ins in a short period // //todo: of time could add 1 tokens everytime // var collectedTokens = (int)Math.Ceiling(effectiveDuration.TotalHours); // data.Tokens += collectedTokens; // //mark it as old attendance // attendance.IsNew = false; // } //} /// <summary> /// private helper method to calculate effective duration /// </summary> /// <param name="attendance"></param> /// <returns></returns> public TimeSpan CalculateEffectiveDuration(AttendanceModel attendance) { if (attendance == null) { return(TimeSpan.Zero); } //the school day model, will use the school day's start time and end time later var schoolDay = DataSourceBackend.Instance.SchoolCalendarBackend.ReadDate(UTCConversionsBackend.UtcToKioskTime(attendance.In)); if (schoolDay == null) { return(TimeSpan.Zero); } var start = schoolDay.TimeStart.Add(-SchoolDismissalSettingsBackend.Instance.GetDefault().EarlyWindow); //the time from which duration starts to count var end = schoolDay.TimeEnd.Add(SchoolDismissalSettingsBackend.Instance.GetDefault().LateWindow); //the time that duration counts until var myIn = UTCConversionsBackend.UtcToKioskTime(attendance.In).TimeOfDay; //check-in time var myOut = UTCConversionsBackend.UtcToKioskTime(attendance.Out).TimeOfDay; //check-out time //trim the start time to actual arrive time only if the student is late if (myIn.CompareTo(start) > 0) { start = myIn; } //trim the end time to actual out time only if the student leave early if (myOut.CompareTo(end) < 0) { end = myOut; } var duration = end.Subtract(start); //If time-in is later than time out, just return 0 if (duration < TimeSpan.Zero) { return(TimeSpan.Zero); } return(duration); }
/// <summary> /// Generate Weekly report /// </summary> /// <param name="report"></param> /// <returns></returns> public WeeklyReportViewModel GenerateWeeklyReport(WeeklyReportViewModel report) { //set student report.Student = DataSourceBackend.Instance.StudentBackend.Read(report.StudentId); //to generate week selection drop down, make a list item for every week between first and last day of school var dayFirst = DataSourceBackend.Instance.SchoolDismissalSettingsBackend.GetDefault().DayFirst.Date; var dayLast = DataSourceBackend.Instance.SchoolDismissalSettingsBackend.GetDefault().DayLast.Date; var dayNow = UTCConversionsBackend.UtcToKioskTime(DateTimeHelper.Instance.GetDateTimeNowUTC()).Date; //The first valid week(Monday's date) for the dropdown var FirstWeek = dayFirst.AddDays(-((dayFirst.DayOfWeek - DayOfWeek.Monday + 7) % 7)); //added this mod operation to make sure it's the previous monday not the next monday //The last valid month for the dropdown var LastWeek = dayLast.AddDays(-((dayLast.DayOfWeek - DayOfWeek.Monday + 7) % 7)); //The month of today var WeekNow = dayNow.AddDays(-((dayNow.DayOfWeek - DayOfWeek.Monday + 7) % 7)); //if today is sunday, dayNow.DayOfWeek - DayOfWeek.Monday = -1 //do not go beyond the week of today if (LastWeek > WeekNow) { LastWeek = WeekNow; } //Set the current week (loop variable) to the last valid week var currentWeek = LastWeek; //initialize the dropdownlist report.Weeks = new List <SelectListItem>(); // the week id int weekId = 1; //loop backwards in time so that the week select list items are in time reversed order while (currentWeek >= FirstWeek) { //the friday's date of the current week var currentWeekFriday = currentWeek.AddDays(4); //make a list item for the current week var week = new SelectListItem { Value = "" + weekId, Text = "" + currentWeek.ToShortDateString() + " to " + currentWeekFriday.ToShortDateString() }; //add to the select list report.Weeks.Add(week); //if current week is the selected month, set the start date and end date for this report if (weekId == report.SelectedWeekId) { //set start date and end date report.DateStart = currentWeek; report.DateEnd = currentWeekFriday; } weekId++; currentWeek = currentWeek.AddDays(-7); } //Generate report for this month GenerateReportFromStartToEnd(report); return(report); }
/// <summary> /// Generate the report from the start date to the end date /// </summary> /// <param name="report"></param> /// <returns></returns> private void GenerateReportFromStartToEnd(BaseReportViewModel report) { var myDateNow = UTCConversionsBackend.UtcToKioskTime(DateTimeHelper.Instance.GetDateTimeNowUTC()).Date; //today's date in kiosk time zone // Don't go beyond today if (report.DateEnd.CompareTo(myDateNow) > 0) { report.DateEnd = myDateNow; } var currentDate = report.DateStart; //loop variable TimeSpan accumlatedTotalHoursExpected = TimeSpan.Zero; //current accumulated total hours expected TimeSpan accumlatedTotalHours = TimeSpan.Zero; //current accululated total hours attended int emotionLevel = 0; //current emotion level while (currentDate.CompareTo(report.DateEnd) <= 0) //loop until last date, include last date { //create a new AttendanceReportViewmodel for each day var temp = new AttendanceReportViewModel { Date = currentDate }; // Hold the emotion for the null condition temp.EmotionUri = "/content/img/placeholder.png"; //get today's school calendar model var myToday = DataSourceBackend.Instance.SchoolCalendarBackend.ReadDate(currentDate); // if the day is not a school day, set IsSchoolDay to false if (myToday == null || myToday.SchoolDay == false) { temp.IsSchoolDay = false; } // if the day is a school day, perform calculations else { temp.HoursExpected = myToday.TimeDuration; // Find out if the student attended that day, and add that in. Because the student can check in/out multiple times add them together. var myRange = report.Student.Attendance.Where(m => UTCConversionsBackend.UtcToKioskTime(m.In).Date == currentDate.Date).OrderBy(m => m.In).ToList(); //if no attendance record on this day, set attendance status to absent if (!myRange.Any()) { temp.AttendanceStatus = AttendanceStatusEnum.AbsentUnexcused; report.Stats.DaysAbsentUnexcused++; } else { temp.AttendanceStatus = AttendanceStatusEnum.Present; //set TimeIn to be the first check-in time in the list, so that if there are multiple check-ins, //the TimeIn is set to the first check-in time. Same for emotion. temp.TimeIn = UTCConversionsBackend.UtcToKioskTime(myRange.First().In); temp.Emotion = myRange.First().Emotion; temp.EmotionUri = Emotion.GetEmotionURI(temp.Emotion); //determine whether is on time or late if (temp.TimeIn.TimeOfDay > myToday.TimeStart) { temp.CheckInStatus = CheckInStatusEnum.ArriveLate; } else { temp.CheckInStatus = CheckInStatusEnum.ArriveOnTime; } // a list containing all intervals in this day List <Interval> intervals = new List <Interval>(); //loop through all attendance records in my range foreach (var item in myRange) { TimeSpan timeIn = UTCConversionsBackend.UtcToKioskTime(item.In).TimeOfDay; TimeSpan timeOut = UTCConversionsBackend.UtcToKioskTime(item.Out).TimeOfDay; Interval inter = new Interval(timeIn, timeOut); intervals.Add(inter); //update the checkout time for this attendance report view model temp.TimeOut = UTCConversionsBackend.UtcToKioskTime(item.Out); //determine whether left early or not if (temp.TimeOut.TimeOfDay < myToday.TimeEnd) { temp.CheckOutStatus = CheckOutStatusEnum.DoneEarly; } else { temp.CheckOutStatus = CheckOutStatusEnum.DoneAuto; } } report.Stats.DaysPresent++; //increase number of days present var earlyWindow = SchoolDismissalSettingsBackend.Instance.GetDefault().EarlyWindow; var lateWindow = SchoolDismissalSettingsBackend.Instance.GetDefault().LateWindow; //the time from which duration starts to count var start = myToday.TimeStart.Add(-earlyWindow); //the time that duration counts until var end = myToday.TimeEnd.Add(lateWindow); //Calculate hours attended on this day temp.HoursAttended = CalculateHoursAttended(intervals, start, end); temp.PercentAttended = (int)(temp.HoursAttended.TotalMinutes * 100 / temp.HoursExpected.TotalMinutes); //calculate percentage of attended time if (temp.CheckInStatus == CheckInStatusEnum.ArriveLate) { report.Stats.DaysLate++; } report.Stats.DaysOnTime = report.Stats.DaysPresent - report.Stats.DaysLate; if (temp.CheckOutStatus == CheckOutStatusEnum.DoneEarly) { report.Stats.DaysOutEarly++; } report.Stats.DaysOutAuto = report.Stats.DaysPresent - report.Stats.DaysOutEarly; } switch (temp.Emotion) { case EmotionStatusEnum.VeryHappy: temp.EmotionLevel = emotionLevel + 2; report.Stats.DaysVeryHappy++; break; case EmotionStatusEnum.Happy: temp.EmotionLevel = emotionLevel + 1; report.Stats.DaysHappy++; break; case EmotionStatusEnum.Neutral: temp.EmotionLevel = emotionLevel; report.Stats.DaysNeutral++; break; case EmotionStatusEnum.Sad: temp.EmotionLevel = emotionLevel - 1; report.Stats.DaysSad++; break; case EmotionStatusEnum.VerySad: temp.EmotionLevel = emotionLevel - 2; report.Stats.DaysVerySad++; break; default: temp.EmotionLevel = emotionLevel; break; } emotionLevel = temp.EmotionLevel; //calculations for both absent and present records //calculations for both absent and present records report.Stats.NumOfSchoolDays++; accumlatedTotalHoursExpected += temp.HoursExpected; accumlatedTotalHours += temp.HoursAttended; // Need to add the totals back to the temp, because the temp is new each iteration temp.TotalHoursExpected += accumlatedTotalHoursExpected; temp.TotalHours = accumlatedTotalHours; } //add this attendance report to the attendance list report.AttendanceList.Add(temp); currentDate = currentDate.AddDays(1); } report.Stats.AccumlatedTotalHoursExpected = accumlatedTotalHoursExpected; report.Stats.AccumlatedTotalHours = accumlatedTotalHours; //if there is at least one school days in this report, calculate the following stats if (report.Stats.NumOfSchoolDays > 0) { report.Stats.PercPresent = (int)Math.Round((double)report.Stats.DaysPresent * 100 / report.Stats.NumOfSchoolDays); report.Stats.PercAttendedHours = (int)Math.Round(report.Stats.AccumlatedTotalHours.TotalHours * 100 / report.Stats.AccumlatedTotalHoursExpected.TotalHours); report.Stats.PercExcused = (int)Math.Round((double)report.Stats.DaysAbsentExcused * 100 / report.Stats.NumOfSchoolDays); report.Stats.PercUnexcused = (int)Math.Round((double)report.Stats.DaysAbsentUnexcused * 100 / report.Stats.NumOfSchoolDays); if (report.Stats.DaysPresent > 0) { report.Stats.PercInLate = (int)Math.Round((double)report.Stats.DaysLate * 100 / report.Stats.DaysPresent); report.Stats.PercOutEarly = (int)Math.Round((double)report.Stats.DaysOutEarly * 100 / report.Stats.DaysPresent); } } //set the attendance goal percent according to school dismissal settings report.Goal = SchoolDismissalSettingsBackend.Instance.GetDefault().Goal; //set the date array, ideal value array and actual value array for line chart report.YearArray = @String.Join(", ", report.AttendanceList.Where(m => m.IsSchoolDay).ToList().Select(m => m.Date.Year.ToString()).ToArray()); report.MonthArray = @String.Join(", ", report.AttendanceList.Where(m => m.IsSchoolDay).ToList().Select(m => m.Date.Month.ToString()).ToArray()); report.DayArray = @String.Join(", ", report.AttendanceList.Where(m => m.IsSchoolDay).ToList().Select(m => m.Date.Day.ToString()).ToArray()); report.ActualValues = @String.Join(", ", report.AttendanceList.Where(m => m.IsSchoolDay).ToList() .Select(m => m.TotalHours.TotalHours.ToString("0.#")).ToArray()); report.PerfectValues = @String.Join(", ", report.AttendanceList.Where(m => m.IsSchoolDay).ToList() .Select(m => m.TotalHoursExpected.TotalHours.ToString("0.#")).ToArray()); report.GoalValues = @String.Join(", ", report.AttendanceList.Where(m => m.IsSchoolDay).ToList() .Select(m => (m.TotalHoursExpected.TotalHours * (report.Goal) / 100).ToString("0.#")).ToArray()); report.EmotionLevelValues = @String.Join(", ", report.AttendanceList.Where(m => m.IsSchoolDay).Select(m => m.EmotionLevel).ToArray()); }
/// <summary> /// Generate monthly report /// </summary> /// <param name="report"></param> /// <returns></returns> public MonthlyReportViewModel GenerateMonthlyReport(MonthlyReportViewModel report) { //set student report.Student = DataSourceBackend.Instance.StudentBackend.Read(report.StudentId); //to generate month selection drop down, make a list item for every month between first and last day of school var dayFirst = DataSourceBackend.Instance.SchoolDismissalSettingsBackend.GetDefault().DayFirst.Date; var dayLast = DataSourceBackend.Instance.SchoolDismissalSettingsBackend.GetDefault().DayLast.Date; var dayNow = UTCConversionsBackend.UtcToKioskTime(DateTimeHelper.Instance.GetDateTimeNowUTC()).Date; //The first valid month for the dropdown var monthFirst = new DateTime(dayFirst.Year, dayFirst.Month, 1); //The last valid month for the dropdown var monthLast = new DateTime(dayLast.Year, dayLast.Month, 1); //The month of today var monthNow = new DateTime(dayNow.Year, dayNow.Month, 1); //do not go beyond the month of today if (monthLast > monthNow) { monthLast = monthNow; } //Set the current month (loop variable) to the last valid month var currentMonth = monthLast; //initialize the dropdownlist report.Months = new List <SelectListItem>(); // the month id int monthId = 1; //loop backwards in time so that the month select list items are in time reversed order while (currentMonth >= monthFirst) { //make a list item for the current month var month = new SelectListItem { Value = "" + monthId, Text = currentMonth.ToString("MMMM yyyy") }; //add to the select list report.Months.Add(month); //if current month is the selected month, set the start date and end date for this report if (monthId == report.SelectedMonthId) { //set start date and end date report.DateStart = currentMonth; report.DateEnd = new DateTime(currentMonth.Year, currentMonth.Month, DateTime.DaysInMonth(currentMonth.Year, currentMonth.Month)); } monthId++; currentMonth = currentMonth.AddMonths(-1); } //Generate report for this month GenerateReportFromStartToEnd(report); return(report); }