Hallo.
Ok – beide Methoden kenne ich aber bei beiden gibt es gewisse Stolpersteine, meine ich:
Die globalen Gruppen der Klassen kann ich nicht löschen, da sie über Plugins abgeglichen werden. Den Papierkorb-Symbol gibt es entsprechend auch gar nicht in der Übersicht. Es ginge auf diesem Weg also nur bei manuell erzeugten globalen Gruppen. Und da ist der Workflow natürlich etwas schräg, wenn man alte Gruppen eigentlich loswerden will, sie aber zunächst neu anlegen soll, um sie dann zu löschen.
Der Weg über den Upload der Kurslisten habe ich bei der Einrichtung der aktuellen Kurse ja auch verwendet. Dort könnte man tatsächlich die alten Einschreibemethoden bestehender Kurse verändern. Dazu muss man aber in den alten CSV-Kurslisten nachträglich für vergangene Kurse etwas ändern und sie dann neu hochladen, wenn ich das richtig sehe. Der Weg könnte tatsächlich funktionieren (auch wenn ich ihn nicht sehr elegant finde).
Ich habe das alte Script, das ein ehemaliger Schüler von uns vor zwei Jahren im Lockdown geschrieben/erweitert hatte, noch hier: Das funktionierte schon mal und macht theoretisch genau das, was ich brauche:
Es greift über die REST-API auf moodle zu und dieser Weg ist vermutlich mit Abstand der eleganteste für solche Massen-Aktionen
.
Die Einzelschritte sind klar, doch reichen meine python-Kenntnisse leider nicht aus, um das wieder in Gang zu bringen. An irgendeiner Stelle klemmte es zuletzt (… ich bin aber nicht mehr sicher, ob es nur daran lag, dass die Verbindung da noch über den v6.x-Server lief)
Wenn sich jemand von Euch daran versuchen möchte: Gerne! Hier nochmal der Link zum Projekt:
und hier das python-Script archive.py
:
# That's the plan
#
# [classes 5 - 10]
# 1.) Rename and move Courses from 5-10 to archive
# 2.) unenrol all students, keep trainers
# 3.) create new courses in correct categories
# 4.) enrol stundents and trainers
import datetime
import re
import moodle
import categorytree
import logger
URL = 'https://meine-domain.de/moodle'
ENDPOINT = '/webservice/rest/server.php'
TOKEN = '11111111111111'
TRAINER_SHORTNAME = 'editingteacher'
STUDENT_SHORTNAME = 'student'
LAST_YEAR = datetime.date.today().year - 1
ARCHIVE_CATEGORY_NAME = "Archiv"
CLASS_YEARS = [5, 6, 7, 8, 9, 10, 11, 12, 13]
m = moodle.Moodle(URL + ENDPOINT, TOKEN)
# ----------- Helper -------------
def getYearFromClass(name):
g = re.match('.*?([0-9]+)', name)
if g is None:
return None
else:
return g.group(0)
def isLowerLevelCourse(c: any):
shortname = c['shortname']
if (shortname and len(shortname) <= 3):
classYear = getYearFromClass(shortname)
if classYear and classYear.isdigit():
return int(classYear) <= 10
return False
def is11(c: any):
if len(c['shortname']) == 4 and re.match('[1-9][a-z][a-z][1-9]', c['shortname']) is not None:
return True
return False
def is12_OR_13(c: any):
if re.match('[A-z][A-z][1-9][1-9][1-9]', c['shortname']) is not None:
return True
return False
def is12(c: any):
if str(LAST_YEAR - 7) in c['idnumber']:
return True
return False
def isUpperLevelCourse(c: any):
return is11(c) or is12_OR_13(c)
def unenrolAllStudentsFromCourse(courseId):
courseUsers = m.course_get_enroled_user(courseId)
unenrolInstructions = []
for user in courseUsers:
if user['roles'][0]['shortname'] == STUDENT_SHORTNAME:
unenrolInstructions.append({'userid': user['id'], 'courseid': course['id']})
if len(unenrolInstructions) > 0:
m.course_unenrol_users(unenrolInstructions)
def archiveCourse(course):
newCategoryId = categorytree.mapCategoryGraphs(CATEGORY_ROOT_NODE, ARCHIVE_ROOT_NODE, course['categoryid']).data['id']
print(course['categoryid'])
print(newCategoryId)
m.course_update(course['id'], categoryid=newCategoryId,
fullname=course['fullname'] + ARCHIVE_COURSE_REPRODUCTION_APPENDIX,
shortname=course['shortname'] + ARCHIVE_COURSE_REPRODUCTION_APPENDIX,
idnumber=course['shortname'] + ARCHIVE_COURSE_REPRODUCTION_APPENDIX)
# -----------------------------------------------------------------------------------
logger.heading('Moodle archive script')
logger.job('Reading all courses...')
########################## All sorts of courses ######################################
ALL_OLD_COURSES = m.courses_get()
ALL_LOWER_LEVEL_COURSES = [c for c in ALL_OLD_COURSES if isLowerLevelCourse(c)]
ALL_UPPER_LEVEL_COURSES = [c for c in ALL_OLD_COURSES if isUpperLevelCourse(c)]
ALL_12_COURSES = [c for c in ALL_OLD_COURSES if is12(c)]
ALL_BUT_12_COURSES = ALL_LOWER_LEVEL_COURSES + [c for c in ALL_OLD_COURSES if isUpperLevelCourse(c) and not is12(c)]
ALL_STUDENT_COURSES = ALL_LOWER_LEVEL_COURSES + ALL_UPPER_LEVEL_COURSES
TARGET_COURSES = [c for c in ALL_OLD_COURSES if is11(c)]
#TARGET_COURSES = ALL_LOWER_LEVEL_COURSES + ALL_BUT_12_COURSES
######################################################################################
ARCHIVE_CATEGORY_REPRODUCTION_APPENDIX = '_' + ARCHIVE_CATEGORY_NAME.lower() + '_' + str(LAST_YEAR)
ARCHIVE_COURSE_REPRODUCTION_APPENDIX = '_' + ARCHIVE_CATEGORY_NAME.lower() + '_' + str(LAST_YEAR)
logger.job('Building category graph. This might take a while....')
CATEGORY_ROOT_NODE = categorytree.buildCategoryGraph(m, TARGET_COURSES).children[0]
logger.success('Category graph built')
logger.job('Creating archive...')
ARCHIVE_CATEGORY_ID = m.category_get_by_name_and_parent(ARCHIVE_CATEGORY_NAME, 0)['id']
logger.job('Archive category name: ' + str(LAST_YEAR))
ARCHIVE_YEAR_CATEGORY_ID = m.category_get_by_name_and_parent(str(LAST_YEAR), ARCHIVE_CATEGORY_ID)['id']
logger.job('Reproducing category root node')
ARCHIVE_ROOT_NODE = categorytree.reproduceCategoryGraph(m, CATEGORY_ROOT_NODE, ARCHIVE_YEAR_CATEGORY_ID, ARCHIVE_CATEGORY_REPRODUCTION_APPENDIX)
print('')
logger.heading('Beginning with archiving')
for course in TARGET_COURSES:
print('removing all students from course ', course['shortname'])
unenrolAllStudentsFromCourse(course['id'])
print('archiving course ', course['shortname'])
archiveCourse(course)
logger.done()