Contact
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
| Download
Views: 39598
1
###
2
Functions for determining various things about applying upgrades to a project.
3
4
WARNING: Pure Javascript with no crazy dependencies for easy unit testing.
5
###
6
7
misc = require('smc-util/misc')
8
{defaults, required, types} = misc
9
10
exports.available_upgrades = (opts) ->
11
types opts,
12
account_id : types.string.isRequired # id of a user
13
purchased_upgrades : types.object.isRequired # map of the total upgrades purchased by account_id
14
project_map : types.immutable.Map.isRequired # immutable.js map of data about projects
15
student_project_ids : types.object.isRequired # map project_id:true with keys *all* student
16
# projects in course, including deleted
17
###
18
Return the total upgrades that the user with given account_id has to apply
19
toward this course. This is all upgrades they have purchased minus
20
upgrades they have applied to projects that aren't student projects in
21
this course. Thus this is what they have available to distribute to
22
their students in this course.
23
24
This is a map {quota0:x, quota1:y, ...}
25
###
26
available = misc.copy(opts.purchased_upgrades)
27
opts.project_map.forEach (project, project_id) ->
28
if opts.student_project_ids[project_id] # do not count projects in course
29
return
30
upgrades = project.getIn(['users', opts.account_id, 'upgrades'])?.toJS()
31
if upgrades?
32
available = misc.map_diff(available, upgrades)
33
return
34
return available
35
36
37
exports.current_student_project_upgrades = (opts) ->
38
types opts,
39
account_id : types.string.isRequired # id of a user
40
project_map : types.immutable.Map.isRequired # immutable.js map of data about projects
41
student_project_ids : types.object.isRequired # map project_id:true with keys *all* student
42
###
43
Return the total upgrades currently applied to each student project from
44
everybody else except the user with given account_id.
45
46
This output is a map {project_id:{quota0:x, quota1:y, ...}, ...}; only projects with
47
actual upgrades are included.
48
###
49
other = {}
50
for project_id of opts.student_project_ids
51
users = opts.project_map.getIn([project_id, 'users'])
52
if not users?
53
continue
54
x = undefined
55
users.forEach (info, user_id) ->
56
if user_id == opts.account_id
57
return
58
upgrades = info.get('upgrades')?.toJS()
59
if not upgrades?
60
return
61
x = misc.map_sum(upgrades, x ? {})
62
return
63
if x?
64
other[project_id] = x
65
return other
66
67
exports.upgrade_plan = (opts) ->
68
types opts,
69
account_id : types.string.isRequired # id of a user
70
purchased_upgrades : types.object.isRequired # map of the total upgrades purchased by account_id
71
project_map : types.immutable.Map.isRequired # immutable.js map of data about projects
72
student_project_ids : types.object.isRequired # map project_id:true with keys *all* student
73
# projects in course, including deleted
74
deleted_project_ids : types.object.isRequired # map project_id:true just for projects where
75
# student is considered deleted from class
76
upgrade_goal : types.object.isRequired # [quota0:x, quota1:y]
77
###
78
Determine what upgrades should be applied by this user to get
79
the student projects to the given upgrade goal. Preference
80
is by project_id in order (arbitrary, but stable).
81
82
The output is a map {student_project_id:{quota0:x, quota1:y, ...}, ...}, where the quota0:x means
83
that account_id will apply x amount of quota0 total. Thus to actually *do* the upgrading,
84
this user (account_id) would go through the project map and set their upgrade contribution
85
for the student projects in this course to exactly what is specified by this function.
86
Note that no upgrade quota will be deducted from projects outside this course to satisfy
87
the upgrade_goal.
88
89
If a student_project_id is missing from the output the contribution is 0; if a quota is
90
missing, the contribution is 0.
91
92
The keys of the output map are **exactly** the ids of the projects where the current
93
allocation should be *changed*. That said, we only consider quotas explicitly given
94
in the upgrade_goal map.
95
###
96
# upgrades, etc., that student projects already have (which account_id did not provide)
97
cur = exports.current_student_project_upgrades
98
account_id : opts.account_id
99
project_map : opts.project_map
100
student_project_ids : opts.student_project_ids
101
102
# upgrades we have that have not been allocated to our course
103
available = exports.available_upgrades
104
account_id : opts.account_id
105
purchased_upgrades : opts.purchased_upgrades
106
project_map : opts.project_map
107
student_project_ids : opts.student_project_ids
108
109
ids = misc.keys(opts.student_project_ids); ids.sort()
110
plan = {}
111
for project_id in ids
112
if opts.deleted_project_ids[project_id]
113
# give this project NOTHING
114
continue
115
plan[project_id] = {}
116
# we only care about quotas in the upgrade_goal
117
for quota, val of opts.upgrade_goal
118
need = val - (cur[project_id]?[quota] ? 0)
119
if need > 0
120
have = Math.min(need, available[quota])
121
plan[project_id][quota] = have
122
available[quota] -= have
123
# is there an actual allocation change? if not, we do not include this key.
124
alloc = opts.project_map.getIn([project_id, 'users', opts.account_id, 'upgrades'])?.toJS() ? {}
125
change = false
126
for quota, _ of opts.upgrade_goal
127
if (alloc[quota] ? 0) != (plan[project_id][quota] ? 0)
128
change = true
129
break
130
if not change
131
delete plan[project_id]
132
return plan
133
134
135
136
137
138
139