Update copyright dates
[nci.git] / nci-list-assignments.c
blob90746c65b093ee02eddfc749bd2f8dea06b15c22
1 /*
2 * Copyright (c) 2016-2017 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 <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
26 #include <curl/curl.h>
27 #include <yajl_parse.h>
29 #include "macros.h"
30 #include "util.h"
32 enum {
33 FN_AG_POSITION = 0,
34 FN_AG_NAME = 1,
35 FN_AG_ID = 2,
36 FN_AG_WEIGHT = 3,
37 FN_AG_DROP_LOWEST = 4,
38 FN_AG_DROP_HIGHEST = 5,
39 FN_AG_END = 6,
42 enum {
43 FN_A_ID = 0,
44 FN_A_GROUP_ID = 1,
45 FN_A_POSITION = 2,
46 FN_A_NAME = 3,
47 FN_A_MAX_POINTS = 4,
48 FN_A_DUE_AT = 5,
49 FN_A_END = 6
52 static int cmp_entry_by_pos(const void *a, const void *b)
54 const entry *ea = (const entry *) a;
55 const entry *eb = (const entry *) b;
56 const char *sa = ea->vs[FN_A_POSITION - 1];
57 const char *sb = eb->vs[FN_A_POSITION - 1];
58 long long la = strtoll(sa, 0, 0);
59 long long lb = strtoll(sb, 0, 0);
61 return (la < lb) ? -1 : ((la > lb) ? 1 : 0);
64 static int write_out_everything(map *assigns, map *agroups)
66 int ret = EINVAL;
67 size_t j = 0;
68 size_t k = 0;
69 char **agroups_positions = 0;
70 size_t agroups_num = 0;
71 char **ag = 0;
72 char **a_ids = 0;
73 char **unprinted_a_ids = 0;
74 size_t as_num = 0;
75 char **a = 0;
76 entry *ca = 0;
77 size_t ca_num = 0;
78 uint_fast8_t some_categorized = 0;
79 uint_fast8_t printed_uncategorized = 0;
81 map_get_keys(agroups, &agroups_positions, &agroups_num);
82 map_get_keys(assigns, &a_ids, &as_num);
84 if (!(unprinted_a_ids = calloc(as_num, sizeof *unprinted_a_ids))) {
85 ret = errno;
86 perror(L("calloc"));
87 goto cleanup;
90 if (!(ca = calloc(as_num, sizeof *ca))) {
91 ret = errno;
92 perror(L("calloc"));
93 goto cleanup;
96 memcpy(unprinted_a_ids, a_ids, as_num * sizeof unprinted_a_ids);
98 for (j = 0; j < agroups_num; ++j) {
99 ag = map_get(agroups, agroups_positions[j]);
101 if (!ag) {
102 continue;
105 printf("%s %s", UBSAFES(ag[FN_AG_ID - 1]), UBSAFES(
106 ag[FN_AG_NAME - 1]));
108 if (ag[FN_AG_WEIGHT - 1]) {
109 printf(" (%s%%)", ag[FN_AG_WEIGHT - 1]);
112 if (ag[FN_AG_DROP_LOWEST - 1] &&
113 strcmp(ag[FN_AG_DROP_LOWEST - 1], "0")) {
114 printf(" (Drop %s lowest)", ag[FN_AG_DROP_LOWEST - 1]);
117 if (ag[FN_AG_DROP_HIGHEST - 1] &&
118 strcmp(ag[FN_AG_DROP_HIGHEST - 1], "0")) {
119 printf(" (Drop %s highest)\n", ag[FN_AG_DROP_HIGHEST -
120 1]);
121 } else {
122 printf("\n");
125 ca_num = 0;
127 for (k = 0; k < as_num; ++k) {
128 a = map_get(assigns, a_ids[k]);
130 if (!strcmp(ag[FN_AG_ID - 1], a[FN_A_GROUP_ID - 1])) {
131 unprinted_a_ids[k] = 0;
132 ca[ca_num] = (entry) { .k = a_ids[k], .vs = a };
133 ca_num++;
137 qsort(ca, ca_num, sizeof *ca, cmp_entry_by_pos);
139 for (k = 0; k < ca_num; ++k) {
140 some_categorized = 1;
141 printf(" %s %s", UBSAFES(ca[k].k), UBSAFES(
142 ca[k].vs[FN_A_NAME - 1]));
144 if (ca[k].vs[FN_A_MAX_POINTS - 1]) {
145 printf(" (%s)", ca[k].vs[FN_A_MAX_POINTS - 1]);
148 if (ca[k].vs[FN_A_DUE_AT - 1]) {
149 printf(" due at %s\n", ca[k].vs[FN_A_DUE_AT -
150 1]);
151 } else {
152 printf("\n");
157 for (k = 0; k < as_num; ++k) {
158 if (!unprinted_a_ids[k]) {
159 continue;
162 if (some_categorized &&
163 !printed_uncategorized) {
164 printf("UNCATEGORIZED\n");
165 printed_uncategorized = 1;
168 if (some_categorized) {
169 printf(" ");
172 a = map_get(assigns, unprinted_a_ids[k]);
173 printf("%s %s", unprinted_a_ids[k], a[FN_A_NAME - 1]);
175 if (a[FN_A_MAX_POINTS - 1]) {
176 printf(" (%s)\n", a[FN_A_MAX_POINTS - 1]);
177 } else {
178 printf("\n");
182 ret = 0;
183 cleanup:
184 free(agroups_positions);
185 free(a_ids);
186 free(unprinted_a_ids);
187 free(ca);
189 return ret;
192 int main(int argc, char **argv)
194 int ret = EINVAL;
195 char *url_base = 0;
196 char *auth_token = 0;
197 char *course_id = 0;
198 const char *fn_agroups[] = {
199 /* */
200 [FN_AG_POSITION] = "position", /* */
201 [FN_AG_NAME] = "name", /* */
202 [FN_AG_ID] = "id", /* */
203 [FN_AG_WEIGHT] = "group_weight", /* */
204 [FN_AG_DROP_LOWEST] = "drop_lowest", /* */
205 [FN_AG_DROP_HIGHEST] = "drop_highest", /* */
206 [FN_AG_END] = 0 /* */
208 const char *fn_assigns[] = {
209 /* */
210 [FN_A_ID] = "id", /* */
211 [FN_A_GROUP_ID] = "assignment_group_id", /* */
212 [FN_A_POSITION] = "position", /* */
213 [FN_A_NAME] = "name", /* */
214 [FN_A_MAX_POINTS] = "points_possible", /* */
215 [FN_A_DUE_AT] = "due_at", /* */
216 [FN_A_END] = 0 /* */
218 map agroups = { 0 };
219 map assigns = { 0 };
220 size_t len = 0;
221 char *built_uri = 0;
222 int opt;
224 setlocale(LC_ALL, "");
226 while ((opt = getopt(argc, argv, "c:")) != -1) {
227 switch (opt) {
228 case 'c':
229 course_id = optarg;
230 break;
231 default:
232 break;
236 if (!course_id) {
237 ret = EINVAL;
238 fprintf(stderr, "course-id is mandatory\n");
239 goto cleanup;
242 curl_global_init(CURL_GLOBAL_DEFAULT);
244 if (!(url_base = get_url_base())) {
245 ret = ENOENT;
247 /* Error should have already been printed */
248 goto cleanup;
251 if (!(auth_token = get_auth_token())) {
252 ret = ENOENT;
254 /* Error should have already been printed */
255 goto cleanup;
258 len = snprintf(0, 0,
259 "%s/api/v1/courses/%s/assignment_groups?per_page=9999",
260 url_base,
261 course_id);
263 if (len + 1 < len) {
264 ret = errno = EOVERFLOW;
265 perror(L(""));
266 goto cleanup;
269 if (!(built_uri = malloc(len + 1))) {
270 ret = errno;
271 perror(L("malloc"));
272 goto cleanup;
275 sprintf(built_uri,
276 "%s/api/v1/courses/%s/assignment_groups?per_page=9999",
277 url_base,
278 course_id);
280 if ((ret = key_value_extract(built_uri, auth_token, fn_agroups, 0,
281 &agroups))) {
282 goto cleanup;
285 free(built_uri);
286 built_uri = 0;
287 len = snprintf(0, 0, "%s/api/v1/courses/%s/assignments?per_page=9999",
288 url_base, course_id);
290 if (len + 1 < len) {
291 ret = errno = EOVERFLOW;
292 perror(L(""));
293 goto cleanup;
296 if (!(built_uri = malloc(len + 1))) {
297 ret = errno;
298 perror(L("malloc"));
299 goto cleanup;
302 sprintf(built_uri, "%s/api/v1/courses/%s/assignments?per_page=9999",
303 url_base, course_id);
305 if ((ret = key_value_extract(built_uri, auth_token, fn_assigns, 0,
306 &assigns))) {
307 goto cleanup;
310 if ((ret = write_out_everything(&assigns, &agroups))) {
311 goto cleanup;
314 ret = 0;
315 cleanup:
316 free(built_uri);
317 map_clean(&agroups);
318 map_clean(&assigns);
319 free(url_base);
320 free(auth_token);
321 curl_global_cleanup();
323 return ret;