Path: blob/master/src/packages/frontend/course/export/file-use-times.ts
1503 views
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45import { StudentsMap, StudentRecord } from "../store";6import {7exec,8write_text_file_to_project,9} from "../../frame-editors/generic/client";10import { splitlines } from "@cocalc/util/misc";11import { webapp_client } from "@cocalc/frontend/webapp-client";1213interface PathUseTimes {14edit_times: number[];15access_times: number[];16}1718interface StudentUseTimes {19student_id: string;20account_id?: string;21project_id?: string;22student_name: string;23assignment_path: string;24paths?: { [path: string]: PathUseTimes };25error?: string; // if it fails for some non-obvious reason26}2728async function one_student_file_use_times(29paths: string[],30project_id: string,31account_id: string,32limit: number = 1000,33): Promise<{ [path: string]: PathUseTimes }> {34project_id = project_id;35account_id = account_id;36const times: { [path: string]: PathUseTimes } = {};37for (const path of paths) {38const { edit_times, access_times } =39await webapp_client.conat_client.hub.db.fileUseTimes({40project_id,41path,42target_account_id: account_id,43limit,44edit_times: true,45access_times: true,46timeout: 1000 * 60 * 15,47});48if (edit_times == null || access_times == null) {49throw Error("bug");50}51times[path] = {52edit_times: edit_times.filter((x) => !!x) as number[],53access_times,54};55}56return times;57}5859function student_info(60assignment_path: string,61student: StudentRecord,62get_name: Function,63): StudentUseTimes {64const student_id = student.get("student_id");65const x: StudentUseTimes = {66student_id,67student_name: get_name(student_id),68assignment_path,69};70for (const field of ["account_id", "project_id"]) {71if (student.has(field)) {72x[field] = student.get(field);73}74}75return x;76}7778async function paths_to_scan(79project_id: string,80src_path: string,81target_path: string,82): Promise<string[]> {83const { stdout } = await exec({84command: "find",85args: ["."],86path: src_path,87err_on_exit: true,88project_id,89});90const v: string[] = [];91for (const path of splitlines(stdout)) {92const path2 = path.slice(2);93if (path2) {94v.push(target_path + "/" + path2);95}96}97return v;98}99100export async function all_students_file_use_times(101course_project_id: string,102src_path: string,103target_path: string,104students: StudentsMap,105get_name: Function,106): Promise<{ [student_id: string]: StudentUseTimes }> {107const paths = await paths_to_scan(course_project_id, src_path, target_path);108109// Iterate through the (nondeleted) students determining to what extent110// they used files in the given path in their projects.111const times: { [student_id: string]: StudentUseTimes } = {};112for (const [student_id, student] of students) {113if (student.get("deleted")) continue;114const info = (times[student_id] = student_info(115target_path,116student,117get_name,118));119if (info.project_id == null || info.account_id == null) {120// nothing more to do, since no account or project121continue;122}123try {124info.paths = await one_student_file_use_times(125paths,126info.project_id,127info.account_id,128);129} catch (err) {130info.error = `${err}`;131}132}133return times;134}135136export async function export_student_file_use_times(137course_project_id: string,138src_path: string,139target_path: string,140students: StudentsMap,141target_json: string,142get_name: Function,143): Promise<void> {144const x = await all_students_file_use_times(145course_project_id,146src_path,147target_path,148students,149get_name,150);151await write_text_file_to_project({152project_id: course_project_id,153path: target_json,154content: JSON.stringify(x, null, 2),155});156}157158159