Reformat everything
[nci.git] / nci-get-assignment-grades.c
blob5d94fec996f1aabcf4c02b1474f35a67b7cfe801
1 /*
2 * Copyright (c) 2016-2018 S. Gilles <sgilles@math.umd.edu>
4 * Permission to use, copy, modify, and/or distribute this software
5 * for any purpose with or without fee is hereby granted, provided
6 * that the above copyright notice and this permission notice appear
7 * in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
13 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
14 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <errno.h>
19 #include <locale.h>
20 #include <stdint.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <strings.h>
25 #include <unistd.h>
27 #include <curl/curl.h>
28 #include <yajl_parse.h>
30 #include "macros.h"
31 #include "util.h"
33 /* Write output to stdout */
34 static void
35 write_out_everything(char *course_id, map *students, map **grades, map *totals,
36 map *partials, size_t assignments_num,
37 char **assignment_ids, map *assignments)
39 size_t j;
40 size_t k;
41 char **student_names = 0;
42 char *name;
43 char **id;
44 char **score;
45 char **res;
46 size_t students_num;
48 map_get_keys(students, &student_names, &students_num);
49 printf("\"Name\",\"ID\"");
51 for (j = 0; j < assignments_num; ++j) {
52 printf(",\"%s:%s (", course_id, assignment_ids[j]);
53 res = map_get(assignments, assignment_ids[j]);
55 if (res &&
56 res[0]) {
57 print_esc_0x22(res[0]);
60 printf(")\"");
63 if (totals) {
64 printf(",\"Total\",\"Letter Grade\"");
67 if (partials) {
68 printf(",\"Partial\",\"Partial Letter Grade\"");
71 printf("\n");
73 for (j = 0; j < students_num; ++j) {
74 name = student_names[j];
75 id = map_get(students, name);
77 if (!id ||
78 !id[0]) {
79 continue;
82 printf("\"");
83 print_esc_0x22(name);
84 printf("\",\"%s\"", id[0]);
86 for (k = 0; k < assignments_num; ++k) {
87 score = map_get(&((*grades)[k]), id[0]);
88 printf(",\"");
90 if (score &&
91 score[1] &&
92 !strcasecmp(score[1], "true")) {
93 print_esc_0x22("EX");
94 } else if (score &&
95 score[0]) {
96 print_esc_0x22(score[0]);
99 printf("\"");
102 if (totals) {
103 score = map_get(totals, id[0]);
104 printf(",\"");
106 if (score &&
107 score[0]) {
108 print_esc_0x22(score[0]);
111 printf(",\"");
113 if (score &&
114 score[1]) {
115 print_esc_0x22(score[1]);
118 printf("\"");
121 if (partials) {
122 score = map_get(partials, id[0]);
123 printf(",\"");
125 if (score &&
126 score[0] &&
127 score[2]) {
128 print_esc_0x22(score[2]);
131 printf("\",\"");
133 if (score &&
134 score[1] &&
135 score[3]) {
136 print_esc_0x22(score[3]);
139 printf("\"");
142 printf("\n");
145 free(student_names);
149 main(int argc, char **argv)
151 int ret = EINVAL;
152 char *url_base = 0;
153 char *auth_token = 0;
154 char *course_id = 0;
155 const char *fn_students[] = { "sortable_name", "id", 0 };
156 const char *fn_scores[] = { "user_id", "score", "excused", 0 };
157 const char *fn_totals[] = { "user_id", "final_score", "final_grade",
158 "current_score", "current_grade", 0 };
159 const char *fn_assignments[] = { "id", "name", 0 };
160 uint_fast8_t fetch_all_arg = 0;
161 uint_fast8_t totals_arg = 0;
162 uint_fast8_t partials_arg = 0;
163 char **assignment_ids = 0;
164 map students = { 0 };
165 map *grades = 0;
166 map aggregates = { 0 };
167 map assignments = { 0 };
168 size_t j = 0;
169 size_t assignments_num = 0;
170 size_t len = 0;
171 char *built_uri = 0;
172 int opt = 0;
174 setlocale(LC_ALL, "");
176 if (!(assignment_ids = calloc((argc / 2), sizeof *assignment_ids))) {
177 ret = errno;
178 perror(L("calloc"));
179 goto cleanup;
182 while ((opt = getopt(argc, argv, "c:a:Atp")) != -1) {
183 switch (opt) {
184 case 'c':
185 course_id = optarg;
186 break;
187 case 'a':
188 assignment_ids[assignments_num++] = optarg;
189 break;
190 case 'A':
191 fetch_all_arg = 1;
192 break;
193 case 't':
194 totals_arg = 1;
195 break;
196 case 'p':
197 partials_arg = 1;
198 break;
199 default:
200 break;
204 if (fetch_all_arg &&
205 assignments_num) {
206 fprintf(stderr, "both -A and -a provided, ignoring -a\n");
209 if (!course_id) {
210 ret = EINVAL;
211 fprintf(stderr, "course-id is mandatory\n");
212 goto cleanup;
215 if (!fetch_all_arg &&
216 !assignments_num &&
217 !totals_arg &&
218 !partials_arg) {
219 ret = EINVAL;
220 fprintf(stderr, "one of -a, -A, -t, -p is mandatory\n");
221 goto cleanup;
224 if (!(grades = calloc(assignments_num, sizeof *grades))) {
225 ret = errno;
226 perror(L("calloc"));
227 goto cleanup;
230 curl_global_init(CURL_GLOBAL_DEFAULT);
232 if (!(url_base = get_url_base())) {
233 /* Error should have already been printed */
234 ret = ENOENT;
235 goto cleanup;
238 if (!(auth_token = get_auth_token())) {
239 /* Error should have already been printed */
240 ret = ENOENT;
241 goto cleanup;
244 /* Get a list of all students, so we can find out their names */
245 len = snprintf(0, 0, "%s/api/v1/courses/%s/students?per_page=9999",
246 url_base, course_id);
248 if (len + 1 < len) {
249 ret = errno = EOVERFLOW;
250 perror(L(""));
251 goto cleanup;
254 if (!(built_uri = malloc(len + 1))) {
255 ret = errno;
256 perror(L("malloc"));
257 goto cleanup;
260 sprintf(built_uri, "%s/api/v1/courses/%s/students?per_page=9999",
261 url_base, course_id);
263 if ((ret = key_value_extract(built_uri, auth_token, fn_students, 0,
264 &students))) {
265 goto cleanup;
268 /* Get a list of all assignments, so we can find out their names */
269 free(built_uri);
270 len = snprintf(0, 0, "%s/api/v1/courses/%s/assignments?per_page=9999",
271 url_base, course_id);
273 if (len + 1 < len) {
274 ret = errno = EOVERFLOW;
275 perror(L(""));
276 goto cleanup;
279 if (!(built_uri = malloc(len + 1))) {
280 ret = errno;
281 perror(L("malloc"));
282 goto cleanup;
285 sprintf(built_uri, "%s/api/v1/courses/%s/assignments?per_page=9999",
286 url_base, course_id);
288 if ((ret = key_value_extract(built_uri, auth_token, fn_assignments, 0,
289 &assignments))) {
290 goto cleanup;
293 if (fetch_all_arg) {
294 assignments_num = 0;
295 free(assignment_ids);
296 assignment_ids = 0;
297 free(grades);
298 grades = 0;
299 map_get_keys(&assignments, &assignment_ids, &assignments_num);
301 if (!assignments_num) {
302 ret = ENOMEM;
303 perror(L("map_get_keys"));
304 goto cleanup;
307 if (!(grades = calloc(assignments_num, sizeof *grades))) {
308 ret = errno;
309 perror(L("calloc"));
310 goto cleanup;
314 for (j = 0; j < assignments_num; ++j) {
315 free(built_uri);
316 len = snprintf(0, 0, "%s/api/v1/courses/%s/assignments/%s/"
317 "submissions?per_page=9999", url_base,
318 course_id,
319 assignment_ids[j]);
321 if (len + 1 < len) {
322 ret = errno = EOVERFLOW;
323 perror(L(""));
324 goto cleanup;
327 if (!(built_uri = malloc(len + 1))) {
328 ret = errno;
329 perror(L("malloc"));
330 goto cleanup;
333 sprintf(built_uri, "%s/api/v1/courses/%s/assignments/%s/"
334 "submissions?per_page=9999", url_base,
335 course_id,
336 assignment_ids[j]);
338 if ((ret = key_value_extract(built_uri, auth_token, fn_scores,
339 0, &(grades[j])))) {
340 goto cleanup;
344 if (totals_arg ||
345 partials_arg) {
346 free(built_uri);
347 len = snprintf(0, 0,
348 "%s/api/v1/courses/%s/enrollments?per_page=9999",
349 url_base,
350 course_id);
352 if (len + 1 < len) {
353 ret = errno = EOVERFLOW;
354 perror(L(""));
355 goto cleanup;
358 if (!(built_uri = malloc(len + 1))) {
359 ret = errno;
360 perror(L("malloc"));
361 goto cleanup;
364 sprintf(built_uri,
365 "%s/api/v1/courses/%s/enrollments?per_page=9999",
366 url_base,
367 course_id);
369 if ((ret = key_value_extract(built_uri, auth_token, fn_totals,
370 0, &aggregates))) {
371 goto cleanup;
375 write_out_everything(course_id, &students, &grades, (totals_arg ?
376 &aggregates : 0),
377 (partials_arg ? &aggregates : 0), assignments_num,
378 assignment_ids, &assignments);
379 ret = 0;
380 cleanup:
381 free(built_uri);
382 free(url_base);
383 free(auth_token);
384 curl_global_cleanup();
385 free(assignment_ids);
386 map_clean(&students);
387 map_clean(&assignments);
388 map_clean(&aggregates);
390 if (assignments_num) {
391 do {
392 map_clean(&grades[--assignments_num]);
393 } while (assignments_num);
396 free(grades);
398 return ret;