/// <summary> /// Compares an expected daily student summary object to the actual daily student summary object /// </summary> /// <remarks> /// For reasons of time constraints, this method assumes that neither object is null and that none of /// the properties are null. /// </remarks> /// <param name="expectedSummary">The expected daily student summary object</param> /// <param name="actualSummary">The actual daily student summary object</param> public static void CompareDailyStudentSummaries(DailyStudentSummary expectedSummary, DailyStudentSummary actualSummary) { Assert.That(actualSummary.Subjects, Is.EquivalentTo(expectedSummary.Subjects)); Assert.That(actualSummary.SummaryRows.Count, Is.EqualTo(expectedSummary.SummaryRows.Count)); //We need to have the rows in a consistent order so that we can compare the same rows to each other var expectedSortedRows = expectedSummary.SummaryRows.OrderBy(row => row.UserId); var actualSortedRows = actualSummary.SummaryRows.OrderBy(row => row.UserId); expectedSortedRows.Zip(actualSortedRows, Tuple.Create) .ToList() .ForEach(rowTuple => CompareDailyStudentSummaries(rowTuple.Item1, rowTuple.Item2)); }
public DailyStudentSummary GetDailyStudentSummary(DateTime summaryDateTime) { DailyStudentSummary dailySummary = new DailyStudentSummary(); //Retrieve the queryable from the answer DB IQueryable <Answer> answerQueryable = answerDB.GetAnswerQueryable(); //Calculate the summary date/time range DateTime minDateTime = summaryDateTime.Date; DateTime maxDateTime = summaryDateTime; //Create a queryable that only contains answers within the date range IQueryable <Answer> relevantAnswers = answerQueryable .Where(answer => answer.SubmitDateTime >= minDateTime && answer.SubmitDateTime <= maxDateTime) .AsQueryable(); //Calculate the unique subjects within the date range dailySummary.Subjects = relevantAnswers .Where(answer => answer.SubmitDateTime >= minDateTime) .Where(answer => answer.SubmitDateTime <= maxDateTime) .Select(answer => answer.Subject) .Distinct() .ToList(); //Retrieve the students and their average subject progress scores dailySummary.SummaryRows = relevantAnswers //Group the answers into users .GroupBy(answer => answer.UserId) //Create a student summary row for each user group .Select(userAnswerGroup => new StudentSummaryRow() { UserId = userAnswerGroup.Key, Name = userAnswerGroup.Key.ToString(), AverageSubjectProgress = userAnswerGroup //Group the user answers by subject .GroupBy(answer => answer.Subject) //Convert each subject group into a collection of tuples containing the subject name and the //average progress score .Select(subjectGroup => Tuple.Create(subjectGroup.Key, subjectGroup.Select(answer => (decimal)answer.Progress).Average())) //Convert that collection to a dictionary with the subject as the key and the average progress score //as the value .ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2) }) .ToList(); return(dailySummary); }
/// <summary> /// Runs a student summary test with a particular test of test data /// </summary> /// <param name="testData">The set of test data to use when running the test</param> private void RunStudentSummaryTestWithData(List <Answer> testData) { //Define the target date/time when the summary is created DateTime targetDateTime = DateTime.Parse("2018-01-30T12:39:00"); //Calculate the expected daily summary DailyStudentSummary expectedSummary = CalculateExpectedSummaryData(testData, targetDateTime); //Create the mock answer DB Mock <IAnswerDB> mockAnswerDB = CreateMockAnswerDB(testData); //Create an instance of the answer repository IAnswerRepository answerRepository = new AnswerRepository(mockAnswerDB.Object); //Retrieve the daily student summary DailyStudentSummary actualSummary = answerRepository.GetDailyStudentSummary(targetDateTime); //Compare the actual summary to the expected summary to see if they match DataComparers.CompareDailyStudentSummaries(actualSummary, expectedSummary); }
/// <summary> /// Calculates the expected summary data based on a set of test data and a particular date/time /// when the summary is generated /// </summary> /// <remarks> /// I'm deliberately calculating the expected data in a different manner than how the method /// will be implemented so that I'm not repeating the same mistakes. I consider this to /// be the less optimal method. Using grouping would be much more readable. /// </remarks> /// <param name="testData">The test data</param> /// <param name="summaryGenDateTime">The date/time the daily summary was generated</param> /// <returns>The expected daily summary</returns> private DailyStudentSummary CalculateExpectedSummaryData(List <Answer> testData, DateTime summaryGenDateTime) { DailyStudentSummary expectedSummary = new DailyStudentSummary(); //Calculate the summary date/time range DateTime minDateTime = summaryGenDateTime.Date; DateTime maxDateTime = summaryGenDateTime; //Create a queryable that only contains answers within the date range IQueryable <Answer> relevantAnswers = testData .Where(answer => answer.SubmitDateTime >= minDateTime && answer.SubmitDateTime <= maxDateTime) .AsQueryable(); //Calculate the unique subjects within the date range expectedSummary.Subjects = relevantAnswers .Where(answer => answer.SubmitDateTime >= minDateTime && answer.SubmitDateTime <= maxDateTime) .Select(answer => answer.Subject) .Distinct() .ToList(); //Get the unique students that have data within the date range List <int> expectedStudents = relevantAnswers .Where(answer => answer.SubmitDateTime >= minDateTime && answer.SubmitDateTime <= maxDateTime) .Select(answer => answer.UserId) .Distinct() .ToList(); //Map each student to a summary row expectedSummary.SummaryRows = expectedStudents .Select(currentUserId => { StudentSummaryRow studentSummary = new StudentSummaryRow() { UserId = currentUserId, Name = currentUserId.ToString() }; //Map each subject to an average progress value. Some values may be null, //since not all students may have done exercises in the same subjects studentSummary.AverageSubjectProgress = expectedSummary.Subjects .Where(subject => relevantAnswers.Any(answer => answer.Subject == subject && answer.UserId == currentUserId)) .Select(targetSubject => { decimal averageProgress = relevantAnswers .Where(answer => answer.Subject == targetSubject) .Where(answer => answer.UserId == currentUserId) .Select(answer => (decimal)answer.Progress) .Average(); return(Tuple.Create(targetSubject, averageProgress)); }) .ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2); return(studentSummary); }) .ToList(); return(expectedSummary); }