public async Task <Challenge> Grade(SectionSubmission model, string actorId) { var entity = await Store.Retrieve(model.Id); // TODO: don't log auto-grader events // if (model.Id != actorId) entity.Events.Add(new Data.ChallengeEvent { Id = Guid.NewGuid().ToString("n"), UserId = actorId, TeamId = entity.TeamId, Timestamp = DateTimeOffset.UtcNow, Type = ChallengeEventType.Submission }); double currentScore = entity.Score; var result = await Sync( entity, Mojo.GradeChallengeAsync(model) ); if (result.Score > currentScore) { await Store.UpdateTeam(entity.TeamId); } return(Mapper.Map <Challenge>(entity)); }
private void _Grade(ChallengeSpec spec, SectionSubmission submission) { var section = spec.Challenge.Sections.ElementAtOrDefault(submission.SectionIndex); double lastScore = spec.Score; int i = 0; foreach (var question in section.Questions) { question.Grade(submission.Questions.ElementAtOrDefault(i++)?.Answer ?? ""); } section.Score = section.Questions .Where(q => q.IsCorrect) .Select(q => q.Weight - q.Penalty) .Sum() ; spec.Score = spec.Challenge.Sections .SelectMany(s => s.Questions) .Where(q => q.IsCorrect) .Select(q => q.Weight - q.Penalty) .Sum() ; if (spec.Score > lastScore) { spec.LastScoreTime = submission.Timestamp; } }
public async Task <ActionResult <GameState> > GradeChallenge([FromBody] SectionSubmission model) { await Validate(new Entity { Id = model.Id }); AuthorizeAny( () => Actor.IsAdmin, () => model.Id == Actor.Id, // from valid grader_apikey () => _svc.CanInteract(model.Id, Actor.Id).Result ); return(Ok( await _svc.Grade(model) )); }
public async Task <Challenge> Grade([FromBody] SectionSubmission model) { AuthorizeAny( () => Actor.IsDirector, () => Actor.Id == model.Id, // auto-grader () => ChallengeService.UserIsTeamPlayer(model.Id, Actor.Id).Result ); await Validate(new Entity { Id = model.Id }); var result = await ChallengeService.Grade(model, Actor.Id); await Hub.Clients.Group(result.TeamId).ChallengeEvent( new HubEvent <Challenge>(result, EventAction.Updated) ); return(result); }
private bool IsDuplicateSubmission(ICollection <SectionSubmission> history, SectionSubmission submission) { bool dupe = false; string incoming = string.Join('|', submission.Questions.Select(q => q.Answer ?? "") ); foreach (var s in history) { string target = string.Join('|', s.Questions.Select(q => q.Answer ?? "") ); dupe |= target.Equals(incoming); if (dupe) { break; } } return(dupe || incoming.Replace("|", "") == string.Empty); }
public async Task <GameState> Grade(SectionSubmission submission) { DateTimeOffset ts = DateTimeOffset.UtcNow; string id = submission.Id; if (!await _locker.Lock(id)) { throw new ResourceIsLocked(); } var ctx = await LoadContext(id); var spec = JsonSerializer.Deserialize <ChallengeSpec>(ctx.Gamespace.Challenge, jsonOptions); var section = spec.Challenge.Sections.ElementAtOrDefault(submission.SectionIndex); if (IsDuplicateSubmission(spec.Submissions, submission)) { _locker.Unlock(id).Wait(); return(await LoadState(ctx.Gamespace)); } if (section == null) { _locker.Unlock(id, new InvalidOperationException()).Wait(); } if (!ctx.Gamespace.IsActive) { _locker.Unlock(id, new GamespaceIsExpired()).Wait(); } if (spec.MaxAttempts > 0 && spec.Submissions.Where(s => s.SectionIndex == submission.SectionIndex).Count() >= spec.MaxAttempts) { _locker.Unlock(id, new AttemptLimitReached()).Wait(); } submission.Timestamp = ts; spec.Submissions.Add(submission); _Grade(spec, submission); ctx.Gamespace.Challenge = JsonSerializer.Serialize(spec, jsonOptions); // handle completion if max attempts reached or full score if ( spec.Score == 1 || ( spec.MaxAttempts > 0 && spec.Submissions.Count == spec.MaxAttempts ) ) { ctx.Gamespace.EndTime = ts; } await _store.Update(ctx.Gamespace); // map return model var result = await LoadState(ctx.Gamespace); // merge submission into return model int i = 0; foreach (var question in result.Challenge.Questions) { question.Answer = submission.Questions.ElementAtOrDefault(i++)?.Answer ?? ""; } await _locker.Unlock(id); return(result); }