Skip to content

Commit f8bda0c

Browse files
chore(packages/graphql): add script to transfer activities between courses (#4981)
1 parent c4c4b8a commit f8bda0c

1 file changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import { prisma } from '@klicker-uzh/prisma'
2+
import { recomputeDerivedPermissions } from '@klicker-uzh/util'
3+
import { EventEmitter } from 'events'
4+
5+
// ! Script to transfer a set of activities between courses
6+
async function run() {
7+
const emitter = new EventEmitter()
8+
9+
// ! Set old and new user IDs and the course IDs, which should be transferred including all activities and elements
10+
const oldCourseId = ''
11+
const newCourseId = ''
12+
const liveQuizIds = new Set<string>([])
13+
const practiceQuizIds = new Set<string>([])
14+
const microLearningIds = new Set<string>([])
15+
const groupActivityIds = new Set<string>([])
16+
const chatbotIds = new Set<string>([])
17+
18+
// fetch all content of the old user
19+
const oldCourse = await prisma.course.findUnique({
20+
where: { id: oldCourseId },
21+
include: {
22+
liveQuizzes: true,
23+
practiceQuizzes: true,
24+
microLearnings: true,
25+
groupActivities: true,
26+
chatbots: true,
27+
},
28+
})
29+
const newCourse = await prisma.course.findUnique({
30+
where: { id: newCourseId },
31+
})
32+
33+
if (!oldCourse) {
34+
throw new Error(`Origin course with ID ${oldCourseId} not found`)
35+
}
36+
if (!newCourse) {
37+
throw new Error(`Destination course with ID ${newCourseId} not found`)
38+
}
39+
40+
if (oldCourse.ownerId !== newCourse.ownerId) {
41+
throw new Error(
42+
`Old course owner and new course owner are not the same user (ID: ${oldCourse.ownerId})`
43+
)
44+
}
45+
46+
await prisma.$transaction(
47+
async (prisma) => {
48+
// migrate live quizzes to new owner
49+
let liveQuizCounter = 0
50+
for (const {
51+
id: liveQuizId,
52+
name: liveQuizName,
53+
} of oldCourse.liveQuizzes) {
54+
if (!liveQuizIds.has(liveQuizId)) {
55+
continue
56+
}
57+
58+
console.log(
59+
`Transferring live quiz: ${liveQuizName} (ID: ${liveQuizId}, ${++liveQuizCounter}/${liveQuizIds.size})`
60+
)
61+
62+
await prisma.liveQuiz.update({
63+
where: { id: liveQuizId },
64+
data: { courseId: newCourseId },
65+
})
66+
67+
emitter.emit('invalidate', {
68+
typename: 'LiveQuiz',
69+
id: liveQuizId,
70+
})
71+
72+
// recompute derived permissions for the old user (-> remove owner permissions)
73+
await recomputeDerivedPermissions(
74+
{
75+
liveQuizId,
76+
updateAccessRequests: true,
77+
},
78+
prisma
79+
)
80+
}
81+
82+
// migrate practice quizzes to new owner
83+
let practiceQuizCounter = 0
84+
for (const {
85+
id: practiceQuizId,
86+
name: practiceQuizName,
87+
} of oldCourse.practiceQuizzes) {
88+
if (!practiceQuizIds.has(practiceQuizId)) {
89+
continue
90+
}
91+
92+
console.log(
93+
`Transferring practice quiz: ${practiceQuizName} (ID: ${practiceQuizId}; ${++practiceQuizCounter}/${practiceQuizIds.size})`
94+
)
95+
96+
await prisma.practiceQuiz.update({
97+
where: { id: practiceQuizId },
98+
data: { courseId: newCourseId },
99+
})
100+
101+
emitter.emit('invalidate', {
102+
typename: 'PracticeQuiz',
103+
id: practiceQuizId,
104+
})
105+
106+
// recompute derived permissions for the old user (-> remove owner permissions)
107+
await recomputeDerivedPermissions(
108+
{
109+
practiceQuizId,
110+
updateAccessRequests: true,
111+
},
112+
prisma
113+
)
114+
}
115+
116+
// migrate microlearnings to new owner
117+
let microLearningCounter = 0
118+
for (const {
119+
id: microLearningId,
120+
name: microLearningName,
121+
} of oldCourse.microLearnings) {
122+
if (!microLearningIds.has(microLearningId)) {
123+
continue
124+
}
125+
126+
console.log(
127+
`Transferring microlearning: ${microLearningName} (ID: ${microLearningId}; ${++microLearningCounter}/${microLearningIds.size})`
128+
)
129+
130+
await prisma.microLearning.update({
131+
where: { id: microLearningId },
132+
data: { courseId: newCourseId },
133+
})
134+
135+
emitter.emit('invalidate', {
136+
typename: 'MicroLearning',
137+
id: microLearningId,
138+
})
139+
140+
// recompute derived permissions for the old user (-> remove owner permissions)
141+
await recomputeDerivedPermissions(
142+
{
143+
microLearningId,
144+
updateAccessRequests: true,
145+
},
146+
prisma
147+
)
148+
}
149+
150+
// migrate group activities to new owner
151+
let groupActivityCounter = 0
152+
for (const {
153+
id: groupActivityId,
154+
name: groupActivityName,
155+
} of oldCourse.groupActivities) {
156+
if (!groupActivityIds.has(groupActivityId)) {
157+
continue
158+
}
159+
160+
console.log(
161+
`Transferring group activity: ${groupActivityName} (ID: ${groupActivityId}; ${++groupActivityCounter}/${groupActivityIds.size})`
162+
)
163+
164+
await prisma.groupActivity.update({
165+
where: { id: groupActivityId },
166+
data: { courseId: newCourseId },
167+
})
168+
169+
emitter.emit('invalidate', {
170+
typename: 'GroupActivity',
171+
id: groupActivityId,
172+
})
173+
174+
// recompute derived permissions
175+
await recomputeDerivedPermissions(
176+
{
177+
groupActivityId,
178+
updateAccessRequests: true,
179+
},
180+
prisma
181+
)
182+
}
183+
184+
// migrate chatbots to new owner
185+
let chatbotCounter = 0
186+
for (const { id: chatbotId, name: chatbotName } of oldCourse.chatbots) {
187+
if (!chatbotIds.has(chatbotId)) {
188+
continue
189+
}
190+
191+
console.log(
192+
`Transferring chatbot: ${chatbotName} (ID: ${chatbotId}; ${++chatbotCounter}/${chatbotIds.size})`
193+
)
194+
195+
await prisma.chatbot.update({
196+
where: { id: chatbotId },
197+
data: { courseId: newCourseId },
198+
})
199+
200+
emitter.emit('invalidate', { typename: 'Chatbot', id: chatbotId })
201+
}
202+
},
203+
{ timeout: 60 * 60 * 1000 } // timeout: 1h
204+
)
205+
}
206+
207+
await run()

0 commit comments

Comments
 (0)