Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/frontend/course/project-upgrades.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Functions for determining various things about applying upgrades to a project.78WARNING: This should stay as simple typescript with no crazy dependencies for easy node.js unit testing.9*/1011import { copy, keys, map_diff, map_sum } from "@cocalc/util/misc";12import { ProjectMap } from "../projects/store";1314interface ExistenceMap {15[keys: string]: boolean;16}1718export function available_upgrades(opts: {19account_id: string; // id of a user20purchased_upgrades: object; // map of the total upgrades purchased by account_id21project_map: ProjectMap; // immutable.js map of data about projects22student_project_ids: ExistenceMap; // map project_id:true with keys *all* student23// projects in course, including deleted24}) {25/*26Return the total upgrades that the user with given account_id has to apply27toward this course. This is all upgrades they have purchased minus28upgrades they have applied to projects that aren't student projects in29this course. Thus this is what they have available to distribute to30their students in this course.3132This is a map {quota0:x, quota1:y, ...}33*/34let available = copy(opts.purchased_upgrades);35opts.project_map.forEach(function (project, project_id) {36if (opts.student_project_ids[project_id]) {37// do not count projects in course38return;39}40const upgrades = project.getIn([41"users",42opts.account_id,43"upgrades",44]) as any;45if (upgrades != null) {46available = map_diff(available as any, upgrades.toJS());47}48});49return available;50}5152export function current_student_project_upgrades(opts: {53account_id: string; // id of a user54project_map: ProjectMap; // immutable.js map of data about projects55student_project_ids: ExistenceMap; // map project_id:true with keys *all* student56}) {57/*58Return the total upgrades currently applied to each student project from59everybody else except the user with given account_id.6061This output is a map {project_id:{quota0:x, quota1:y, ...}, ...}; only projects with62actual upgrades are included.63*/64const other = {};65for (const project_id in opts.student_project_ids) {66const users = opts.project_map.getIn([project_id, "users"]) as any;67if (users == null) {68continue;69}70var x: any = undefined;71users.forEach(function (info, user_id) {72if (user_id === opts.account_id) {73return;74}75const upgrades = info.get("upgrades");76if (upgrades == null) {77return;78}79x = map_sum(upgrades.toJS(), x != null ? x : {});80});81if (x != null) {82other[project_id] = x;83}84}85return other;86}8788export function upgrade_plan(opts: {89account_id: string; // id of a user90purchased_upgrades: object; // map of the total upgrades purchased by account_id91project_map: ProjectMap; // immutable.js map of data about projects92student_project_ids: ExistenceMap; // map project_id:true with keys *all* student93// projects in course, including deleted94deleted_project_ids: ExistenceMap; // map project_id:true just for projects where95// student is considered deleted from class96upgrade_goal: object; // [quota0:x, quota1:y]97}) {98/*99Determine what upgrades should be applied by this user to get100the student projects to the given upgrade goal. Preference101is by project_id in order (arbitrary, but stable).102103The output is a map {student_project_id:{quota0:x, quota1:y, ...}, ...}, where the quota0:x means104that account_id will apply x amount of quota0 total. Thus to actually *do* the upgrading,105this user (account_id) would go through the project map and set their upgrade contribution106for the student projects in this course to exactly what is specified by this function.107Note that no upgrade quota will be deducted from projects outside this course to satisfy108the upgrade_goal.109110If a student_project_id is missing from the output the contribution is 0; if a quota is111missing, the contribution is 0.112113The keys of the output map are **exactly** the ids of the projects where the current114allocation should be *changed*. That said, we only consider quotas explicitly given115in the upgrade_goal map.116*/117// upgrades, etc., that student projects already have (which account_id did not provide)118const cur = current_student_project_upgrades({119account_id: opts.account_id,120project_map: opts.project_map,121student_project_ids: opts.student_project_ids,122});123124// upgrades we have that have not been allocated to our course125const available = available_upgrades({126account_id: opts.account_id,127purchased_upgrades: opts.purchased_upgrades,128project_map: opts.project_map,129student_project_ids: opts.student_project_ids,130});131132const ids = keys(opts.student_project_ids);133ids.sort();134const plan = {};135for (const project_id of ids) {136if (opts.deleted_project_ids[project_id]) {137// give this project NOTHING138continue;139}140plan[project_id] = {};141// we only care about quotas in the upgrade_goal142for (var quota in opts.upgrade_goal) {143const val = opts.upgrade_goal[quota];144const need =145val -146((cur[project_id] != null ? cur[project_id][quota] : undefined) != null147? cur[project_id] != null148? cur[project_id][quota]149: undefined150: 0);151if (need > 0) {152const have = Math.min(need, available[quota]);153plan[project_id][quota] = have;154available[quota] -= have;155}156}157// is there an actual allocation change? if not, we do not include this key.158const upgrades = opts.project_map.getIn([159project_id,160"users",161opts.account_id,162"upgrades",163]) as any;164const alloc = upgrades != null ? upgrades.toJS() : {};165let change = false;166for (quota in opts.upgrade_goal) {167if (168(alloc[quota] != null ? alloc[quota] : 0) !==169(plan[project_id][quota] != null ? plan[project_id][quota] : 0)170) {171change = true;172break;173}174}175if (!change) {176delete plan[project_id];177}178}179return plan;180}181182183