コード例 #1
0
        public void ThenICanSeeTheUserSCreditScore()
        {
            AppHome Ahp = new AppHome(driver);

            Ahp.MobileCreditscoreDisplays();
            driver.Quit();
        }
コード例 #2
0
        public static void Main()
        {
            var home = new AppHome("bulk_file_delete");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"                  }
                            }
                        },
                        new TableSyntax("data")
                        {
                            Items =
                            {
                                { "map_file",  "RELATIVE_MAP_FILE_PATH_HERE"  },
                                { "id_is_sis", true                           }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and map info.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var  data        = config.GetTable("data");
            var  mapFileName = data.Get <string>("map_file");
            bool idIsSis     = data.GetOr("id_is_sis", true);

            string mapFilePath = Path.Combine(home.NsDir, mapFileName);

            Console.WriteLine($"Sourcing map from {mapFilePath}");

            var startedAt = DateTime.Now;

            // ------------------------------------------------------------------------

            var list = File.ReadAllLines(mapFilePath).ToList();

            List <string>[] taskLists = list.Chunk(Math.Min(Math.Max(list.Count / 7, 2), list.Count - 1))
                                        .ToArray();

            int nThreads = taskLists.Length;

            var apis = new Api[nThreads];

            for (var i = 0; i < nThreads; i++)
            {
                apis[i] = new Api(token, "https://uview.instructure.com/api/v1/");
            }

            Console.WriteLine($"Using {nThreads} threads.");

            Console.WriteLine(idIsSis ? "Interpeting userkey as SIS ID."
                                      : "Interpreting userkey as CANVAS ID.");

            var completed    = new ConcurrentBag <string>();
            var userNotFound = new ConcurrentBag <string>();
            var fileNotFound = new ConcurrentBag <string>();
            var error        = new ConcurrentBag <string>();
            var keys         = new ConcurrentBag <string>();

            using (var countdown = new CountdownEvent(nThreads)) {
                for (var i = 0; i < nThreads; i++)
                {
                    ThreadPool.QueueUserWorkItem(async o => {
                        try {
                            var n = (int)o;
                            foreach (string line in taskLists[n])
                            {
                                string[] halves = line.Split(',');
                                Debug.Assert(halves.Length == 2);

                                var(userKey, userFile) = (halves[0], halves[1]);
                                keys.Add(userKey);

                                var api = apis[n];

                                try {
                                    var user = idIsSis switch {
                                        true => await api.GetUserBySis(userKey),
                                        _ => await api.GetUser(ulong.Parse(userKey))
                                    };

                                    if (user == null)
                                    {
                                        Console.WriteLine($"WARN: Couldn't find the user for userkey {userKey} !!");
                                        userNotFound.Add(userKey);
                                        continue;
                                    }

                                    Console.WriteLine($"Preparing to delete filename(s) {userFile} from userkey " +
                                                      $"{userKey}, Id {user.Id}, SIS {user.SisUserId}");

                                    api.MasqueradeAs(user.Id);

                                    var anyDeleted = false;
                                    await foreach (var file in api.StreamPersonalFiles(searchTerm: userFile))
                                    {
                                        if (file.Filename == userFile)
                                        {
                                            var deleted = await api.DeleteFile(file.Id, false);
                                            Console.WriteLine($"Deleted {userFile}, id {deleted.Id}, from userkey {userKey}!");
                                            anyDeleted = true;
                                        }
                                    }

                                    if (anyDeleted)
                                    {
                                        completed.Add(userKey);
                                    }
                                    else
                                    {
                                        fileNotFound.Add(userKey);
                                        Console.WriteLine($"Userkey {userKey} has no {userFile} to delete.");
                                    }
                                } catch (Exception e) {
                                    Console.WriteLine($"Caught an exception during upload for userkey {userKey}: {e}");
                                    error.Add(userKey);
                                } finally {
                                    api.StopMasquerading();
                                }
                            }
                        } finally {
                            // ReSharper disable once AccessToDisposedClosure
                            countdown.Signal();
                        }
                    }, i);
                }
                countdown.Wait();
            }

            var completedList               = completed.Concat(fileNotFound).ToList();
            var completedWithDeletionIds    = completed.Distinct().ToList();
            var completedWithoutDeletionIds = fileNotFound.Distinct().ToList();
            var errorIds        = error.Distinct().ToList();
            var userNotFoundIds = userNotFound.Distinct().ToList();

            Console.WriteLine($"{completedList.Count} out of {list.Count} operations were completed.");

            if (errorIds.Any())
            {
                Console.WriteLine($"Operation failed for the following userkeys: {errorIds.ToPrettyString()}");
            }

            if (userNotFoundIds.Any())
            {
                Console.WriteLine($"The following userkeys could not be resolved: {userNotFoundIds.ToPrettyString()}");
            }

            if (completedWithoutDeletionIds.Any())
            {
                Console.WriteLine($"The following userkeys were missing at least one file (usually not an error): {completedWithoutDeletionIds.ToPrettyString()}");
            }

            var document = new JObject {
                ["dateStarted"]              = startedAt.ToIso8601Date(),
                ["dateCompleted"]            = DateTime.Now.ToIso8601Date(),
                ["completedWithDeletion"]    = new JArray(completedWithDeletionIds),
                ["completedWithoutDeletion"] = new JArray(completedWithoutDeletionIds),
                ["error"]        = new JArray(errorIds),
                ["userNotFound"] = new JArray(userNotFoundIds)
            };

            var outPath = Path.Combine(home.NsDir, $"BulkFileDelete_Log_{startedAt.Ticks}.json");

            File.WriteAllText(outPath, document.ToString(Formatting.Indented) + "\n");
            Console.WriteLine($"Wrote log to {outPath}");
        }
コード例 #3
0
        public void WhenIClickOnDeals()
        {
            AppHome Ahp = new AppHome(driver);

            Ahp.ClickOffers();
        }
コード例 #4
0
        public void WhenIClickOnReports()
        {
            AppHome Ahp = new AppHome(driver);

            Ahp.ClickReport();
        }
コード例 #5
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("export_attendance_columns");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"          }
                            }
                        },
                        new TableSyntax("options")
                        {
                            Items =
                            {
                                { "include_hidden", false            }
                            }
                        },
                        new TableSyntax("debug")
                        {
                            Items =
                            {
                                { "limit_to",                     -1 }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and input path.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var includeHidden = config.GetTable("options")
                                .Get <bool>("include_hidden");

            var courseLimit = config.GetTable("debug")
                              .Get <long>("limit_to");

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            if (courseLimit > 0)
            {
                Console.WriteLine($"[DEBUG] Limited to course id {courseLimit}");
            }

            var coursesObj = new JObject();

            var document = new JObject {
                ["metadata"] = new JObject {
                    ["includeHidden"] = includeHidden,
                    ["started"]       = DateTime.Now.ToIso8601Date()
                },
                ["courses"] = coursesObj
            };

            try {
                var courses = courseLimit <= 0 ? api.StreamCourses()
                                               : AsyncEnumerable.Repeat(await api.GetCourse(Convert.ToUInt64(courseLimit)), 1);

                await foreach (var course in courses)
                {
                    try {
                        var columnsObj = new JObject();
                        await foreach (var col in api.StreamCustomGradebookColumns(course.Id, includeHidden))
                        {
                            var thisColObj = new JObject();
                            await foreach (var datum in api.StreamColumnEntries(col.Id, course.Id))
                            {
                                thisColObj[datum.UserId.ToString()] = datum.Content;
                            }
                            columnsObj[col.Title] = thisColObj;
                        }
                        coursesObj[course.Id.ToString()] = columnsObj;
                    } catch (Exception e) {
                        Console.WriteLine($"Threw up during course {course.Id}:\n{e}\nContinuing onwards.");
                    }
                }

                var now     = DateTime.Now;
                var outPath = Path.Combine(home.NsDir, $"AttendanceColumns_{now.Year}_{now.Month}_{now.Day}.json");
                File.WriteAllText(outPath, document.ToString(Formatting.Indented));
                Console.WriteLine($"Wrote report to {outPath}");
            } catch (Exception e) {
                Console.WriteLine($"Threw up:\n{e}");
            }
        }
コード例 #6
0
        public static async Task Main(string[] args)
        {
            var home = new AppHome("discover_ungraded_assignments");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"  }
                            }
                        },
                        new TableSyntax("input")
                        {
                            Items =
                            {
                                { "user",                   -1 },
                                { "ignore", new int[] {        } }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and input path.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var input = config.GetTable("input");

            var userId = Convert.ToUInt64(input.Get <long>("user"));

            var ignoreIds = input.MaybeGet <TomlArray>("ignore")
                            ?.Select(Convert.ToUInt64)
                            .ToHashSet() ?? new HashSet <ulong>();

            var sectionFilters = input.MaybeGet <TomlTableArray>("section_filters")
                                 ?.ToLookup(table => Convert.ToUInt64(table.Get <long>("course")),
                                            table => table.Get <string>("section_name"));

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            try {
                var enrollments = api.StreamUserEnrollments(userId, new[] { TeacherEnrollment });

                await foreach (var taughtCourse in enrollments)
                {
                    if (ignoreIds.Contains(taughtCourse.CourseId))
                    {
                        continue;
                    }

                    IEnumerable <Section> sections = null;
                    if (sectionFilters != null && sectionFilters.Contains(taughtCourse.CourseId))
                    {
                        sections = await api.StreamCourseSections(taughtCourse.CourseId)
                                   .Where(s => sectionFilters[taughtCourse.CourseId].Any(n => n == s.Name))
                                   .ToListAsync();
                    }

                    await foreach (var assignment in api.StreamCourseAssignments(taughtCourse.CourseId,
                                                                                 Submission | AllDates))
                    {
                        await foreach (var submission in api.StreamSubmissionVersions(taughtCourse.CourseId,
                                                                                      assignment.Id))
                        {
                            if (sections != null)
                            {
                                // need to make sure we want this student
                                var ok = await api.StreamUserEnrollments(submission.UserId)
                                         .Where(e => e.CourseId == taughtCourse.CourseId)
                                         .AnyAsync(e => e.CourseSectionId != null && sections.Any(s => s.Id == e.CourseSectionId));

                                if (!ok)
                                {
                                    continue;
                                }
                            }

                            if (submission.Score == null)
                            {
                                Console.WriteLine($"Ungraded Assignment: {assignment.Name} ({assignment.Id}) submitted by user {submission.UserId} " +
                                                  $"in course {taughtCourse.CourseId}.");
                            }
                        }
                    }
                }
            } catch (Exception e) {
                Console.WriteLine($"Exception:\n{e}");
            }
        }
コード例 #7
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("manage_attendance_columns_csv");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"    }
                            }
                        },
                        new TableSyntax("debug")
                        {
                            Items =
                            {
                                { "limit_to",               -1 }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and input path.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var courseLimit = config.GetTable("debug")
                              .Get <long>("limit_to");

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            if (courseLimit > 0)
            {
                Console.WriteLine($"[DEBUG] Limited to course id {courseLimit}");
            }

            try {
                var courses = courseLimit <= 0 ? api.StreamCourses()
                                               : AsyncEnumerable.Repeat(await api.GetCourse(Convert.ToUInt64(courseLimit)), 1);

                await foreach (var course in courses)
                {
                    try {
                        await foreach (var col in api.StreamCustomGradebookColumns(course.Id, true)
                                       .Where(col => col.Title.Contains("Attendance_")))
                        {
                            var unhidden = await api.UpdateCustomColumn(col.Id, course.Id, hidden : false);

                            Console.WriteLine($"Course {course.Id} - Unhid {unhidden.Title}");
                        }
                    } catch (Exception e) {
                        Console.WriteLine($"Threw up during course {course.Id}:\n{e}\nContinuing onwards.");
                    }
                }
            } catch (Exception e) {
                Console.WriteLine($"Threw up:\n{e}");
            }
        }
コード例 #8
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("export_attendance_columns_csv");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"          }
                            }
                        },
                        new TableSyntax("options")
                        {
                            Items =
                            {
                                { "include_hidden", false            }
                            }
                        },
                        new TableSyntax("debug")
                        {
                            Items =
                            {
                                { "limit_to",                     -1 }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and input path.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var includeHidden = config.GetTable("options")
                                .Get <bool>("include_hidden");

            var courseLimit = config.GetTable("debug")
                              .Get <long>("limit_to");

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            if (courseLimit > 0)
            {
                Console.WriteLine($"[DEBUG] Limited to course id {courseLimit}");
            }

            var table = new StringBuilder("course_id,column_name,student_id,enrollment_id,column_value\n");

            try {
                var courses = courseLimit <= 0 ? api.StreamCourses()
                                               : (await api.GetCourse(Convert.ToUInt64(courseLimit))).YieldAsync();

                await foreach (var course in courses)
                {
                    try {
                        var enrollments = api.StreamCourseEnrollments(course.Id, StudentEnrollment.Yield())
                                          .ToDictionaryAsync(e => e.UserId);
                        await foreach (var col in api.StreamCustomGradebookColumns(course.Id, includeHidden))
                        {
                            await foreach (var datum in api.StreamColumnEntries(col.Id, course.Id))
                            {
                                (await enrollments).TryGetValue(datum.UserId, out var thisEnrollment);
                                table.Append($"{course.Id}," +
                                             $"\"{col.Title}\"," +
                                             $"{datum.UserId}," +
                                             $"{thisEnrollment?.Id.ToString() ?? "NULL"}" +
                                             $",\"{datum.Content}\"\n");
                            }
                        }
                    } catch (Exception e) {
                        Console.WriteLine($"Threw up during course {course.Id}:\n{e}\nContinuing onwards.");
                    }
                }

                var now     = DateTime.Now;
                var outPath = Path.Combine(home.NsDir, $"AttendanceColumns_{now.Year}_{now.Month}_{now.Day}.csv");
                File.WriteAllText(outPath, table.ToString());
                Console.WriteLine($"Wrote report to {outPath}");
            } catch (Exception e) {
                Console.WriteLine($"Threw up:\n{e}");
            }
        }
コード例 #9
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("uva_quota_purger");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"             }
                            }
                        },
                        new TableSyntax("options")
                        {
                            Items =
                            {
                                { "destroy",      false                      },
                                { "send_message", false                      }
                            }
                        },
                        new TableSyntax("input")
                        {
                            Items =
                            {
                                { "input_file", "PUT_INPUT_FILE_HERE"   }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and input file.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens").Get <string>("token");

            var options       = config.GetTable("options");
            var destroy       = options.GetOr <bool>("destroy");
            var sendMessage   = destroy && options.GetOr <bool>("send_message");
            var inputFilePath = config.GetTable("input").Get <string>("input_file");

            if (!File.Exists(inputFilePath))
            {
                if (File.Exists(Path.Combine(home.NsDir, inputFilePath)))
                {
                    inputFilePath = Path.Combine(home.NsDir, inputFilePath);
                }
                else
                {
                    Console.WriteLine($"The input file path {inputFilePath} was not found.");
                    return;
                }
            }

            var input         = JObject.Parse(File.ReadAllText(inputFilePath));
            var detectedUsers = input["detectedUsers"].ToObject <Dictionary <string, JObject> >()
                                .KeySelect(ulong.Parse)
                                .ValSelect(v => new {
                userSis      = v["userSis"],
                userFullName = v["userFullName"]
            });

            Console.WriteLine($"Loaded input file {inputFilePath} with {input["usersInLog"]} entries.");

            Console.WriteLine($"Being destructive? {(destroy ? "YES" : "NO")}");
            Console.WriteLine($"Sending message? {(sendMessage ? "YES" : "NO")}");

            // --------------------------------------------------------------------

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            var started = DateTime.Now;

            var resolvedUsers         = new JObject();
            var actionTakenUsers      = new JObject();
            var actionFailedUsers     = new JObject();
            var inspectionFailedUsers = new JObject();
            var actionLimitedUsers    = new JObject();

            var document = new JObject {
                ["resolvedUsers"]         = resolvedUsers,
                ["actionTakenUsers"]      = actionTakenUsers,
                ["actionLimitedUsers"]    = actionLimitedUsers,
                ["actionFailedUsers"]     = actionFailedUsers,
                ["inspectionFailedUsers"] = inspectionFailedUsers,
                ["dateStarted"]           = started.ToIso8601Date(),
                ["sentMessages"]          = sendMessage,
                ["deletedFiles"]          = destroy
            };

            foreach (var(userId, userData) in detectedUsers)
            {
                try {
                    var isTeacher = await await api.GetUser(userId)
                                    .ThenApply(u => u.IsTeacher());

                    api.MasqueradeAs(userId);
                    var(quota, used) = await api.GetPersonalQuotaMiB();

                    api.StopMasquerading();

                    var overLimit = used / quota >= .85m;

                    if (!overLimit)
                    {
                        resolvedUsers[userId.ToString()] = JToken.FromObject(new {
                            userData.userSis,
                            userData.userFullName,
                            usedMiB = used
                        });

                        Console.WriteLine($"User {userId} is now under the limit. No action taken.");
                        continue;
                    }

                    if (isTeacher)
                    {
                        actionLimitedUsers[userId.ToString()] = JToken.FromObject(new {
                            userData.userSis,
                            userData.userFullName,
                            usedMiB = used
                        });

                        Console.WriteLine($"User {userId} is still over the limit, but is a teacher. No action taken.");
                        continue;
                    }

                    if (!destroy)
                    {
                        var mockDeletedFiles = new JObject();

                        api.MasqueradeAs(userId);

                        var conversationAttachments = await api.StreamPersonalFolders()
                                                      .FirstAsync(pf => pf.Name.ToLowerInvariant().Equals("conversation attachments"));

                        await foreach (var file in api.StreamPersonalFiles(sortBy: Size, order: Descending)
                                       .Where(f => f.FolderId == conversationAttachments.Id))
                        {
                            Console.WriteLine($"[MOCK] Would delete file {file.Id} for user {userId}.");
                            mockDeletedFiles[file.Id.ToString()] = JToken.FromObject(new {
                                fileName    = file.Filename,
                                displayName = file.DisplayName,
                                fileSize    = file.Size,
                                url         = file.Url,
                                contentType = file.ContentType,
                                createdAt   = file.CreatedAt,
                                modifiedAt  = file.ModifiedAt,
                                updatedAt   = file.UpdatedAt
                            });
                        }

                        api.StopMasquerading();

                        actionTakenUsers[userId.ToString()] = JToken.FromObject(new {
                            userData.userSis,
                            userData.userFullName,
                            filesWereDeleted = false,
                            mockDeletedFiles
                        });
                        Console.WriteLine($"User {userId} was over the limit, and his files would have been purged, but destructive mode is off.");
                        continue;
                    }

                    try {
                        var deletedFiles = new JObject();

                        api.MasqueradeAs(userId);

                        var conversationAttachments = await api.StreamPersonalFolders()
                                                      .FirstAsync(pf => pf.Name.ToLowerInvariant().Equals("conversation attachments"));


                        await foreach (var file in api.StreamPersonalFiles(sortBy: Size, order: Descending)
                                       .Where(f => f.FolderId == conversationAttachments.Id))
                        {
                            await api.DeleteFile(file.Id);

                            Console.WriteLine($"Deleted file {file.Id} for user {userId}.");
                            deletedFiles[file.Id.ToString()] = JToken.FromObject(new {
                                fileName    = file.Filename,
                                displayName = file.DisplayName,
                                fileSize    = file.Size,
                                url         = file.Url,
                                contentType = file.ContentType,
                                createdAt   = file.CreatedAt,
                                modifiedAt  = file.ModifiedAt,
                                updatedAt   = file.UpdatedAt
                            });
                        }

                        api.StopMasquerading();

                        actionTakenUsers[userId.ToString()] = JToken.FromObject(new {
                            userData.userSis,
                            userData.userFullName,
                            filesWereDeleted = true,
                            deletedFiles
                        });

                        Console.WriteLine($"User {userId} was over the limit, and his files were purged.");

                        if (sendMessage)
                        {
                            // todo
                        }
                    } catch (Exception e) {
                        Console.WriteLine($"Exception when taking action for user {userId}.\n{e}\nContinuing as normal ------");
                        actionFailedUsers[userId.ToString()] = JToken.FromObject(new {
                            userData.userSis,
                            userData.userFullName,
                            usedMiB = used
                        });
                    } finally {
                        api.StopMasquerading();
                    }
                } catch (Exception e) {
                    Console.WriteLine($"Exception when inspecting user {userId}.\n{e}\nContinuing as normal ------");
                    inspectionFailedUsers[userId.ToString()] = JToken.FromObject(userData);
                } finally {
                    api.StopMasquerading();
                }
            }

            document["dateCompleted"] = DateTime.Now.ToIso8601Date();
            document["usersInLog"]    = detectedUsers.Count;

            var outPath = Path.Combine(home.NsDir, $"QuotaPurger_Log_{started.Ticks}.json");

            File.WriteAllText(outPath, document.ToString(Indented) + "\n");
            Console.WriteLine($"Wrote log to {outPath}");
        }
コード例 #10
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("rolling_attendance_columns");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"            }
                            }
                        },
                        new TableSyntax("debug")
                        {
                            Items =
                            {
                                { "limit_to",                       -1 }
                            }
                        },
                        new TableSyntax("filter")
                        {
                            Items =
                            {
                                { "new_column_terms", new string[] {   }}
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and input path.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var courseLimit = config.GetTable("debug")
                              .Get <long>("limit_to");

            var filterTerms = config.GetTable("filter")
                              .Get <TomlArray>("new_column_terms")
                              .Cast <string>()
                              .ToHashSet();

            var termWhitelist = filterTerms.Count > 0;

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            if (courseLimit > 0)
            {
                Console.WriteLine($"[DEBUG] Limited to course id {courseLimit}");
            }

            if (termWhitelist)
            {
                Console.WriteLine("[FILTER] New column creation is limited to the following sections:");
                foreach (var s in filterTerms)
                {
                    Console.WriteLine($"         - {s}");
                }
            }
            else
            {
                Console.WriteLine("[FILTER] No section filter!");
            }

            try {
                var courses = courseLimit <= 0 ? api.StreamCourses(includes: Term)
                                               : AsyncEnumerable.Repeat(await api.GetCourse(Convert.ToUInt64(courseLimit), includes: Api.IndividualLevelCourseIncludes.Term), 1);

                var nextMonday    = NextWeekday(DateTime.Today, Monday);
                var nextMondayStr = FormatColumnName(nextMonday);

                var lastMonday    = NextWeekday(DateTime.Today.AddDays(-7), Monday);
                var lastMondayStr = FormatColumnName(lastMonday);

                Console.WriteLine($"The new column will be called {nextMondayStr}");
                Console.WriteLine($"The old column (if it exists) is called {lastMondayStr}");

                await foreach (var course in courses)
                {
                    try {
                        var old = await api.StreamCustomGradebookColumns(course.Id)
                                  .FirstOrDefaultAsync(col => col.Title == lastMondayStr);

                        if (old != null)
                        {
                            await api.UpdateCustomColumn(old.Id, course.Id, hidden : true);

                            Console.WriteLine($"[Course {course.Id}] Hid old column id {old.Id}");
                        }

                        if (termWhitelist)
                        {
                            var t = course.Term?.Name;
                            if (t == null || !filterTerms.Contains(t))
                            {
                                Console.WriteLine($"[Course {course.Id}] Skipping new column creation (term {t ?? "default"} not in whitelist)");
                                continue;
                            }
                        }

                        var c = await api.CreateCustomColumn(course.Id, nextMondayStr);

                        Console.WriteLine($"[Course {course.Id}] Created new column id {c.Id}");

                        var enrollments = api.StreamCourseEnrollments(
                            course.Id,
                            Api.CourseEnrollmentType.StudentEnrollment.Yield()
                            );
                        var updates = await enrollments.Select(e => new Api.ColumnEntryUpdate {
                            UserId   = e.UserId,
                            ColumnId = c.Id,
                            Content  = "N"
                        }).ToListAsync();

                        await api.UpdateCustomColumnEntries(course.Id, updates);

                        Console.WriteLine($"[Course {course.Id} - Column {c.Id}] Submitted bulk default update");
                    } catch (Exception e) {
                        Console.WriteLine($"Threw up during course {course.Id}:\n{e}\nContinuing onwards.");
                    }
                }

                Console.WriteLine("Done.");
            } catch (Exception e) {
                Console.WriteLine($"Threw up:\n{e}");
            }
        }
コード例 #11
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("uva_super_report");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"                      }
                            }
                        },
                        new TableSyntax("limits")
                        {
                            Items =
                            {
                                { "sample_skip",                                              0 },
                                { "sample_take",                                              0 },
                                { "courses_per_teacher",                                      0 },
                                { "assignments_per_course",                                   0 },
                                { "submissions_per_assignment",                               0 },
                                { "teachers_only",              false                           }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens").Get <string>("token");

            var limits                   = config.GetTable("limits");
            var sampleTake               = (int)limits.GetOr <long>("sample_take");
            var sampleSkip               = (int)limits.GetOr <long>("sample_skip");
            var coursesPerTeacher        = (int)limits.GetOr <long>("courses_per_teacher");
            var assignmentsPerCourse     = (int)limits.GetOr <long>("assignments_per_course");
            var submissionsPerAssignment = (int)limits.GetOr <long>("submissions_per_assignment");
            var teachersOnly             = limits.GetOr <bool>("teachers_only");

            Console.WriteLine($"SKIPPING {sampleSkip} users.");
            Console.WriteLine($"TAKING {(sampleTake == default ? "ALL" : sampleTake.ToString())} users.");
            Console.WriteLine($"TAKING {(coursesPerTeacher == default ? "ALL" : coursesPerTeacher.ToString())} courses per teacher.");
            Console.WriteLine($"TAKING {(assignmentsPerCourse == default ? "ALL" : assignmentsPerCourse.ToString())} assignments per course.");
            Console.WriteLine($"TAKING {(submissionsPerAssignment == default ? "ALL" : submissionsPerAssignment.ToString())} submissions per assignment.");

            // --------------------------------------------------------------------

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            var studentsObj                    = new JObject();
            var teachersObj                    = new JObject();
            var coursesObj                     = new JObject();
            var submissionsObj                 = new JObject();
            var assignmentsOverallObj          = new JObject();
            var assignmentsIndividualObj       = new JObject();
            var individualCoursePerformanceObj = new JObject();
            var overallCoursePerformanceObj    = new JObject();
            var teacherPerformanceObj          = new JObject();

            var started = DateTime.Now;

            var document = new JObject {
                ["teachers"]                    = teachersObj,
                ["students"]                    = studentsObj,
                ["courses"]                     = coursesObj,
                ["submissions"]                 = submissionsObj,
                ["assignmentsOverall"]          = assignmentsOverallObj,
                ["assignmentsIndividual"]       = assignmentsIndividualObj,
                ["individualCoursePerformance"] = individualCoursePerformanceObj,
                ["overallCoursePerformance"]    = overallCoursePerformanceObj,
                ["teacherPerformance"]          = teacherPerformanceObj,
                ["limits"] = new JObject {
                    ["sampleTake"]               = sampleTake,
                    ["sampleSkip"]               = sampleSkip,
                    ["coursesPerTeacher"]        = coursesPerTeacher,
                    ["assignmentsPerCourse"]     = assignmentsPerCourse,
                    ["submissionsPerAssignment"] = submissionsPerAssignment,
                    ["teachersOnly"]             = teachersOnly
                },
                ["dateStarted"] = started.ToIso8601Date()
            };

            var sample = api.StreamUsers()
                         .Where(u => !u.Name.ToLowerInvariant().Contains("test"))
                         .Where(u => !u.SisUserId?.StartsWith("pG") ?? false);

            if (teachersOnly)
            {
                Console.WriteLine("TAKING TEACHERS ONLY.");
                sample = sample.WhereAwait(async u => await u.IsTeacher());
            }

            sample = sample.Skip(sampleSkip);

            if (sampleTake != default)
            {
                sample = sample.Take(sampleTake);
            }

            await foreach (var user in sample)
            {
                try {
                    if (await user.IsTeacher())
                    {
                        #region CurrentUserIsTeacher

                        if (!teachersObj.ContainsKey(user.Id.ToString()))
                        {
                            teachersObj[user.Id.ToString()] = new JObject {
                                ["sisId"]    = user.SisUserId,
                                ["fullName"] = user.Name
                            };
                        }

                        var enrollmentsStream = api.StreamUserEnrollments(user.Id, TeacherEnrollment.Yield())
                                                .SelectAwait(async e => (e, await api.GetCourse(e.CourseId)))
                                                .Where(ec => !ec.Item2.Name.ToLowerInvariant().Contains("advisory"));

                        if (coursesPerTeacher != default)
                        {
                            enrollmentsStream = enrollmentsStream.Take(coursesPerTeacher);
                        }

                        await foreach (var(enrollment, course) in enrollmentsStream)
                        {
                            if (!coursesObj.ContainsKey(course.Id.ToString()))
                            {
                                coursesObj[course.Id.ToString()] = new JObject {
                                    ["sisId"] = course.SisCourseId,
                                    ["name"]  = course.Name
                                };
                            }

                            if (!overallCoursePerformanceObj.ContainsKey(course.Id.ToString()))
                            {
                                var studentEnrollments =
                                    api.StreamCourseEnrollments(course.Id, StudentEnrollment.Yield());

                                var courseScores = new List <decimal>();
                                await foreach (var studentEnrollment in studentEnrollments)
                                {
                                    var grades = studentEnrollment.Grades;

                                    if (!individualCoursePerformanceObj.ContainsKey(enrollment.Id.ToString()))
                                    {
                                        individualCoursePerformanceObj[enrollment.Id.ToString()] = new JObject {
                                            ["studentId"]            = user.Id,
                                            ["courseId"]             = enrollment.CourseId,
                                            ["currentLetterGrade"]   = grades.CurrentGrade,
                                            ["finalLetterGrade"]     = grades.FinalGrade,
                                            ["currentScore"]         = grades.CurrentScore,
                                            ["finalScore"]           = grades.FinalScore,
                                            ["activitySecondsSpent"] = studentEnrollment.TotalActivityTime,
                                            ["lastAttendedAt"]       = studentEnrollment.LastAttendedAt?.ToIso8601Date(),
                                            ["lastActivityAt"]       = studentEnrollment.LastActivityAt?.ToIso8601Date()
                                        };
                                    }

                                    var currentScore = grades.CurrentScore;
                                    courseScores.Add(string.IsNullOrEmpty(currentScore) ? 0
                                                                                        : Convert.ToDecimal(grades.CurrentScore));
                                }

                                if (!courseScores.Any())
                                {
                                    continue;
                                }

                                var courseScoreStats = new Stats(courseScores);
                                var pass             = courseScores.Count(s => s > 66.5m);

                                overallCoursePerformanceObj[course.Id.ToString()] = new JObject {
                                    ["gradesInSample"]               = courseScores.Count,
                                    ["meanCourseScore"]              = courseScoreStats.Mean,
                                    ["modeCourseScore"]              = courseScoreStats.Mode,
                                    ["25thPercentileCourseScore"]    = courseScoreStats.Q1,
                                    ["medianCourseScore"]            = courseScoreStats.Median,
                                    ["75thPercentileCourseScore"]    = courseScoreStats.Q3,
                                    ["courseScoreStandardDeviation"] = courseScoreStats.Sigma,
                                    ["coursePassCount"]              = pass,
                                    ["courseFailCount"]              = courseScores.Count - pass
                                };
                            }

                            var assignments = (await api.StreamCourseAssignments(course.Id)
                                               .Where(a => a.Published)
                                               .ToListAsync())
                                              .DistinctBy(a => a.Id);

                            if (assignmentsPerCourse != default)
                            {
                                assignments = assignments.Take(assignmentsPerCourse);
                            }

                            foreach (var assignment in assignments)
                            {
                                var allSubmissionsStream = api.StreamSubmissionVersions(course.Id, assignment.Id);

                                if (submissionsPerAssignment != default)
                                {
                                    allSubmissionsStream = allSubmissionsStream.Take(submissionsPerAssignment);
                                }

                                var allSubmissions = await allSubmissionsStream.ToListAsync();

                                // just listing every submission here
                                foreach (var submission in allSubmissions)
                                {
                                    var submitter = await api.GetUser(submission.UserId);

                                    submissionsObj[$"c_{course.Id}|a_{assignment.Id}|u_{submitter.Id}|n_{submission.Attempt??0}"] = new JObject {
                                        ["assignmentId"]        = assignment.Id,
                                        ["courseId"]            = course.Id,
                                        ["userId"]              = submitter.Id,
                                        ["versionNumber"]       = submission.Attempt,
                                        ["submissionDate"]      = submission.SubmittedAt,
                                        ["pointsEarned"]        = submission.Score,
                                        ["dateSubmitted"]       = submission.SubmittedAt,
                                        ["dateGraded"]          = submission.GradedAt,
                                        ["gradeMatchesCurrent"] = submission.GradeMatchesCurrentSubmission,
                                        ["late"]    = submission.Late ?? false,
                                        ["missing"] = submission.Missing ?? false
                                    };
                                }

                                // now narrow down for assignment performance
                                var submissions = allSubmissions.Where(s => s.Score != null)
                                                  .GroupBy(s => s.UserId)
                                                  .Select(sg => sg.First())
                                                  .ToList();

                                if (!submissions.Any())
                                {
                                    continue;
                                }

                                var scores = submissions.Select(s => s.Score)
                                             .Cast <decimal>()
                                             .ToList();

                                var stats = new Stats(scores);

                                if (assignmentsOverallObj.ContainsKey($"c_{course.Id}|a_{assignment.Id}"))
                                {
                                    Console.WriteLine($"WARN: Duplicated assignment?\nc={course.Id}\na={assignment.Id}\n\n{assignment.ToPrettyString()}\nSkipping!------\n");
                                    continue;
                                }

                                assignmentsOverallObj[$"c_{course.Id}|a_{assignment.Id}"] = new JObject {
                                    ["assignmentName"]         = assignment.Name,
                                    ["assignmentId"]           = assignment.Id,
                                    ["courseId"]               = course.Id,
                                    ["teacherId"]              = user.Id,
                                    ["countedInFinalGrade"]    = !(assignment.OmitFromFinalGrade ?? false),
                                    ["pointsPossible"]         = assignment.PointsPossible,
                                    ["createdDate"]            = assignment.CreatedAt.ToIso8601Date(),
                                    ["dueDate"]                = assignment.DueAt?.ToIso8601Date(),
                                    ["gradesInSample"]         = submissions.Count,
                                    ["meanScore"]              = stats.Mean,
                                    ["modeScore"]              = stats.Mode,
                                    ["25thPercentileScore"]    = stats.Q1,
                                    ["medianScore"]            = stats.Median,
                                    ["75thPercentileScore"]    = stats.Q3,
                                    ["scoreStandardDeviation"] = stats.Sigma
                                };

                                // here we're listing the submissions per user that actually count for grades
                                foreach (var submission in submissions)
                                {
                                    var submitter = await api.GetUser(submission.UserId);

                                    Debug.Assert(!assignmentsIndividualObj.ContainsKey($"c_{course.Id}|a_{assignment.Id}|u_{submitter.Id}"));

                                    var score = submission.Score.Value;
                                    var z     = Convert.ToDouble(score - stats.Mean) / stats.Sigma;
                                    var iqr   = stats.Q3 - stats.Q1;

                                    TimeSpan?minutesSubmittedBeforeDueDate = null;
                                    if (submission.SubmittedAt != null && assignment.DueAt != null)
                                    {
                                        minutesSubmittedBeforeDueDate = assignment.DueAt.Value - submission.SubmittedAt.Value;
                                    }

                                    assignmentsIndividualObj[$"c_{course.Id}|a_{assignment.Id}|u_{submitter.Id}"] = new JObject {
                                        ["assignmentId"]   = assignment.Id,
                                        ["courseId"]       = course.Id,
                                        ["userId"]         = submitter.Id,
                                        ["submissionDate"] = submission.SubmittedAt,
                                        ["pointsEarned"]   = score,
                                        ["z"]              = z,
                                        ["isUnusual"]      = Math.Abs(z) > 1.96,
                                        ["isMinorOutlier"] = score <stats.Q1 - iqr * 1.5m ||
                                                                    score> stats.Q3 + iqr * 1.5m,
                                        ["isMajorOutlier"] = score <stats.Q1 - iqr * 3m ||
                                                                    score> stats.Q3 + iqr * 3m,
                                        ["minutesSubmittedBeforeDueDate"] = minutesSubmittedBeforeDueDate?.TotalMinutes
                                    };
                                }
                            }
                        }

                        teacherPerformanceObj[user.Id.ToString()] = new JObject {
                        };

                        #endregion
                    }
                    else
                    {
                        #region CurrentUserIsStudent

                        if (!studentsObj.ContainsKey(user.Id.ToString()))
                        {
                            var lastLogin = api.StreamUserAuthenticationEvents(user.Id)
                                            .Where(e => e.Event == EventType.Login)
                                            .Select(e => e.CreatedAt)
                                            .DefaultIfEmpty()
                                            .MaxAsync();

                            studentsObj[user.Id.ToString()] = new JObject {
                                ["sisId"]     = user.SisUserId,
                                ["fullName"]  = user.Name,
                                ["lastLogin"] = (await lastLogin).ToIso8601Date()
                            };
                        }

                        await foreach (var enrollment in api.StreamUserEnrollments(user.Id))
                        {
                            if (!coursesObj.ContainsKey(enrollment.CourseId.ToString()))
                            {
                                var course = await api.GetCourse(enrollment.CourseId);

                                coursesObj[course.Id.ToString()] = new JObject {
                                    ["sisId"] = course.SisCourseId,
                                    ["name"]  = course.Name
                                };
                            }
                        }

                        #endregion
                    }
                } catch (Exception e) {
                    Console.WriteLine($"Caught an exception while processing user id {user.Id}\n{e}\n-------\n");
                }
            }

            document["dateCompleted"] = DateTime.Now.ToIso8601Date();
            document["usersInReport"] = studentsObj.Count + teachersObj.Count;

            var outPath = Path.Combine(home.NsDir, $"SuperReport_{started.Ticks}.json");
            File.WriteAllText(outPath, document.ToString(Indented) + "\n");
            Console.WriteLine($"Wrote report to {outPath}");
        }
コード例 #12
0
        public static async Task Main(string[] args)
        {
            var home = new AppHome("sis_importer");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"                       }
                            }
                        },
                        new TableSyntax("input")
                        {
                            Items =
                            {
                                { "import_file", "RELATIVE_IMPORT_FILE_PATH_HERE" }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and input path.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var inputFilePath = config.GetTable("input")
                                .Get <string>("import_file");

            if (!File.Exists(inputFilePath))
            {
                if (File.Exists(Path.Combine(home.NsDir, inputFilePath)))
                {
                    inputFilePath = Path.Combine(home.NsDir, inputFilePath);
                }
                else
                {
                    Console.WriteLine($"The import file path {inputFilePath} was not found.");
                    return;
                }
            }

            byte[] file = File.ReadAllBytes(inputFilePath);

            Console.WriteLine($"Successfully read import file at {inputFilePath}.");

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            var started = DateTime.Now;

            try {
                var imp = await api.ImportSisData(file, Path.GetFileName(inputFilePath));

                Console.WriteLine($"Import with ID {imp.Id} was submitted. Waiting until completion...");

                do
                {
                    await Task.Delay(500); // check the import status every N

                    imp = await api.GetSisImport(imp.Id);
                } while (!imp.WorkflowState.IsHaltedState());

                Console.WriteLine($"Import finished with status: {imp.WorkflowState}");
                var completed = DateTime.Now;

                switch (imp.WorkflowState)
                {
                case Imported:
                case ImportedWithMessages:
                case FailedWithMessages:
                case Failed: {
                    var document = new JObject {
                        ["wasSuccessful"] = imp.WorkflowState == Imported ||
                                            imp.WorkflowState == ImportedWithMessages,
                        ["status"]          = imp.WorkflowState.ToString(),
                        ["importStarted"]   = started.ToIso8601Date(),
                        ["importCompleted"] = completed.ToIso8601Date(),
                        ["counts"]          = JToken.FromObject(imp.Data.Counts ?? new object()),
                        ["errors"]          = JToken.FromObject(imp.ProcessingErrors ?? new List <IEnumerable <string> >()),
                        ["warnings"]        = JToken.FromObject(imp.ProcessingWarnings ?? new List <IEnumerable <string> >())
                    };

                    var outPath = Path.Combine(home.NsDir, $"SisImport_{started.Year}-{started.Month}-{started.Day}.json");
                    File.WriteAllText(outPath, document.ToString(Indented) + "\n");
                    Console.WriteLine($"Wrote log to {outPath}");

                    return;
                }

                case PartiallyRestored:
                case Restored:
                case Aborted:
                    return;

                case Initializing:
                case Created:
                case Importing:
                case CleanupBatch:
                case Restoring:
                case Invalid:
                    Debug.Assert(false);
                    return;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            } catch (Exception e) {
                Console.WriteLine($"ImportSisData call threw up:\n{e}");
            }
        }
コード例 #13
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("uva_quota_watcher");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"        }
                            }
                        },
                        new TableSyntax("options")
                        {
                            Items =
                            {
                                { "send_message", false            }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens").Get <string>("token");

            var options     = config.GetTable("options");
            var sendMessage = options.GetOr <bool>("send_message");

            Console.WriteLine($"Sending message? {(sendMessage ? "YES" : "NO")}");

            // --------------------------------------------------------------------

            var api = new Api(token, "https://uview.instructure.com/api/v1/");

            var started = DateTime.Now;

            var detectedUsers = new JObject();
            var document      = new JObject {
                ["detectedUsers"] = detectedUsers,
                ["dateStarted"]   = started.ToIso8601Date(),
                ["sentMessages"]  = sendMessage
            };

            await foreach (var user in api.StreamUsers())
            {
                try {
                    api.MasqueradeAs(user.Id);
                    var(quota, used) = await api.GetPersonalQuotaMiB();

                    api.StopMasquerading();

                    if (used / quota < .85m)
                    {
                        continue;
                    }

                    var isTeacher = await await api.GetUser(user.Id)
                                    .ThenApply(u => u.IsTeacher());

                    if (isTeacher)
                    {
                        Console.WriteLine($"Noticed teacher {user.Id, -4} - {Math.Round(used / quota * 100, 3), 8:####.000}%");
                    }
                    else
                    {
                        Console.WriteLine($"Noticed non-teacher {user.Id, -4} - {Math.Round(used / quota * 100, 3), 8:####.000}%");
                    }

                    detectedUsers[user.Id.ToString()] = new JObject {
                        ["userSis"]          = user.SisUserId,
                        ["userFullName"]     = user.Name,
                        ["quotaUsedMiB"]     = used,
                        ["quotaUsedPercent"] = used / quota * 100
                    };

                    if (!sendMessage)
                    {
                        continue;
                    }

                    if (isTeacher)
                    {
                        Console.WriteLine($"Didn't send a message to {user.Id} because they are a teacher.");
                        continue;
                    }

                    var message = string.Format(WarningMessageTemplate,
                                                user.Name,
                                                Math.Round(used / quota * 100, 2),
                                                Math.Round(used, 3));

                    await foreach (var c in api.CreateConversation(new QualifiedId[] { user.Id },
                                                                   message,
                                                                   "File Storage Alert",
                                                                   true))
                    {
                        Console.WriteLine($"Sent the message to {user.Id}.\n{c.ToPrettyString()}\n------\n");
                    }
                } catch (Exception e) {
                    Console.WriteLine($"Caught exception, skipping check for user {user.Id}.\n{e}\n");
                } finally {
                    api.StopMasquerading();
                }
            }

            document["dateCompleted"] = DateTime.Now.ToIso8601Date();
            document["usersInLog"]    = detectedUsers.Count;

            var outPath = Path.Combine(home.NsDir, $"QuotaWatcher_Log_{started.Ticks}.json");

            File.WriteAllText(outPath, document.ToString(Indented) + "\n");
            Console.WriteLine($"Wrote log to {outPath}");
        }
コード例 #14
0
ファイル: Program.cs プロジェクト: uvadev/UVACanvasAccess
        public static async Task Main(string[] args)
        {
            var home = new AppHome("file_map_uploader");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"                                            }
                            }
                        },
                        new TableSyntax("data")
                        {
                            Items =
                            {
                                { "map_file",          "RELATIVE_MAP_FILE_PATH_HERE"                             },
                                { "id_is_sis",         true                                                      },
                                { "target_canvas_dir", "uploaded"                                                },
                                { "send_message",      true                                                      },
                                { "message",           "An important file was pushed to your account."           }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token and map info.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens").Get <string>("token");

            var data         = config.GetTable("data");
            var mapFileName  = data.Get <string>("map_file");
            var idIsSis      = data.GetOr("id_is_sis", true);
            var targetFolder = data.Get <string>("target_canvas_dir");
            var sendMessage  = data.Get <bool>("send_message");
            var message      = data.Get <string>("message");

            var mapFilePath = Path.Combine(home.NsDir, mapFileName);

            Console.WriteLine($"Sourcing map from {mapFilePath}");

            var startedAt = DateTime.Now;

            // ------------------------------------------------------------------------

            var list = File.ReadAllLines(mapFilePath).ToList();

            List <string>[] taskLists = list.Chunk(Math.Min(Math.Max(list.Count / 7, 2), list.Count - 1))
                                        .ToArray();

            var nThreads = taskLists.Length;

            var apis = new Api[nThreads];

            for (int i = 0; i < nThreads; i++)
            {
                apis[i] = new Api(token, "https://uview.instructure.com/api/v1/");
            }

            Console.WriteLine($"Using {nThreads} threads.");

            var completed = new ConcurrentBag <string>();
            var notFound  = new ConcurrentBag <string>();
            var error     = new ConcurrentBag <string>();
            var keys      = new ConcurrentBag <string>();

            using (var countdown = new CountdownEvent(nThreads)) {
                for (int i = 0; i < nThreads; i++)
                {
                    ThreadPool.QueueUserWorkItem(o => {
                        try {
                            var n = (int)o;
                            foreach (var line in taskLists[n])
                            {
                                string[] halves = line.Split(',');
                                Debug.Assert(halves.Length == 2);

                                var(userKey, userFile) = (halves[0], halves[1]);
                                keys.Add(userKey);

                                var api = apis[n];

                                try {
                                    var user = idIsSis switch {
                                        true => api.GetUserBySis(userKey).Result,
                                        _ => api.GetUser(ulong.Parse(userKey)).Result
                                    };

                                    if (user == null)
                                    {
                                        Console.WriteLine($"WARN: Couldn't find the user for sis {userKey} !!");
                                        notFound.Add(userKey);
                                        continue;
                                    }

                                    var bytes = File.ReadAllBytes(Path.Combine(Path.GetDirectoryName(mapFilePath),
                                                                               userFile));

                                    Console.WriteLine($"Preparing to upload filename {userFile} to user " +
                                                      $"{userKey}, Id {user.Id}, SIS {user.SisUserId}");

                                    api.MasqueradeAs(user.Id);

                                    var file = api.UploadPersonalFile(bytes,
                                                                      userFile,
                                                                      targetFolder)
                                               .Result;

                                    Console.WriteLine($"Uploaded as {file.Id}!");
                                    completed.Add(userKey);

                                    if (sendMessage)
                                    {
                                        api.StopMasquerading();
                                        api.CreateConversation(new QualifiedId(user.Id).Yield(),
                                                               message,
                                                               forceNew: true);
                                    }
                                } catch (Exception e) {
                                    Console.WriteLine($"Caught an exception during upload for {userKey}: {e}");
                                    error.Add(userKey);
                                } finally {
                                    api.StopMasquerading();
                                }
                            }
                        } finally {
                            countdown.Signal();
                        }
                    }, i);
                }
                countdown.Wait();
            }

            var completedE = completed.Distinct().ToList();
            var errorE     = error.Distinct().ToList();
            var notFoundE  = notFound.Distinct().ToList();

            Console.WriteLine($"{completedE.Count} out of {list.Count} operations were completed.");

            if (errorE.Any())
            {
                Console.WriteLine($"Operation failed for the following SIS IDs: {errorE.ToPrettyString()}");
            }

            if (notFoundE.Any())
            {
                Console.WriteLine($"The following SIS IDs could not be resolved: {notFoundE.ToPrettyString()}");
            }

            var document = new JObject {
                ["dateStarted"]   = startedAt.ToIso8601Date(),
                ["dateCompleted"] = DateTime.Now.ToIso8601Date(),
                ["completed"]     = new JArray(completedE),
                ["error"]         = new JArray(errorE),
                ["notFound"]      = new JArray(notFoundE)
            };

            var outPath = Path.Combine(home.NsDir, $"FileMapUploader_Log_{startedAt.Ticks}.json");

            File.WriteAllText(outPath, document.ToString(Formatting.Indented) + "\n");
            Console.WriteLine($"Wrote log to {outPath}");
        }
コード例 #15
0
        public static async Task Main(string[] args)
        {
            var selected = -1;

            if (args.Length > 0)
            {
                int.TryParse(args[0], out selected);
            }

            var home = new AppHome("canvas_report_gen");

            Console.WriteLine($"Using config path: {home.ConfigPath}");

            if (!home.ConfigPresent())
            {
                Console.WriteLine("Need to generate a config file.");

                home.CreateConfig(new DocumentSyntax {
                    Tables =
                    {
                        new TableSyntax("tokens")
                        {
                            Items =
                            {
                                { "token", "PUT_TOKEN_HERE"           }
                            }
                        },
                        new TableSyntax("sis")
                        {
                            Items =
                            {
                                { "use_sis",      false                    },
                                { "current_year", "2021"                   },
                                { "conn_str",     "PUT_CONN_STR_HERE"      }
                            }
                        },
                        new TableSyntax("truancy")
                        {
                            Items =
                            {
                                { "sis_id_year", "2020"               },
                                { "subaccounts", new string[] {       } }
                            }
                        }
                    }
                });

                Console.WriteLine("Created a new config file. Please go put in your token.");
                return;
            }

            Console.WriteLine("Found config file.");

            var config = home.GetConfig();

            Debug.Assert(config != null, nameof(config) + " != null");

            var token = config.GetTable("tokens")
                        .Get <string>("token");

            var sisTable = config.GetTable("sis");

            Database.UseSis = sisTable.Get <bool>("use_sis");

            if (Database.UseSis)
            {
                Database.ConnStr     = sisTable.Get <string>("conn_str");
                Database.CurrentYear = sisTable.Get <string>("current_year");
            }

            var emailTable = config.GetTable("email");

            var started = DateTime.Now;

            var outPath = Path.Combine(home.NsDir, "{0}" + $"_{started.Ticks}.csv");

            for (;;)
            {
                Console.WriteLine("Which report? (* = needs SIS)");
                Console.WriteLine("1: Zero Logins");
                Console.WriteLine("2: Last Activity");
                Console.WriteLine("3: *Truancy");
                Console.WriteLine("4: *Truancy (Short Interval)");
                Console.WriteLine("5: *Grade Levels");
                Console.Write("?> ");
                await Console.Out.FlushAsync();

                if (selected > -1 || int.TryParse(Console.ReadLine(), out selected))
                {
                    switch (selected)
                    {
                    case 1:
                        await Reports.ZeroLogins(token, string.Format(outPath, "ZeroLogins"));

                        Mailman.SendReport(emailTable, "ZeroLogins", outPath, started);
                        return;

                    case 2:
                        await Reports.LastActivity(token, string.Format(outPath, "LastActivity"));

                        Mailman.SendReport(emailTable, "LastActivity", outPath, started);
                        return;

                    case 3:
                        await Reports.Truancy(token,
                                              string.Format(outPath, "Truancy"),
                                              config.GetTable("truancy"));

                        Mailman.SendReport(emailTable, "Truancy", outPath, started);
                        return;

                    case 4:
                        await Reports.Truancy(token,
                                              string.Format(outPath, "TruancyShort"),
                                              config.GetTable("truancy"),
                                              true);

                        Mailman.SendReport(emailTable, "TruancyShort", outPath, started);
                        return;

                    case 5:
                        await Reports.GradeLevels(token, string.Format(outPath, "GradeLevels"));

                        Mailman.SendReport(emailTable, "GradeLevels", outPath, started);
                        return;

                    case 0:
                        File.WriteAllText(string.Format(outPath, "Dummy"), "dummy1,dummy2\na,b\n");
                        Mailman.SendReport(emailTable, "Dummy", outPath, started);
                        return;

                    default:
                        Console.WriteLine("Please choose a report.\n");
                        break;
                    }
                }
                else
                {
                    Console.WriteLine("Please choose a report.\n");
                }
            }
        }