Example #1
0
        /// <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);
            }
        }
Example #2
0
        /// <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);
            }
        }
Example #3
0
        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);
            }
        }
Example #4
0
        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();
                }
            }
        }
Example #5
0
        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();
                    }
                }
            }
        }
Example #6
0
 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();
         }
     }
 }
Example #7
0
        /// <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;
                }
            }
        }
Example #8
0
        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();
                    }
                }
            }
        }
Example #10
0
        /// <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();
                        }
                    }
                }
            }
        }