-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlibrebooking-moodle-sync.py
164 lines (136 loc) · 6.29 KB
/
librebooking-moodle-sync.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/python3
import schedule
import time
import configparser
import untangle
import requests
import json
from datetime import datetime
import os
import pprint
pp = pprint.PrettyPrinter(indent=4)
config = configparser.ConfigParser(interpolation=None)
config.read(os.path.join(os.path.dirname(__file__),'config/config.ini'))
gradebook_interval = int(config['schedule']['gradebook_interval'])
sync_interval = int(config['schedule']['librebooking_interval'])
full_resync = int(config['schedule']['full_resync'])
cmid_mapping = {} # Maps common-module IDs to LibreBooking Groups
memberships = {} # Holds group membership data for all enrolled users
unmanaged_groups = {} # Holds unmanaged groups from the LibreBooking Instance
syncedUsers = 0
def update_cmid_mapping():
headers = authenticate()
groupsURI = config['data']['librebooking_uri'] + "/Groups"
r = requests.get(groupsURI, headers=headers)
lbGroups = r.json()
# Parse the group names to find the mapping for Moodle Common Module IDs or Special Keywords
for group in lbGroups['groups']:
groupName = group['name']
if "|" in groupName:
cmid = groupName.split('|')[0].strip().lower()
cmid_mapping[cmid] = int(group['id'])
else:
unmanaged_groups[int(group['id'])] = groupName
signout(headers)
def update_memberships():
try:
gradebook = untangle.parse(config['data']['gradebook_uri'])
except:
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\tError connecting to Moodle")
return
changedCount = 0
for result in gradebook.results.result:
if not result.student.cdata in memberships:
memberships[result.student.cdata] = { 'groups':[cmid_mapping['enrolled']] }
memberships[result.student.cdata]['changed'] = True
if result.score == '100 %':
if result.assignment.cdata in cmid_mapping:
if int(cmid_mapping[result.assignment.cdata]) not in memberships[result.student.cdata]['groups']:
memberships[result.student.cdata]['groups'].append(int(cmid_mapping[result.assignment.cdata]))
memberships[result.student.cdata]['changed'] = True
def sync_memberships():
headers = authenticate()
getAllUsersURI = config['data']['librebooking_uri'] + "/Users/"
r = requests.get(getAllUsersURI, headers=headers)
for user in r.json()['users']: # Loop through the list of users from LibreBooking
if user['userName'] in memberships: # If they're in the memberships list
if memberships[user['userName']]['changed']: # And the record has been updated since last sync
memberships[user['userName']]['changed'] = False
getUserURI = config['data']['librebooking_uri'] + "/Users/" + user['id']
r = requests.get(getUserURI, headers=headers)
userDetails = r.json()
groups = [int(d['id']) for d in userDetails['groups']]
## Check if the user is in any unmanaged groups
for unmanagedGID in unmanaged_groups:
if unmanagedGID in groups:
if unmanagedGID not in memberships[user['userName']]['groups']:
memberships[user['userName']]['groups'].append(unmanagedGID)
groups.sort()
memberships[user['userName']]['groups'].sort()
if not groups == memberships[user['userName']]['groups']:
updateUserURI = config['data']['librebooking_uri'] + "/Users/" + user['id']
user['groups'] = memberships[user['userName']]['groups']
r = requests.post(updateUserURI, data=json.dumps(user), headers=headers)
groups_strings = [str(gid) for gid in user['groups']]
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\tUpdated permissions for " + user['userName'] + " (GIDs: " + ", ".join(groups_strings) + ")")
signout(headers)
def stale_all_memberships():
for user in memberships:
memberships[user]['changed'] = True
## Removes unenrolled users from managed groups
def cleanup_groups():
headers = authenticate()
getAllUsersURI = config['data']['librebooking_uri'] + "/Users/"
r = requests.get(getAllUsersURI, headers=headers)
for user in r.json()['users']:
if user['userName'] not in memberships: # If a user isn't in the 'memberships' list, they aren't enrolled and so shouldn't be in any managed groups.
getUserURI = config['data']['librebooking_uri'] + "/Users/" + user['id']
r = requests.get(getUserURI, headers=headers)
userDetails = r.json()
groups = [int(d['id']) for d in userDetails['groups']]
for cmid in cmid_mapping:
if cmid_mapping[cmid] in groups:
updateUserURI = config['data']['librebooking_uri'] + "/Users/" + user['id']
groups.remove(cmid_mapping[cmid])
user['groups'] = groups
r = requests.post(updateUserURI, data=json.dumps(user), headers=headers)
print(datetime.now().strftime("%Y-%m-%d %H:%M:%S") + "\t" + user['userName'] + " is no longer enrolled, removed from managed groups.")
signout(headers)
## LibreBooking Authentication Routines
def authenticate():
credentials = {'username':config['librebooking_credentials']['username'], 'password': config['librebooking_credentials']['password']}
authURI = config['data']['librebooking_uri'] + "/Authentication/Authenticate"
r = requests.post(authURI, data=json.dumps(credentials))
auth = r.json()
headers = {'X-Booked-SessionToken':auth['sessionToken'], 'X-Booked-UserId':auth['userId']}
return(headers)
def signout(headers):
signoutURI = config['data']['librebooking_uri'] + "/Authentication/SignOut"
signoutData = {'userId':headers['X-Booked-UserId'],"sessionToken":headers['X-Booked-SessionToken']}
r = requests.post(signoutURI, data=json.dumps(signoutData))
##
## Scheduling
##
schedule.every().day.at("23:30").do(update_cmid_mapping)
schedule.every().day.at("23:35").do(cleanup_groups)
schedule.every(gradebook_interval).minutes.do(update_memberships)
schedule.every(sync_interval).minutes.do(sync_memberships)
schedule.every(full_resync).hours.do(stale_all_memberships)
print("Moodle -> Librebooking Sync Starting")
print("Gradebook pull interval:",gradebook_interval,"minutes")
print("LibreBooking sync interval:",sync_interval,"minutes")
# Initial population of the CMID map
print("Initial pull for CMID map from LibreBooking: ", end='')
update_cmid_mapping()
print(len(cmid_mapping),"group mappings retrieved")
print(len(unmanaged_groups),"unmanaged groups found")
# Initial population of memberships
print("Initial pull of Moodle gradebook for memberships mapping: ", end='')
update_memberships()
print(len(memberships), "users retrieved")
##
## Main Loop
##
while True:
schedule.run_pending()
time.sleep(1)