public static void SubmitScore(Slide slide, string userId) { var ltiRequestsRepo = new LtiRequestsRepo(); var consumersRepo = new ConsumersRepo(); var visitsRepo = new VisitsRepo(); var ltiRequest = ltiRequestsRepo.Find(userId, slide.Id); if (ltiRequest == null) { throw new Exception("LtiRequest for user '" + userId + "' not found"); } var consumerSecret = consumersRepo.Find(ltiRequest.ConsumerKey).Secret; var score = visitsRepo.GetScore(slide.Id, userId); // TODO: fix outcome address in local edx (no localhost and no https) var uri = new UriBuilder(ltiRequest.LisOutcomeServiceUrl); if (uri.Host == "localhost") { uri.Host = "192.168.33.10"; uri.Port = 80; uri.Scheme = "http"; } var result = OutcomesClient.PostScore(uri.ToString(), ltiRequest.ConsumerKey, consumerSecret, ltiRequest.LisResultSourcedId, score / (double)slide.MaxScore); if (!result.IsValid) { throw new Exception(uri + "\r\n\r\n" + result.Message); } }
/// <summary> /// Returns True if the <see cref="IOwinRequest"/> contains an LTI request that requires authentication. /// </summary> /// <param name="request">The <see cref="IOwinRequest"/> to examine.</param> /// <returns>True if the request contains an LTI request that requires authentication.</returns> public static async Task <bool> IsAuthenticatedLtiRequestAsync(this IOwinRequest request) { // All LTI 1.x communication is handled with HTTP POST if (!request.Method.Equals(WebRequestMethods.Http.Post)) { return(false); } // All LTI requests should contain content and specify ContentType if (string.IsNullOrEmpty(request.ContentType)) { return(false); } // LTI launch and content item requests are sent as form posts if (request.ContentType.Equals("application/x-www-form-urlencoded")) { var form = await request.ReadFormAsync(); var messageType = form[LtiConstants.LtiMessageTypeParameter]; return(!string.IsNullOrWhiteSpace(messageType)); } // LTI Outcome requests and responses are sent as Plain Old XML if (request.ContentType.Equals("application/xml")) { return(OutcomesClient.IsLtiOutcomesRequest(request.Body)); } // Tool Consumer Profile requests and responses do not need authentication return(false); }
public ActionResult PostScore(PostScoreModel model) { var outcome = ProviderContext.Outcomes.Find(model.OutcomeId); var consumer = ProviderContext.Consumers.Find(outcome.ConsumerId); var result = Task.Run(() => OutcomesClient.PostScoreAsync( outcome.ServiceUrl, consumer.Key, consumer.Secret, outcome.LisResultSourcedId, model.Score)).Result; return(RedirectToAction("View", new { id = model.ToolId, success = result.IsValid })); }
public static void SubmitScore(string courseId, Slide slide, string userId, Visit visit = null) { var db = new ULearnDb(); var ltiRequestsRepo = new LtiRequestsRepo(db); var consumersRepo = new ConsumersRepo(db); var visitsRepo = new VisitsRepo(db); var ltiRequest = ltiRequestsRepo.Find(courseId, userId, slide.Id); if (ltiRequest == null) { throw new Exception("LtiRequest for user '" + userId + "' not found"); } var consumerSecret = consumersRepo.Find(ltiRequest.ConsumerKey).Secret; var score = visit?.Score ?? visitsRepo.GetScore(courseId, slide.Id, userId); log.Info($"Надо отправить результаты слайда {slide.Id} пользователя {userId} по LTI. Нашёл LtiRequest: {ltiRequest.JsonSerialize()}"); UriBuilder uri; try { uri = new UriBuilder(ltiRequest.LisOutcomeServiceUrl); } catch (Exception e) { log.Error($"Неверный адрес отправки результатов по LTI: {ltiRequest.LisOutcomeServiceUrl}", e); throw; } if (uri.Host == "localhost") { uri.Host = "192.168.33.10"; uri.Port = 80; uri.Scheme = "http"; } var maxScore = ControllerUtils.GetMaxScoreForUsersSlide(slide, true, false, false); var outputScore = score / (double)maxScore; log.Info($"Отправляю результаты на {ltiRequest.LisOutcomeServiceUrl}: {score} из {maxScore} ({outputScore})"); /* Sometimes score is bigger then slide's MaxScore, i.e. in case of manual checking */ if (score > maxScore) { outputScore = 1; } var result = OutcomesClient.PostScore(uri.ToString(), ltiRequest.ConsumerKey, consumerSecret, ltiRequest.LisResultSourcedId, outputScore); if (!result.IsValid) { throw new Exception(uri + "\r\n\r\n" + result.Message); } }
public static async Task SubmitScore(string courseId, Slide slide, string userId, int score, ILtiRequestsRepo ltiRequestsRepo, ILtiConsumersRepo consumersRepo) { var ltiRequestJson = await ltiRequestsRepo.Find(courseId, userId, slide.Id); if (ltiRequestJson == null) { throw new Exception("LtiRequest for user '" + userId + "' not found"); } var ltiRequest = JsonConvert.DeserializeObject <LtiRequest>(ltiRequestJson); var consumerSecret = (await consumersRepo.Find(ltiRequest.ConsumerKey)).Secret; log.Info($"Надо отправить результаты слайда {slide.Id} пользователя {userId} по LTI. Нашёл LtiRequest: {ltiRequestJson}"); UriBuilder uri; try { uri = new UriBuilder(ltiRequest.LisOutcomeServiceUrl); } catch (Exception e) { log.Error(e, $"Неверный адрес отправки результатов по LTI: {ltiRequest.LisOutcomeServiceUrl}"); throw; } if (uri.Host == "localhost") { uri.Host = "192.168.33.10"; uri.Port = 80; uri.Scheme = "http"; } var maxScore = BaseController.GetMaxScoreForUsersSlide(slide, true, false, false); var outputScore = score / (double)maxScore; log.Info($"Отправляю результаты на {ltiRequest.LisOutcomeServiceUrl}: {score} из {maxScore} ({outputScore})"); /* Sometimes score is bigger then slide's MaxScore, i.e. in case of manual checking */ if (score > maxScore) { outputScore = 1; } var result = OutcomesClient.PostScore(uri.ToString(), ltiRequest.ConsumerKey, consumerSecret, ltiRequest.LisResultSourcedId, outputScore); if (!result.IsValid) { throw new Exception(uri + "\r\n\r\n" + result.Message); } }
public void SubmitScore() { var userId = User.Identity.GetUserId(); var score = scoreRepo.Find(userId); if (score == null) { throw new Exception("Score for user '" + userId + "' not found"); } var ltiRequest = ltiRequestsRepo.Find(userId); if (ltiRequest == null) { throw new Exception("LtiRequest for user '" + userId + "' not found"); } var consumerSecret = consumersRepo.Find(ltiRequest.ConsumerKey).Secret; // TODO: fix outcome address in local edx (no localhost and no https) var uri = new UriBuilder(ltiRequest.LisOutcomeServiceUrl); if (uri.Host == "localhost") { uri.Host = "192.168.33.10"; uri.Port = 80; uri.Scheme = "http"; } var result = OutcomesClient.PostScore(uri.ToString(), ltiRequest.ConsumerKey, consumerSecret, ltiRequest.LisResultSourcedId, score.Value / 100.0); if (!result.IsValid) { throw new Exception(uri.ToString() + "\r\n\r\n" + result.Message); } }
public ActionResult PostScores(int id) { var outcome = GetOutcomeFromClaim(); if (outcome != null) { var consumer = ProviderContext.Consumers.Find(outcome.ConsumerId); var score = OutcomesClient.ReadScore(outcome.ServiceUrl, consumer.Key, consumer.Secret, outcome.LisResultSourcedId); if (score.IsValid) { return(PartialView("_PostScoresPartial", new PostScoreModel { ConsumerName = consumer.Name, ContextTitle = outcome.ContextTitle, OutcomeId = outcome.OutcomeId, Score = score.Score, ToolId = id })); } } return(new EmptyResult()); }
public async Task <ActionResult> Outcomes2(Outcomes2Model model, string submit) { switch (submit) { case "Delete LineItem (Delete)": var deleteLineItemResponse = await OutcomesClient.DeleteLineItem( model.LineItemServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = deleteLineItemResponse.HttpRequest; model.HttpResponse = deleteLineItemResponse.HttpResponse; switch (deleteLineItemResponse.StatusCode) { case HttpStatusCode.OK: model.LineItem = null; ModelState.Clear(); ViewBag.Message = "200 LineItem deleted"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(deleteLineItemResponse.StatusCode) + " " + deleteLineItemResponse.StatusCode; break; } break; case "Get LineItem (Read)": var getLineItemResponse = await OutcomesClient.GetLineItem( model.LineItemServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = getLineItemResponse.HttpRequest; model.HttpResponse = getLineItemResponse.HttpResponse; switch (getLineItemResponse.StatusCode) { case HttpStatusCode.OK: model.LineItem = getLineItemResponse.Outcome; ModelState.Clear(); ViewBag.Message = "200 LineItem received"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(getLineItemResponse.StatusCode) + " " + getLineItemResponse.StatusCode; break; } break; case "Get LineItemResults (Read)": var getLineItemResultsResponse = await OutcomesClient.GetLineItemWithResults( model.LineItemServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = getLineItemResultsResponse.HttpRequest; model.HttpResponse = getLineItemResultsResponse.HttpResponse; switch (getLineItemResultsResponse.StatusCode) { case HttpStatusCode.OK: model.LineItem = getLineItemResultsResponse.Outcome; ModelState.Clear(); ViewBag.Message = "200 LineItem received"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(getLineItemResultsResponse.StatusCode) + " " + getLineItemResultsResponse.StatusCode; break; } break; case "Get LineItems (Read)": var getLineItemsResponse = await OutcomesClient.GetLineItems( model.LineItemsServiceUrl, model.ConsumerKey, model.ConsumerSecret, activityId : model.LineItem.AssignedActivity.ActivityId); model.HttpRequest = getLineItemsResponse.HttpRequest; model.HttpResponse = getLineItemsResponse.HttpResponse; switch (getLineItemsResponse.StatusCode) { case HttpStatusCode.OK: ViewBag.Message = "200 LineItems received"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(getLineItemsResponse.StatusCode) + " " + getLineItemsResponse.StatusCode; break; } break; case "Post LineItem (Create)": var postLineItem = new LineItem { ReportingMethod = "totalScore", LineItemOf = new Context { ContextId = model.ContextId }, AssignedActivity = new Activity { ActivityId = model.LineItem.AssignedActivity.ActivityId }, ScoreContraints = new NumericLimits { NormalMaximum = 100, ExtraCreditMaximum = 10, TotalMaximum = 110 } }; var postLineItemResponse = await OutcomesClient.PostLineItem( postLineItem, model.LineItemsServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = postLineItemResponse.HttpRequest; model.HttpResponse = postLineItemResponse.HttpResponse; switch (postLineItemResponse.StatusCode) { case HttpStatusCode.Created: model.LineItem = postLineItemResponse.Outcome; ModelState.Clear(); ViewBag.Message = "201 LineItem added"; break; case HttpStatusCode.BadRequest: ViewBag.Message = "400 Bad Request"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(postLineItemResponse.StatusCode) + " " + postLineItemResponse.StatusCode; break; } break; case "Put LineItem (Update)": var putLineItem = new LineItem { Id = model.LineItem.Id, ReportingMethod = "totalScore", LineItemOf = new Context { ContextId = model.ContextId }, AssignedActivity = new Activity { ActivityId = model.LineItem.AssignedActivity.ActivityId }, ScoreContraints = new NumericLimits { NormalMaximum = 100, ExtraCreditMaximum = 10, TotalMaximum = 110 }, Results = model.LineItem.Results }; var putLineItemResponse = await OutcomesClient.PutLineItem( putLineItem, model.LineItemsServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = putLineItemResponse.HttpRequest; model.HttpResponse = putLineItemResponse.HttpResponse; switch (putLineItemResponse.StatusCode) { case HttpStatusCode.OK: ViewBag.Message = "200 LineItem updated"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(putLineItemResponse.StatusCode) + " " + putLineItemResponse.StatusCode; break; } break; case "Delete Result (Delete)": var deleteResultResponse = await OutcomesClient.DeleteResult( model.ResultServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = deleteResultResponse.HttpRequest; model.HttpResponse = deleteResultResponse.HttpResponse; switch (deleteResultResponse.StatusCode) { case HttpStatusCode.OK: model.LineItem = null; ModelState.Clear(); ViewBag.Message = "200 Result deleted"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(deleteResultResponse.StatusCode) + " " + deleteResultResponse.StatusCode; break; } break; case "Get Result (Read)": var getResultResponse = await OutcomesClient.GetResult( model.ResultServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = getResultResponse.HttpRequest; model.HttpResponse = getResultResponse.HttpResponse; switch (getResultResponse.StatusCode) { case HttpStatusCode.OK: model.Result = getResultResponse.Outcome; ModelState.Clear(); ViewBag.Message = "200 LineItem received"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(getResultResponse.StatusCode) + " " + getResultResponse.StatusCode; break; } break; case "Get Results (Read)": var getResultsResponse = await OutcomesClient.GetResults( model.ResultsServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = getResultsResponse.HttpRequest; model.HttpResponse = getResultsResponse.HttpResponse; switch (getResultsResponse.StatusCode) { case HttpStatusCode.OK: ViewBag.Message = "200 Results received"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(getResultsResponse.StatusCode) + " " + getResultsResponse.StatusCode; break; } break; case "Post Result (Create)": var postResult = new LisResult { Comment = "Good job!", ResultAgent = new LisPerson { UserId = "12345" }, ResultOf = new Uri(model.LineItemServiceUrl), ResultScore = "0.75", ResultScoreConstraints = new NumericLimits { TotalMaximum = 1 }, ResultStatus = ResultStatus.Completed, TotalScore = (decimal?)0.75 }; var postResultResponse = await OutcomesClient.PostResult( postResult, model.ResultServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = postResultResponse.HttpRequest; model.HttpResponse = postResultResponse.HttpResponse; switch (postResultResponse.StatusCode) { case HttpStatusCode.Created: //model.LineItem = postResult.Outcome; ModelState.Clear(); ViewBag.Message = "201 Result added"; break; case HttpStatusCode.BadRequest: ViewBag.Message = "400 Bad Request"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(postResultResponse.StatusCode) + " " + postResultResponse.StatusCode; break; } break; case "Put Result (Update)": var putResult = new LisResult { Comment = "Good job!", ResultAgent = new LisPerson { UserId = "12345" }, ResultOf = new Uri(model.LineItemServiceUrl), ResultScore = "0.75", ResultScoreConstraints = new NumericLimits { TotalMaximum = 1 }, ResultStatus = ResultStatus.Final, // Change the status to Final TotalScore = (decimal?)0.75 }; var putResultResponse = await OutcomesClient.PutResult( putResult, model.ResultServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = putResultResponse.HttpRequest; model.HttpResponse = putResultResponse.HttpResponse; switch (putResultResponse.StatusCode) { case HttpStatusCode.OK: ViewBag.Message = "200 Result updated"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(putResultResponse.StatusCode) + " " + putResultResponse.StatusCode; break; } break; } return(View(model)); }
public async Task <ActionResult> Outcomes(LineItemModel model, string submit) { switch (submit) { case "Delete LineItem": var deleteLineItemResponse = await OutcomesClient.DeleteLineItem( model.LineItemServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = deleteLineItemResponse.HttpRequest; model.HttpResponse = deleteLineItemResponse.HttpResponse; switch (deleteLineItemResponse.StatusCode) { case HttpStatusCode.OK: model.LineItem = null; ModelState.Clear(); ViewBag.Message = "200 LineItem deleted"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(deleteLineItemResponse.StatusCode) + " " + deleteLineItemResponse.StatusCode; break; } break; case "Get LineItem": var getLineItemResponse = await OutcomesClient.GetLineItem( model.LineItemServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = getLineItemResponse.HttpRequest; model.HttpResponse = getLineItemResponse.HttpResponse; switch (getLineItemResponse.StatusCode) { case HttpStatusCode.OK: model.LineItem = getLineItemResponse.Outcome; ModelState.Clear(); ViewBag.Message = "200 LineItem received"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(getLineItemResponse.StatusCode) + " " + getLineItemResponse.StatusCode; break; } break; case "Get LineItems": var getLineItemsResponse = await OutcomesClient.GetLineItemPage( model.LineItemsServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = getLineItemsResponse.HttpRequest; model.HttpResponse = getLineItemsResponse.HttpResponse; switch (getLineItemsResponse.StatusCode) { case HttpStatusCode.OK: ViewBag.Message = "200 LineItems received"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(getLineItemsResponse.StatusCode) + " " + getLineItemsResponse.StatusCode; break; } break; case "Post LineItem": var postLineItem = new LineItem { ReportingMethod = "res:totalScore", LineItemOf = new Context { ContextId = model.ContextId }, AssignedActivity = new Activity { ActivityId = model.LineItem.AssignedActivity.ActivityId }, ScoreContraints = new NumericLimits { NormalMaximum = 100, ExtraCreditMaximum = 10, TotalMaximum = 110 } }; var postLineItemResponse = await OutcomesClient.PostLineItem( postLineItem, model.LineItemsServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = postLineItemResponse.HttpRequest; model.HttpResponse = postLineItemResponse.HttpResponse; switch (postLineItemResponse.StatusCode) { case HttpStatusCode.Created: model.LineItem = postLineItemResponse.Outcome; ModelState.Clear(); ViewBag.Message = "201 LineItem added"; break; case HttpStatusCode.BadRequest: ViewBag.Message = "400 Bad Request"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(postLineItemResponse.StatusCode) + " " + postLineItemResponse.StatusCode; break; } break; case "Put LineItem": var putLineItem = new LineItem { Id = model.LineItem.Id, ReportingMethod = "res:totalScore", LineItemOf = new Context { ContextId = model.ContextId }, AssignedActivity = new Activity { ActivityId = model.LineItem.AssignedActivity.ActivityId }, ScoreContraints = new NumericLimits { NormalMaximum = 100, ExtraCreditMaximum = 10, TotalMaximum = 110 }, Results = model.LineItem.Results }; var putLineItemResponse = await OutcomesClient.PutLineItem( putLineItem, model.LineItemsServiceUrl, model.ConsumerKey, model.ConsumerSecret); model.HttpRequest = putLineItemResponse.HttpRequest; model.HttpResponse = putLineItemResponse.HttpResponse; switch (putLineItemResponse.StatusCode) { case HttpStatusCode.OK: ViewBag.Message = "200 LineItem updated"; break; case HttpStatusCode.Unauthorized: ViewBag.Message = "401 Not authorized"; break; case HttpStatusCode.NotFound: ViewBag.Message = "404 Not found"; break; case HttpStatusCode.InternalServerError: ViewBag.Message = "500 Internal server error"; break; default: ViewBag.Message = Convert.ToInt32(putLineItemResponse.StatusCode) + " " + putLineItemResponse.StatusCode; break; } break; } return(View(model)); }