/// <summary> /// Returns information about all short-answer questions that appear on the quiz /// </summary> /// <returns></returns> private static List <Question> GetQuestions() { List <Question> questions = new List <Question>(); using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/api/v1/courses/{0}/quizzes/{1}/questions?per_page=100", Auth.COURSE_ID, quizID); while (url != null) { HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic qs = JArray.Parse(response.Content.ReadAsStringAsync().Result); foreach (dynamic question in qs) { if (question.question_type == "short_answer_question") { questions.Add(new Question(question)); } } } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read question information"); throw new Exception(); } url = GetNextURL(response.Headers); } return(questions); } }
/// <summary> /// Get information about all submissions made to the quiz /// </summary> private static List <Submission> GetSubmissions() { List <Submission> submissions = new List <Submission>(); using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/api/v1/courses/{0}/quizzes/{1}/submissions?per_page=100", Auth.COURSE_ID, quizID); while (url != null) { HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic data = JObject.Parse(response.Content.ReadAsStringAsync().Result); data = data.quiz_submissions; foreach (dynamic submission in data) { submissions.Add(new Submission(submission)); } } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read course"); throw new Exception(); } url = GetNextURL(response.Headers); } return(submissions); } }
private static List <string> GetQuizIDs(string quizName) { List <string> quizIDs = new List <string>(); using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/api/v1/courses/{0}/quizzes?search_term={1}&per_page=100", Auth.COURSE_ID, quizName); while (url != null) { HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic quizzes = JArray.Parse(response.Content.ReadAsStringAsync().Result); foreach (dynamic quiz in quizzes) { if (quiz.title == quizName) { quizIDs.Add(quiz.id.ToString()); } } } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read quiz information"); throw new Exception(); } url = GetNextURL(response.Headers); } return(quizIDs); } }
private static List <KeyValuePair <string, string> > GetInputs(Submission submission, IList <Question> questions) { using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/courses/{0}/quizzes/{1}/history?headless=1&hide_student_name=0&score_updated=1&user_id={2}&version={3}", Auth.COURSE_ID, quizID, submission.UserId, submission.Attempt); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { string page = response.Content.ReadAsStringAsync().Result; //Console.WriteLine(page); List <KeyValuePair <string, string> > inputs = new List <KeyValuePair <string, string> >(); int inputIndex = page.IndexOf("<input "); while (inputIndex >= 0) { int nameIndex = page.IndexOf("name=\"", inputIndex) + 6; int valueIndex = page.IndexOf("value=\"", inputIndex) + 7; int nameQuote = page.IndexOf("\"", nameIndex); int valueQuote = page.IndexOf("\"", valueIndex); string name = page.Substring(nameIndex, nameQuote - nameIndex); string value = page.Substring(valueIndex, valueQuote - valueIndex); if (name != "answer_text") { inputs.Add(new KeyValuePair <string, string>(name, value)); } inputIndex = page.IndexOf("<input ", inputIndex + 1); } int textIndex = page.IndexOf("<textarea "); while (textIndex >= 0) { int nameIndex = page.IndexOf("name=\"", textIndex) + 6; int valueIndex = page.IndexOf(">", textIndex) + 1; int nameQuote = page.IndexOf("\"", nameIndex); int valueEnd = page.IndexOf("</textarea>", valueIndex); string name = page.Substring(nameIndex, nameQuote - nameIndex); string value = page.Substring(valueIndex, valueEnd - valueIndex); inputs.Add(new KeyValuePair <string, string>(name, value)); textIndex = page.IndexOf("<textarea ", textIndex + 1); } return(inputs); } else { Console.WriteLine("Error: " + (int)response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read page"); throw new Exception(); } } }
private static AllAttempts GetAllAttempts(List <Question> questions) { using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/api/v1/courses/{0}/quizzes/{1}/reports", Auth.COURSE_ID, quizID); var inputs = new Dictionary <string, string>(); inputs["quiz_report[report_type]"] = "student_analysis"; inputs["quiz_report[includes_all_versions]"] = "true"; while (true) { var content = new FormUrlEncodedContent(inputs); HttpResponseMessage response = client.PostAsync(url, content).Result; if (response.IsSuccessStatusCode) { dynamic qs = JObject.Parse(response.Content.ReadAsStringAsync().Result); if (qs.file != null) { using (StreamReader reader = new StreamReader(WebRequest.Create(qs.file.url.ToString()).GetResponse().GetResponseStream())) { using (CsvReader parser = new CsvReader(reader)) { AllAttempts attempts = new AllAttempts(parser, questions); return(attempts); } } } else { Console.WriteLine("Waiting for quiz report to be generated by Canvas..."); Thread.Sleep(1000); } } else if (response.StatusCode == HttpStatusCode.Conflict) { Console.WriteLine("Waiting for quiz report to be generated by Canvas..."); Thread.Sleep(1000); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to obtain quiz analysis"); throw new Exception(); } } } }
private static string GetCourseName() { using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/api/v1/courses/{0}", Auth.COURSE_ID); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic course = JObject.Parse(response.Content.ReadAsStringAsync().Result); return(course.name); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read course information"); throw new Exception(); } } }
/// <summary> /// Accumulate answers given to the questions in the submission /// </summary> /// <param name="questions"></param> /// <param name="answers"></param> private static void AddAnswers(Submission submission, IList <Question> questions, Dictionary <int, SortedSet <string> > answers) { using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/courses/{0}/quizzes/{1}/history?headless=1&user_id={2}&version={3}", Auth.COURSE_ID, quizID, submission.UserId, submission.Attempt); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { string page = response.Content.ReadAsStringAsync().Result; foreach (Question q in questions) { int start = page.IndexOf("<input type=\"text\" name=\"question_" + q.Id + "\""); start = page.IndexOf("value=\"", start) + 7; int stop = page.IndexOf("\"", start); String value = page.Substring(start, stop - start); SortedSet <string> set; if (answers.TryGetValue(q.Position, out set)) { set.Add(value.Trim()); } else { set = new SortedSet <string>(); answers[q.Position] = set; } } } else { Console.WriteLine("Error: " + (int)response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read course"); return; } } }
public static void SetScores() { // Get information about all of the short answer questions that appear on the quiz List <Question> questions = GetQuestions(); Console.WriteLine(questions.Count + " short answer questions found"); // Get information about all submissions to the quiz List <Submission> submissions = GetSubmissions(); Console.WriteLine(submissions.Count + " submissions found"); // Get information about attempts AllAttempts attempts = GetAllAttempts(questions); Console.WriteLine(attempts.Count + " attempts found"); // Read the grading rules for this quiz GradingRules rules; using (var input = new CsvReader(new StreamReader(@"..\..\" + GetQuizFilename()))) { rules = new GradingRules(input); } Console.WriteLine(GetQuizFilename() + " is based on " + rules.Count + " attempts"); // Warn the user int delta = attempts.Count - rules.Count; if (delta != 0) { Console.WriteLine("There have " + delta + " attempts since " + GetQuizFilename() + " was created"); Console.WriteLine("You should recreate that file"); } // Make sure we proceed Console.Write("Update grades in Canvas? [y/n] "); if (Console.ReadLine().Trim().ToLower() != "y") { return; } // Change the grade of attempt if necessary foreach (Attempt attempt in attempts.GetAttempts()) { Submission submission = GetSubmission(submissions, attempt.UserID, attempt.Number); dynamic correction = GetCorrection(attempt, questions, rules, out string name); if (correction == null) { Console.WriteLine("unchanged (v" + attempt.Number + ") " + name); } else if (submission == null) { Console.WriteLine("*MISSING* (v" + attempt.Number + ") " + name); } else { using (HttpClient client = CanvasHttp.MakeClient()) { String url = String.Format("/api/v1/courses/{0}/quizzes/{1}/submissions/{2} ", Auth.COURSE_ID, quizID, submission.Id); var content = new StringContent(correction.ToString(), Encoding.UTF8, "application/json"); HttpResponseMessage response = client.PutAsync(url, content).Result; //HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK); if (response.IsSuccessStatusCode) { Console.WriteLine("updated (v" + attempt.Number + ") " + name); } else { Console.WriteLine("*FAILED* (v" + attempt.Number + ") " + name + " " + response.StatusCode); return; } } } } }
/// <summary> /// Adds a note to each student's grade /// </summary> public static void Main(string[] args) { // Get the course name String courseName = ""; using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/api/v1/courses/{0}", Auth.COURSE_ID); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic resp = JObject.Parse(response.Content.ReadAsStringAsync().Result); courseName = resp.name; Console.WriteLine("Labeler for " + courseName); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read course"); return; } } // Get the assignment ID string assignID; Console.Write("Enter assignment name: "); string assignName = Console.ReadLine(); List <string> assignmentIDs = CanvasHttp.GetAssignmentIDs(assignName); if (assignmentIDs.Count == 0) { Console.WriteLine("There is no assigment by that name"); return; } else if (assignmentIDs.Count > 1) { Console.WriteLine("There are " + assignmentIDs.Count + " assignments by that name"); return; } else { assignID = assignmentIDs[0]; } // Confirm with the user Console.WriteLine("You are about to label the submissions for " + courseName + " " + assignName + " with this message: "); Console.WriteLine(); Console.WriteLine(message); Console.WriteLine(); Console.WriteLine("Do you want to continue? [y/n]"); String yesno = Console.ReadLine(); if (yesno != "y") { return; } // Get the students Dictionary <string, string> allStudents = new Dictionary <string, string>(); using (HttpClient client = CanvasHttp.MakeClient()) { string url = String.Format("/api/v1/courses/{0}/search_users?enrollment_type[]=student&per_page=200", Auth.COURSE_ID); while (url != null) { HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic resp = JArray.Parse(response.Content.ReadAsStringAsync().Result); foreach (dynamic user in resp) { allStudents[user.id.ToString()] = user.name.ToString(); //allStudents[user.sis_user_id.ToString()] = user.name.ToString(); } url = CanvasHttp.GetNextURL(response.Headers); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read users"); return; } } Console.WriteLine("Student count: " + allStudents.Count); } // Do the labeling foreach (string id in allStudents.Keys) { using (HttpClient client = CanvasHttp.MakeClient()) { string data = "&comment[text_comment]=" + Uri.EscapeDataString(message); string url = String.Format("/api/v1/courses/{0}/assignments/{1}/submissions/{2}", Auth.COURSE_ID, assignID, id); //string url = String.Format("/api/v1/courses/{0}/assignments/{1}/submissions/sis_user_id:{2}", courseID, assignID, id); StringContent content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded"); HttpResponseMessage response = client.PutAsync(url, content).Result; if (response.IsSuccessStatusCode) { Console.WriteLine("Success: " + allStudents[id] + " " + id); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine(allStudents[id] + " " + id); Console.WriteLine(); } } } }
/// <summary> /// Obtains the logs /// </summary> public static void Main(string[] args) { // Get the course name and the Kattis problem String courseName; using (HttpClient client = CreateClient(REDIRECT)) { string url = String.Format("https://utah.instructure.com/api/v1/courses/{0}", Auth.COURSE_ID); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic resp = JObject.Parse(response.Content.ReadAsStringAsync().Result); courseName = resp.name; Console.WriteLine("Grading Kattis problem for " + courseName); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read course"); return; } } // Get the assignment ID string assignID; { Console.Write("Enter assignment name: "); string assignName = Console.ReadLine(); List <string> assignmentIDs = CanvasHttp.GetAssignmentIDs(assignName); if (assignmentIDs.Count == 0) { Console.WriteLine("There is no assigment by that name"); return; } else if (assignmentIDs.Count > 1) { Console.WriteLine("There are " + assignmentIDs.Count + " assignments by that name"); return; } else { assignID = assignmentIDs[0]; } } // Get the Kattis problem name string problemName; using (HttpClient client = CreateClient(REDIRECT)) { string url = String.Format("https://utah.instructure.com/api/v1/courses/{0}/assignments/{1}", Auth.COURSE_ID, assignID); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic resp = JObject.Parse(response.Content.ReadAsStringAsync().Result); problemName = GetKattisProblemName(resp.description.ToString()); if (problemName == null) { Console.WriteLine("No Kattis problem referenced from the problem"); return; } else { Console.WriteLine("Grading Kattis problem " + problemName); problem = problems[problemName]; problem.StartDate = Convert.ToDateTime(Auth.START_TIME).ToLocalTime(); } } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read assignment"); return; } } // Due date overrides Dictionary <int, Tuple <DateTime, DateTime> > overrides = new Dictionary <int, Tuple <DateTime, DateTime> >(); // Get the due dates and assignment name using (HttpClient client = CreateClient(AUTH)) { String url = String.Format("https://utah.instructure.com/api/v1/courses/{0}/assignments/{1}?include[]=overrides", Auth.COURSE_ID, assignID); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic resp = JObject.Parse(response.Content.ReadAsStringAsync().Result); problem.DueDate = Convert.ToDateTime(resp.due_at.ToString()).ToLocalTime(); problem.LockDate = Convert.ToDateTime(resp.lock_at.ToString()).ToLocalTime(); problem.MaxGrade = Convert.ToInt32(resp.points_possible.ToString()); Console.WriteLine("Assignment: " + resp.name); Console.WriteLine("Start Date: " + problem.StartDate); Console.WriteLine("Default Due Date: " + problem.DueDate); Console.WriteLine("Default Lock Date: " + problem.LockDate); Console.WriteLine("Max Grade: " + problem.MaxGrade); foreach (dynamic oride in resp.overrides) { foreach (dynamic id in oride.student_ids) { overrides[(int)id] = new Tuple <DateTime, DateTime>(Convert.ToDateTime(oride.due_at).ToLocalTime(), Convert.ToDateTime(oride.lock_at).ToLocalTime()); } } } else { Console.WriteLine("Error obtaining due date: " + response.StatusCode); return; } } // Get the token string csrfToken; using (HttpClient client = CreateClient(REDIRECT)) { String url = "https://utah.kattis.com/login"; HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { string body = response.Content.ReadAsStringAsync().Result; string pattern = "name=\"csrf_token\" value=\""; int index1 = body.LastIndexOf(pattern); index1 = index1 + pattern.Length; int index2 = body.IndexOf("\"", index1); csrfToken = body.Substring(index1, index2 - index1); } else { Console.WriteLine("Error obtaining Kattis token: " + response.StatusCode); return; } } // Get the cookie using (HttpClient client = CreateClient(NONE)) { String url = "https://utah.kattis.com/login"; var values = new Dictionary <string, string> { { "user", Auth.KATTIS_LOGIN }, { "password", Auth.KATTIS_PASSWORD }, { "submit", "Submit" }, { "csrf_token", csrfToken } }; var content = new FormUrlEncodedContent(values); HttpResponseMessage response = client.PostAsync(url, content).Result; if (response.StatusCode != HttpStatusCode.Redirect) { Console.WriteLine("Error logging in to Kattis: " + response.StatusCode); return; } } // Get the data dynamic exported; using (HttpClient client = CreateClient(REDIRECT)) { String url = String.Format("https://utah.kattis.com/courses/{0}/{1}/export?type=results&submissions=all&include_testcases=true", Auth.KATTIS_COURSE, Auth.KATTIS_SEMESTER); //client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; …) Gecko/20100101 Firefox/57.0"); //client.DefaultRequestHeaders.Referrer = new Uri("https://utah.kattis.com/courses/CS4150/S18/export"); //client.DefaultRequestHeaders.Host = "utah.kattis.com"; Console.WriteLine(client.DefaultRequestHeaders.Accept); HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { String s = response.Content.ReadAsStringAsync().Result; exported = JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result); } else { Console.WriteLine("Error obtaining Kattis data: " + response.StatusCode); return; } } // Get the number of test cases problem.PrivateTests = -1; List <Grade> grades = new List <Grade>(); foreach (dynamic student in exported.students) { foreach (dynamic submission in student.submissions) { if (submission.problem == problemName && submission.testcase_count != null) { int tests = Convert.ToInt32(submission.testcase_count.ToString()); Console.WriteLine("Total tests: " + tests); problem.PrivateTests = tests - problem.PublicTests; break; } } if (problem.PrivateTests >= 0) { break; } } if (problem.PrivateTests < 0) { Console.WriteLine("Unable to get count of tests"); return; } Dictionary <string, string> allStudents = new Dictionary <string, string>(); Dictionary <string, int> allIDs = new Dictionary <string, int>(); using (HttpClient client = CreateClient(REDIRECT | AUTH)) { string url = String.Format("https://utah.instructure.com/api/v1/courses/{0}/search_users?enrollment_type[]=student&per_page=200", Auth.COURSE_ID); while (url != null) { HttpResponseMessage response = client.GetAsync(url).Result; if (response.IsSuccessStatusCode) { dynamic resp = JArray.Parse(response.Content.ReadAsStringAsync().Result); foreach (dynamic user in resp) { allStudents[user.sis_user_id.ToString()] = user.name.ToString(); allIDs[user.sis_user_id.ToString()] = (int)user.id; } url = GetNextURL(response.Headers); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine("Unable to read users"); return; } } Console.WriteLine("Student count: " + allStudents.Count); } Console.WriteLine(); Console.WriteLine((compareGrades) ? "Using only students whose grades have changed" : "Using all students"); Console.WriteLine((recordGrades) ? "Recording grades" : "Not recording grades"); Console.Write("Do you want to continue? [y/n] "); String yesno = Console.ReadLine(); if (yesno != "y") { return; } Console.WriteLine(); Gradebook gradebook = new Gradebook(exported); foreach (string unid in allStudents.Keys) { // Get due and lock dates for this student. They can differ because of extensions. DateTime dueDate; DateTime lockDate; getDueAndLockDate(allIDs[unid], overrides, out dueDate, out lockDate); Grade g = gradebook.GetGrade(unid, problemName, problem, dueDate, lockDate); string compareResult = ""; if (compareGrades) { using (HttpClient client = CreateClient(REDIRECT | AUTH)) { String url = String.Format("https://utah.instructure.com/api/v1/courses/{0}/assignments/{1}/submissions/sis_user_id:{2}", Auth.COURSE_ID, assignID, unid); HttpResponseMessage response = client.GetAsync(url).Result; int oldGrade = -1; if (response.IsSuccessStatusCode) { dynamic resp = JObject.Parse(response.Content.ReadAsStringAsync().Result); string og = resp.score.ToString(); if (og == null || og == "") { oldGrade = -1; } else { oldGrade = Convert.ToInt32(resp.score.ToString()); } } if (oldGrade == -1) { compareResult = "No old grade"; } else if (g.Score != oldGrade) { compareResult = "Old grade " + oldGrade; } else { continue; } } } Console.WriteLine(); Console.WriteLine(allStudents[unid] + " " + unid); if (g.DueDate != problem.DueDate || g.LockDate != problem.LockDate) { Console.WriteLine(" " + "Due " + g.DueDate + " " + "Locked " + g.LockDate); Console.WriteLine(" " + "Due " + problem.DueDate + " " + "Locked " + problem.LockDate); } if (problem.SkipUsers.Contains(unid)) { Console.WriteLine(" Skipping"); continue; } Console.Write(" Passed: " + g.PublicTestsPassed + "/" + problem.PublicTests + " " + g.PrivateTestsPassed + "/" + problem.PrivateTests); Console.Write(" Grade: " + g.Score); if (compareGrades) { Console.Write(" (" + compareResult + ")"); } if (g.ExtraDaysUsed > 0) { Console.Write(" Extra: " + g.ExtraDaysUsed); } Console.WriteLine(); //Console.WriteLine(" " + g.Comment); if (recordGrades) { using (HttpClient client = CreateClient(REDIRECT | AUTH)) { string data = "submission[posted_grade]=" + g.Score; data += "&comment[text_comment]=" + Uri.EscapeDataString(g.Comment); string url = String.Format("https://utah.instructure.com/api/v1/courses/{0}/assignments/{1}/submissions/sis_user_id:{2}", Auth.COURSE_ID, assignID, unid); StringContent content = new StringContent(data, Encoding.UTF8, "application/x-www-form-urlencoded"); HttpResponseMessage response = client.PutAsync(url, content).Result; if (response.IsSuccessStatusCode) { Console.WriteLine("Success: " + allStudents[unid] + " " + unid); } else { Console.WriteLine("Error: " + response.StatusCode); Console.WriteLine(response.ReasonPhrase.ToString()); Console.WriteLine(allStudents[unid] + " " + unid); Console.WriteLine(); } } } } }