diff --git a/StepChallenge.Tests/StepsServiceStub.cs b/StepChallenge.Tests/StepsServiceStub.cs new file mode 100644 index 0000000..92e3b73 --- /dev/null +++ b/StepChallenge.Tests/StepsServiceStub.cs @@ -0,0 +1,19 @@ +using System.Threading.Tasks; +using StepChallenge.Services; + +namespace StepChallenge.Tests +{ + public class StepsServiceStub : StepsService + { + private readonly int _averageTeamSize; + public StepsServiceStub(StepContext stepContext, int averageTeamSize = 3) : base(stepContext) + { + _averageTeamSize = averageTeamSize; + } + + public override async Task GetAverageTeamSize() + { + return _averageTeamSize; + } + } +} \ No newline at end of file diff --git a/StepChallenge.Tests/TeamOverviewTests.Data.cs b/StepChallenge.Tests/TeamOverviewTests.Data.cs new file mode 100644 index 0000000..3468292 --- /dev/null +++ b/StepChallenge.Tests/TeamOverviewTests.Data.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using Model; +using NUnit.Framework; + +namespace StepChallenge.Tests +{ + public class TestOverviewData + { + private static DateTime StartDate = new DateTime(2019,09,16, 0,0,0); + + [SetUp] + public void Setup() + { + } + + public static IQueryable GetTeams(){ + var teams = CreateThreeTeams(); + return teams; + } + + public static IQueryable GetTeamWithThreePeople() + { + var teams = CreateTeamWithOneLessPerson(); + return teams; + } + + private static IQueryable CreateTeamWithOneLessPerson() + { + return (new List + { + new Team + { + TeamId = 1, + TeamName = "Team_1", + NumberOfParticipants = 3, + Participants = CreateParticipants(10) + } + }).AsQueryable(); + } + + private static IQueryable CreateThreeTeams() + { + return (new List + { + new Team + { + TeamId = 1, + TeamName = "Team_1", + NumberOfParticipants = 3, + Participants = CreateParticipants(10) + }, + new Team + { + TeamId = 2, + TeamName = "Team_2", + NumberOfParticipants = 3, + Participants = CreateParticipants(20) + }, + new Team + { + TeamId = 3, + TeamName = "Team_3", + NumberOfParticipants = 3, + Participants = CreateParticipants(30) + } + }).AsQueryable(); + } + + private static ICollection CreateParticipants(int id) + { + var participants = new List + { + new Participant + { + ParticipantName = "ParticipantNameOne", + ParticipantId = id + 1, + Steps = CreateSteps(20) + }, + new Participant + { + ParticipantName = "ParticipantNameTwo", + ParticipantId = id + 2, + Steps = CreateSteps(20) + }, + new Participant + { + ParticipantName = "ParticipantNameThree", + ParticipantId = id + 3, + Steps = CreateSteps(20) + }, + }; + return participants; + } + + private static ICollection CreateSteps(int stepCount) + { + var steps = new List(); + + for (int i = 0; i < 3; i++) + { + steps.Add( + new Steps + { + StepCount = stepCount, + DateOfSteps = StartDate.AddDays(i), + } + ); + } + + return steps; + } + } +} \ No newline at end of file diff --git a/StepChallenge.Tests/TeamOverviewTests.cs b/StepChallenge.Tests/TeamOverviewTests.cs new file mode 100644 index 0000000..21e2141 --- /dev/null +++ b/StepChallenge.Tests/TeamOverviewTests.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Model; +using Moq; +using NUnit.Framework; +using StepChallenge.Services; + +namespace StepChallenge.Tests +{ + public class TeamOverviewTests : BaseTests + { + /// + /// Test a teams step total is counted correctly + /// + [Test] + public async Task Test_TeamOverview_TotalSteps_AreCountedCorrectly() + { + var team = TestOverviewData.GetTeams().ToList(); + + var stepsService = new StepsServiceStub(GetMockStepContext(team)); + var result = await stepsService.GetTeamsOverview(); + + var resultTotal = result.Teams.ToList().First(r => r.TeamId == 1).TeamTotalSteps; + var expectedTotal = 180; + + Assert.IsTrue(resultTotal == expectedTotal, $"Expected total steps to be: {expectedTotal} but got {resultTotal}"); + } + + /// + /// Test a teams step total is counted correctly and ignore steps counted outside of the challenge dates + /// + [Test] + [TestCase("01/01/2012")] + [TestCase("01/01/2020")] + public async Task Test_TeamOverview_TotalSteps_DoNotCountStepsOutsideOfDates(DateTime date) + { + var team = TestOverviewData.GetTeams().ToList(); + team.First(r => r.TeamId == 1).Participants.First(p => p.ParticipantId == 11).Steps.Add(new Steps + { + StepCount = 30, + DateOfSteps = date, + }); + + var stepsService = new StepsServiceStub(GetMockStepContext(team)); + var result = await stepsService.GetTeamsOverview(); + + var resultTotal = result.Teams.ToList().First(r => r.TeamId == 1).TeamTotalSteps; + var expectedTotal = 180; + + Assert.IsTrue(resultTotal == expectedTotal, $"Expected total steps to be: {expectedTotal} but got {resultTotal}"); + } + + /// + /// Test a teams step total adds an average person correctly for teams with less people + /// + [Test] + public async Task Test_TeamOverview_TotalSteps_TeamsWithLessPeopleGetAverageSteps() + { + var team = TestOverviewData.GetTeamWithThreePeople().ToList(); + + var averageTeamSize = 4; + var stepsService = new StepsServiceStub(GetMockStepContext(team), averageTeamSize); + var result = await stepsService.GetTeamsOverview(); + + var resultTotal = result.Teams.ToList().First(r => r.TeamId == 1).TeamTotalStepsWithAverage; + var expectedTotal = 240; + + Assert.IsTrue(resultTotal == expectedTotal, $"Expected total steps to be: {expectedTotal} but got {resultTotal}"); + } + + private StepContext GetMockStepContext(List teams) + { + var mockContext = new Mock(); + + mockContext.Setup(x => x.Team).Returns(GetDbSetTeams(teams).Object); + + return mockContext.Object; + } + + + private Mock> GetDbSetTeams(List teamList) + { + var teams = teamList.AsQueryable(); + + var mockParticipantSet = new Mock>(); + mockParticipantSet.As>().Setup(m => m.Expression).Returns(teams.Expression); + mockParticipantSet.As>().Setup(m => m.Provider).Returns(teams.Provider); + mockParticipantSet.As>().Setup(m => m.Expression).Returns(teams.Expression); + mockParticipantSet.As>().Setup(m => m.ElementType).Returns(teams.ElementType); + mockParticipantSet.As>().Setup(m => m.GetEnumerator()).Returns(teams.GetEnumerator()); + + return mockParticipantSet; + } + } +} \ No newline at end of file diff --git a/StepChallenge/ClientApp/src/Admin.js b/StepChallenge/ClientApp/src/Admin.js index f5708fb..a924911 100644 --- a/StepChallenge/ClientApp/src/Admin.js +++ b/StepChallenge/ClientApp/src/Admin.js @@ -6,6 +6,7 @@ import { EditParticipant } from './components/admin/editParticipant'; import { EditTeams } from './components/admin/editTeams'; import { EditChallengeSettings } from './components/admin/editChallengeSettings'; import { AdminScoreBoard } from './components/admin/adminScoreBoard'; +import { TotalOverview } from './components/admin/totalOverview'; export class Admin extends Component { static displayName = Admin.name; @@ -41,12 +42,15 @@ export class Admin extends Component {

Loading...

} {!this.state.loading && - - - - - - +
+ + + + + + + +
} ); diff --git a/StepChallenge/ClientApp/src/App.css b/StepChallenge/ClientApp/src/App.css index e4ac676..2aa59c5 100644 --- a/StepChallenge/ClientApp/src/App.css +++ b/StepChallenge/ClientApp/src/App.css @@ -18,3 +18,53 @@ border: "1px solid red"; text-decoration: underline; } + +.table-container{ + width: 100%; + position: relative; + height: 800px; + overflow: auto; +} + +.overview-table{ + overflow-x: scroll; + overflow-y: overflow; + margin-left: 24em; +} + +.overview-table table { + border-collapse: separate; + border-spacing: 0; +} + +.overview-table .headcol { + position: absolute; + width: 12em; + left: 0; + top: auto; + border-top-width: 1px; + /*only relevant for first row*/ + margin-top: -1px; + /*compensate for top border*/ +} + +.overview-table .headcol2 { + margin-left: 12em; +} + +.overview-table .headcol:before { + margin-left: 12em; +} + +.overview-table .winner { + color: rgb(102, 219, 121) !important; + font-weight: 700; +} + +.overview-table .border-right{ + border-right: 1px solid black; +} + +.overview-table .border-left{ + border-left: 1px solid black; +} \ No newline at end of file diff --git a/StepChallenge/ClientApp/src/components/CreateStepTable.js b/StepChallenge/ClientApp/src/components/CreateStepTable.js index 31c21c7..aa9e3c4 100644 --- a/StepChallenge/ClientApp/src/components/CreateStepTable.js +++ b/StepChallenge/ClientApp/src/components/CreateStepTable.js @@ -6,11 +6,16 @@ import TeamStep from './TeamStep'; function getDaysToDisplay(startDate){ - var displayFromNextDay = moment().add(2, 'days'); - var diff = displayFromNextDay.diff(startDate, "days"); - var roundUpToTheNextWeek = Math.ceil(diff/7)*7 - var howManyDaysToShow = moment(startDate).add(roundUpToTheNextWeek, 'days'); - return howManyDaysToShow.diff(startDate, 'days'); + if(moment().isAfter(moment("2019-12-01T01:00:00+01:00"))){ + var endDate = moment("2019-12-07T01:00:00+01:00") + var diff = endDate.diff(startDate, 'days'); + return diff + } + var displayFromNextDay = moment().add(2, 'days'); + var diff = displayFromNextDay.diff(startDate, "days"); + var roundUpToTheNextWeek = Math.ceil(diff/7)*7 + var howManyDaysToShow = moment(startDate).add(roundUpToTheNextWeek, 'days'); + return howManyDaysToShow.diff(startDate, 'days'); } function getTable(steps, numberOfParticipants, type){ diff --git a/StepChallenge/ClientApp/src/components/admin/totalOverview.js b/StepChallenge/ClientApp/src/components/admin/totalOverview.js new file mode 100644 index 0000000..d1330fe --- /dev/null +++ b/StepChallenge/ClientApp/src/components/admin/totalOverview.js @@ -0,0 +1,118 @@ +import React, { Component } from 'react'; +import * as moment from 'moment'; +import ApiHelper from '../ApiHelper'; + +export class TotalOverview extends Component { + static displayName = TotalOverview.name; + + constructor (props) { + super(props); + this.state = { + loading: true, + error : false, + highestStepsParticipant: 0, + highestStepsParticipantId: 0, + highestStepsTeam: 0, + highestStepsTeamId: 0, + teams: [], + } + var query = `{ "query": "{ adminParticipantsOverview { highestStepsParticipant, highestStepsParticipantId, highestStepsTeam, highestStepsTeamId, + teams { teamId, teamName, numberOfParticipants, teamTotalSteps, teamTotalStepsWithAverage, participantsStepsOverviews { participantId, participantName, stepTotal, stepsOverviews {stepCount, dateOfSteps } } } } }" }` + this.apiHelper = new ApiHelper(); + this.apiHelper.GraphQlApiHelper(query) + .then(data => { + if(data.hasOwnProperty("adminParticipantsOverview")){ + this.setState({ + loading: false, + teams : data.adminParticipantsOverview.teams, + highestStepsParticipant : data.adminParticipantsOverview.highestStepsParticipant, + highestStepsParticipantId : data.adminParticipantsOverview.highestStepsParticipantId, + highestStepsTeam : data.adminParticipantsOverview.highestStepsTeam, + highestStepsTeamId : data.adminParticipantsOverview.highestStepsTeamId, + }); + }else{ + this.setState({ + loading: false, + error: true + }); + } + }) + } + + static getStepTds (steps){ + var firstParticipantRow = [] + steps.forEach(step => { + firstParticipantRow.push({step.stepCount}) + }); + return firstParticipantRow + } + + static renderTable (teams, highest) { + //TODO get from api + var startDate = moment("2019-09-16T02:00:00+00:00"); + var endDate = moment("2019-12-01T01:00:00+01:00") + var totalDays = endDate.diff(startDate, 'days') + 2; //the two is for the two extra columns at the start. Needs sorting out! + var formattedDayHeader = []; + for (let index = 0; index < totalDays; index++) { + var day = moment(startDate).add(index, 'days').format('D-MM'); + formattedDayHeader.push({ day }); + } + /** Header */ + const header = ( + Team Name + Participants Name + {formattedDayHeader} + Participant Total + Actual Team Total + Team Total Including Averaged Person + ) + /** Header */ + const tableRows = []; + teams.forEach(team => { + for (let i= 0; i< team.numberOfParticipants; i++) { + var row = [] + var participant = team.participantsStepsOverviews[i] ? team.participantsStepsOverviews[i] : { participantName : "Not known", stepTotal : 0, stepsOverviews : new Array(totalDays).fill({ stepCount : 0 }) } + participant.stepsOverviews = participant.stepsOverviews.length > 0 ? participant.stepsOverviews : new Array(totalDays).fill({ stepCount : 0 }) + if(i === 0){ + var firstParticipantRow = TotalOverview.getStepTds(participant.stepsOverviews) + row.push( + + {team.teamName} + { participant.participantName } + {firstParticipantRow} + {participant.stepTotal} + {team.teamTotalSteps} + {team.teamTotalStepsWithAverage == 0 ? team.teamTotalSteps : team.teamTotalStepsWithAverage} + + ) + } + else{ + var participantRow = TotalOverview.getStepTds(participant.stepsOverviews) + row.push( + + { participant.participantName } + {participantRow} + {participant.stepTotal} + ) + } + tableRows.push(row) + } + }) + return( +
+ {header}{tableRows}
+
+ ) + } + + render () { + let contents = this.state.loading ? +

Loading...

: + TotalOverview.renderTable(this.state.teams, {highestStepsTeamId : this.state.highestStepsTeamId, highestStepsParticipantId : this.state.highestStepsParticipantId}) + + return ( +
+ {contents} +
+ ) +}} diff --git a/StepChallenge/DataModels/AdminParticipantsOverview.cs b/StepChallenge/DataModels/AdminParticipantsOverview.cs new file mode 100644 index 0000000..c0ba03b --- /dev/null +++ b/StepChallenge/DataModels/AdminParticipantsOverview.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using Model; + +namespace StepChallenge.DataModels +{ + public class AdminParticipantsOverview + { + public List Teams { get; set; } + public int HighestStepsParticipant { get; set; } + public int HighestStepsParticipantId { get; set; } + public int HighestStepsTeam { get; set; } + public int HighestStepsTeamId { get; set; } + } +} \ No newline at end of file diff --git a/StepChallenge/DataModels/ParticipantsStepsOverview.cs b/StepChallenge/DataModels/ParticipantsStepsOverview.cs new file mode 100644 index 0000000..cb2696b --- /dev/null +++ b/StepChallenge/DataModels/ParticipantsStepsOverview.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace StepChallenge.DataModels +{ + public class ParticipantsStepsOverview + { + public int ParticipantId { get; set; } + public string ParticipantName { get; set; } + public List StepsOverviews { get; set; } + + public int StepTotal { get; set; } + + } + + public class StepsOverview + { + public int StepCount { get; set; } + public DateTimeOffset DateOfSteps { get; set; } + } +} \ No newline at end of file diff --git a/StepChallenge/DataModels/TeamScoresOverview.cs b/StepChallenge/DataModels/TeamScoresOverview.cs new file mode 100644 index 0000000..b82df3a --- /dev/null +++ b/StepChallenge/DataModels/TeamScoresOverview.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace StepChallenge.DataModels +{ + public class TeamScoresOverview + { + public List ParticipantsStepsOverviews { get; set; } + public int TeamId { get; set; } + public string TeamName { get; set; } + public int NumberOfParticipants { get; set; } + + public int TeamTotalSteps { get; set; } + public int TeamTotalStepsWithAverage { get; set; } + + } +} \ No newline at end of file diff --git a/StepChallenge/Model/GraphQL/AdminParticipantsOverviewType.cs b/StepChallenge/Model/GraphQL/AdminParticipantsOverviewType.cs new file mode 100644 index 0000000..1a59073 --- /dev/null +++ b/StepChallenge/Model/GraphQL/AdminParticipantsOverviewType.cs @@ -0,0 +1,21 @@ +using GraphQL.Authorization; +using GraphQL.Types; +using StepChallenge.DataModels; + +namespace Model.GraphQL +{ + public class AdminParticipantsOverviewType : ObjectGraphType + { + public AdminParticipantsOverviewType() + { + Name = "AdminParticipantsOverview"; + this.AuthorizeWith("AdminPolicy"); + + Field(x => x.Teams , type: typeof(ListGraphType)).Description("All the Teams"); + Field(x => x.HighestStepsTeam, type: typeof(IntGraphType)).Description("Team with the highest step count"); + Field(x => x.HighestStepsTeamId, type: typeof(IdGraphType)).Description("Id of the Team with the highest step count"); + Field(x => x.HighestStepsParticipant, type: typeof(IntGraphType)).Description("Participant with the highest step count"); + Field(x => x.HighestStepsParticipantId, type: typeof(IntGraphType)).Description("Id of the Participant with the highest step count"); + } + } +} \ No newline at end of file diff --git a/StepChallenge/Model/GraphQL/ParticipantOverviewType.cs b/StepChallenge/Model/GraphQL/ParticipantOverviewType.cs new file mode 100644 index 0000000..06ee57d --- /dev/null +++ b/StepChallenge/Model/GraphQL/ParticipantOverviewType.cs @@ -0,0 +1,28 @@ +using GraphQL.Types; +using StepChallenge.DataModels; + +namespace Model.GraphQL +{ + public class ParticipantOverviewType : ObjectGraphType + { + public ParticipantOverviewType() + { + Name = "ParticipantOverviewType"; + Field(x => x.ParticipantId, type: typeof(IdGraphType)).Description("The ID of the participant."); + Field(x => x.ParticipantName).Description("The name of the participant"); + Field(x => x.StepTotal, type: typeof(IntGraphType)).Description("The step count total"); + Field(x => x.StepsOverviews, type: typeof(ListGraphType)).Description("The participants steps"); + //Field(x => x.Users, type: typeof(ListGraphType)).Description("Users in the Team"); + } + } + + public class StepsOverviewType : ObjectGraphType + { + public StepsOverviewType() + { + Name = "StepsOverview"; + Field(x => x.StepCount, type: typeof(IntGraphType)).Description("The step count for this day"); + Field(x => x.DateOfSteps, type: typeof(DateTimeOffsetGraphType)).Description("The date the steps were taken"); + } + } +} \ No newline at end of file diff --git a/StepChallenge/Model/GraphQL/TeamOverviewType.cs b/StepChallenge/Model/GraphQL/TeamOverviewType.cs new file mode 100644 index 0000000..57242b3 --- /dev/null +++ b/StepChallenge/Model/GraphQL/TeamOverviewType.cs @@ -0,0 +1,23 @@ +using GraphQL.Authorization; +using GraphQL.Types; +using StepChallenge.DataModels; + +namespace Model.GraphQL +{ + public class TeamOverviewType : ObjectGraphType + { + + public TeamOverviewType() + { + Name = "TeamOverview"; + + Field(x => x.TeamId, type: typeof(IdGraphType)).Description("The ID of the Team."); + Field(x => x.TeamName).Description("The name of the Team"); + Field(x => x.NumberOfParticipants, type: typeof(IntGraphType)).Description("Number of Participants in the team"); + Field(x => x.TeamTotalSteps, type: typeof(IntGraphType)).Description(("Total number of team steps")); + Field(x => x.TeamTotalStepsWithAverage, type: typeof(IntGraphType)).Description(("Total number of team steps")); + Field(x => x.ParticipantsStepsOverviews, type: typeof(ListGraphType)).Description("Users in the Team with their steps").AuthorizeWith("AdminPolicy"); + } + + } +} \ No newline at end of file diff --git a/StepChallenge/Query/StepChallengeQuery.cs b/StepChallenge/Query/StepChallengeQuery.cs index 2e8706c..332b483 100644 --- a/StepChallenge/Query/StepChallengeQuery.cs +++ b/StepChallenge/Query/StepChallengeQuery.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; using GraphQL.Types; using Microsoft.EntityFrameworkCore; using Model; @@ -132,7 +133,13 @@ public StepChallengeQuery(StepContext db, StepsService stepsService, TeamService return settings; }); + FieldAsync("AdminParticipantsOverview", + resolve: async context => + { + var overview = await stepsService.GetTeamsOverview(); + return overview; + }); } } diff --git a/StepChallenge/Services/StepsService.cs b/StepChallenge/Services/StepsService.cs index 07eb0e4..5a88c9d 100644 --- a/StepChallenge/Services/StepsService.cs +++ b/StepChallenge/Services/StepsService.cs @@ -3,8 +3,10 @@ using System.Globalization; using System.Linq; using System.Threading.Tasks; +using GraphQL; using Microsoft.EntityFrameworkCore; using Model; +using Model.GraphQL; using StepChallenge.DataModels; using StepChallenge.Mutation; @@ -14,7 +16,7 @@ public class StepsService { public StepContext _stepContext; public DateTime _startDate = new DateTime(2019,09,16, 0,0,0); - public DateTime _endDate = new DateTime(2019, 12, 05,0,0,0); + public DateTime _endDate = new DateTime(2019, 12, 01,0,0,0); public StepsService(StepContext stepContext) { @@ -83,7 +85,7 @@ public Participant GetParticipantSteps(Participant participant) { var steps = _stepContext.Steps .Where(s => s.ParticipantId == participant.ParticipantId) - .Where(s => s.DateOfSteps >= _startDate && s.DateOfSteps < _endDate) + .Where(s => s.DateOfSteps >= _startDate && s.DateOfSteps <= _endDate) .OrderBy(s => s.DateOfSteps) .ToList(); @@ -107,15 +109,37 @@ public List GetTeamScores(IQueryable teams, DateTime thisMonda }) .ToList(); + var teamWithAveragePerson = GetAverageStepsForTeamWithLessPeople(sortedTeams, averageTeamSize); + + return teamWithAveragePerson.OrderByDescending(t => t.TeamStepCount).ToList(); + } + + private List GetAverageStepsForTeamWithLessPeople(List teams, int averageTeamSize) + { // add a pretend participant with averaged steps to teams with less participants - foreach (var teamScore in sortedTeams.Where(t => t.NumberOfParticipants != averageTeamSize && t.NumberOfParticipants != 0)) + foreach (var teamScore in teams.Where(t => t.NumberOfParticipants != averageTeamSize && t.NumberOfParticipants != 0)) { - teamScore.TeamStepCount = ((teamScore.TeamStepCount / teamScore.NumberOfParticipants) * - (averageTeamSize - teamScore.NumberOfParticipants)) + - teamScore.TeamStepCount; + teamScore.TeamStepCount = GetAveragedStepCountTotal(teamScore.TeamStepCount, + teamScore.NumberOfParticipants, averageTeamSize); } + return teams; + } - return sortedTeams.OrderByDescending(t => t.TeamStepCount).ToList(); + private List GetAverageStepsForTeamWithLessPeople(List teams, int averageTeamSize) + { + // add a pretend participant with averaged steps to teams with less participants + foreach (var teamScore in teams.Where(t => t.NumberOfParticipants != averageTeamSize && t.NumberOfParticipants != 0)) + { + teamScore.TeamTotalStepsWithAverage = GetAveragedStepCountTotal(teamScore.TeamTotalSteps, teamScore.NumberOfParticipants, averageTeamSize); + } + return teams; + } + + private int GetAveragedStepCountTotal(int teamStepCount, int teamNumberOfParticipants, int averageTeamSize) + { + return ((teamStepCount / teamNumberOfParticipants) * + (averageTeamSize - teamNumberOfParticipants)) + + teamStepCount; } public int GetTotalSteps(IQueryable teams, DateTime thisMonday, DateTime startDate, int averageTeamSize) @@ -129,6 +153,103 @@ public int GetTotalSteps(IQueryable teams, DateTime thisMonday, DateTime s return total; } + public async Task GetTeamsOverview() + { + var overview = new AdminParticipantsOverview + { + HighestStepsParticipant = 0, + HighestStepsParticipantId = 0, + HighestStepsTeam = 0, + HighestStepsTeamId = 0, + }; + + var teams = _stepContext.Team + .Select(team => new TeamScoresOverview + { + TeamId = team.TeamId, + TeamName = team.TeamName, + NumberOfParticipants = team.NumberOfParticipants, + TeamTotalSteps = team.Participants.Sum(p => p.Steps + .Where(s => s.DateOfSteps >= _startDate && s.DateOfSteps <= _endDate) + .Sum(s => s.StepCount)), + ParticipantsStepsOverviews = team.Participants.Select(participant => new ParticipantsStepsOverview + { + ParticipantId = participant.ParticipantId, + ParticipantName = participant.ParticipantName, + StepsOverviews = GetAllDaysSteps(participant.Steps), + StepTotal = participant.Steps.Where(s => s.DateOfSteps >= _startDate && s.DateOfSteps <= _endDate).Sum(s => s.StepCount) + }).ToList() + }) + .OrderBy(t => t.TeamName) + .ToList(); + + var averageTeamSize = await GetAverageTeamSize(); + + //TODO this should be returning all in one flat list so these can be added inline + //var teamsWithAverageScores = GetAverageStepsForTeamWithLessPeople(teams, averageTeamSize); + foreach (var team in teams) + { + foreach (var participant in team.ParticipantsStepsOverviews) + { + if (participant.StepTotal > overview.HighestStepsParticipant) + { + overview.HighestStepsParticipant = participant.StepTotal; + overview.HighestStepsParticipantId = participant.ParticipantId; + } + } + + if (team.TeamTotalSteps > overview.HighestStepsTeam) + { + overview.HighestStepsTeam = team.TeamTotalSteps; + overview.HighestStepsTeamId = team.TeamId; + } + + if (team.NumberOfParticipants != averageTeamSize && team.NumberOfParticipants != 0) + { + team.TeamTotalStepsWithAverage = GetAveragedStepCountTotal(team.TeamTotalSteps, team.NumberOfParticipants, averageTeamSize); + } + if(team.NumberOfParticipants != team.ParticipantsStepsOverviews.Count) + { + team.ParticipantsStepsOverviews.AddRange(GetMissingParticipants(team)); + } + } + + overview.Teams = teams; + + return overview; + } + + private List GetMissingParticipants(TeamScoresOverview team) + { + var need = team.NumberOfParticipants - team.ParticipantsStepsOverviews.Count; + var emptyParticipant = new ParticipantsStepsOverview + { + ParticipantName = "Not registered", + StepsOverviews = new List(), + StepTotal = 0 + }; + var emptyParticipants = new ParticipantsStepsOverview[need]; + Array.Fill(emptyParticipants, emptyParticipant); + + return emptyParticipants.ToList(); + } + + private List GetAllDaysSteps(IEnumerable steps) + { + var days = new List(); + for (var dt = _startDate; dt <= _endDate; dt = dt.AddDays(1)) + { + var dayStepCount = new StepsOverview + { + StepCount = steps.Any(s => s.DateOfSteps == dt) ? steps.First(s => s.DateOfSteps == dt).StepCount : 0, + DateOfSteps = dt, + }; + days.Add(dayStepCount); + } + + return days; + } + private int GetTeamSize() { // average number of people in a team @@ -142,5 +263,14 @@ public DateTime StartOfWeek(DateTime dt, DayOfWeek startOfWeek) return dt.AddDays(-1 * diff).Date; } + public virtual async Task GetAverageTeamSize() + { + var average = await _stepContext.ChallengeSettings + .Where(s => s.ChallengeSettingsId == 1) + .FirstOrDefaultAsync(); + + return average?.NumberOfParticipantsInATeam ?? 6; + } + } } \ No newline at end of file diff --git a/StepChallenge/Services/TeamService.cs b/StepChallenge/Services/TeamService.cs index b2780d5..dc3aeaf 100644 --- a/StepChallenge/Services/TeamService.cs +++ b/StepChallenge/Services/TeamService.cs @@ -12,7 +12,7 @@ public class TeamService { private readonly StepContext _stepContext; private DateTime _startDate = new DateTime(2019,09,16, 0,0,0); - private DateTime _endDate = new DateTime(2019, 12, 05,0,0,0); + private DateTime _endDate = new DateTime(2019, 12, 01,0,0,0); public TeamService(StepContext stepContext) { @@ -46,7 +46,7 @@ public List GetTeamScoreBoard(int teamId) .Select(s => new TeamScoreBoard { DateOfSteps = s.First().DateOfSteps, - StepCount = s.Where(st => st.DateOfSteps >= _startDate && st.DateOfSteps < _endDate) + StepCount = s.Where(st => st.DateOfSteps >= _startDate && st.DateOfSteps <= _endDate) .Sum(st => st.StepCount), }) .OrderBy(s => s.DateOfSteps) diff --git a/StepChallenge/Startup.cs b/StepChallenge/Startup.cs index 078da43..20810b7 100644 --- a/StepChallenge/Startup.cs +++ b/StepChallenge/Startup.cs @@ -17,6 +17,7 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Model.GraphQL; using StepChallenge.Controllers; +using StepChallenge.DataModels; using StepChallenge.Mutation; using StepChallenge.Query; using StepChallenge.Services; @@ -56,6 +57,10 @@ public void ConfigureServices(IServiceCollection services) services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); @@ -145,7 +150,7 @@ public async void Configure(IApplicationBuilder app, IHostingEnvironment env) app.UseSession(); app.UseCookiePolicy(); app.UseAuthentication(); - app.UseGraphiQl("/graphql"); + //app.UseGraphiQl("/graphql"); app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseSpaStaticFiles();