1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | import os
|
21 | import csv
|
22 | import re
|
23 | import owncloud
|
24 | import getpass
|
25 | import requests
|
26 | import lxml.html
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | CSV_FILE_PATH = '/home/username/students.csv'
|
33 | OWNCLOUD_FOLDER = 'Interro Maths/'
|
34 |
|
35 | OWNCLOUD_ADDRESS = 'http://ncloud.zaclys.com/'
|
36 | OWNCLOUD_USERNAME = 'MyUserName'
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | class Student:
|
43 |
|
44 |
|
45 | def __init__(self, name='', surname='', group='', number = '',
|
46 | email='', owncloud='', quiz=''):
|
47 | |
48 |
|
49 | - name
|
50 | - surname
|
51 | - group (or class)
|
52 | - identification number
|
53 | - email
|
54 | - owncloud username
|
55 | - quiz path
|
56 | """
|
57 | self.name = name
|
58 | self.surname = surname
|
59 | self.group = group
|
60 | self.number = number
|
61 | self.email = email
|
62 | self.owncloud = owncloud
|
63 | self.quiz = quiz
|
64 |
|
65 |
|
66 | def print(self):
|
67 |
|
68 | print(' {0:15} {1:15} {2:5} {3:4} {4:40} {5:40} {6}'.format(
|
69 | self.surname,
|
70 | self.name,
|
71 | self.group,
|
72 | self.number,
|
73 | self.email,
|
74 | self.owncloud,
|
75 | self.quiz))
|
76 |
|
77 |
|
78 | def get_files_paths_from_nautilus():
|
79 | |
80 |
|
81 | Look also into selected folders (but not in subfolders).
|
82 | Returns a list of file paths that are not symbolic links.
|
83 | """
|
84 |
|
85 | if 'NAUTILUS_SCRIPT_SELECTED_FILE_PATHS' in os.environ:
|
86 | selection = os.environ['NAUTILUS_SCRIPT_SELECTED_FILE_PATHS']
|
87 |
|
88 |
|
89 | list_of_files = []
|
90 | for path in selection.splitlines():
|
91 |
|
92 | if os.path.isfile(path):
|
93 | if not os.path.islink(path):
|
94 | list_of_files.append(path)
|
95 |
|
96 | else:
|
97 | for f in os.listdir(path):
|
98 | newpath = os.path.join(path,f)
|
99 | if os.path.isfile(newpath) and not os.path.islink(newpath):
|
100 | list_of_files.append(newpath)
|
101 |
|
102 | print( '{0} files selected:'.format( len(list_of_files) ) )
|
103 | print( ' '+'\n '.join( list_of_files ) )
|
104 | return list_of_files
|
105 |
|
106 |
|
107 | def get_students_from_csv(csv_file_path, verbose=False):
|
108 | |
109 |
|
110 | Returns a dictionary of all students (key = number, value = student object)
|
111 | """
|
112 | dict_of_students = {}
|
113 | with open(csv_file_path, newline='') as csv_file:
|
114 | tableau = csv.DictReader(csv_file, delimiter=':')
|
115 | for row in tableau:
|
116 | student = Student( name = row["name"],
|
117 | surname = row["surname"],
|
118 | group = row["group"],
|
119 | number = row["number"],
|
120 | email = row["email"],
|
121 | owncloud = row["owncloud"])
|
122 | dict_of_students[student.number] = student
|
123 |
|
124 | print( '\nFound {} students in {}.'.format(len(dict_of_students),
|
125 | csv_file_path
|
126 | ))
|
127 | if verbose:
|
128 | for student in dict_of_students.values():
|
129 | student.print()
|
130 | return dict_of_students
|
131 |
|
132 |
|
133 | def associate_quiz_to_student(list_of_quiz, dict_of_students, verbose=False):
|
134 | |
135 |
|
136 | Update quiz attribute of each student with the corresponding file path.
|
137 | Returns a list of students that matched quizzes.
|
138 | """
|
139 | matched_students = []
|
140 | unmatched_quiz = []
|
141 |
|
142 | for quiz_path in list_of_quiz:
|
143 |
|
144 | quiz_name = os.path.basename(quiz_path)
|
145 | regular_expression = re.compile('[0-9]+')
|
146 | student_number = re.search(regular_expression,quiz_name).group()
|
147 | try:
|
148 |
|
149 | dict_of_students[student_number].quiz = quiz_path
|
150 | matched_students.append(dict_of_students[student_number])
|
151 | except:
|
152 |
|
153 | unmatched_quiz.append(quiz_path)
|
154 |
|
155 | print('\n{}/{} quizzes matched'.format(len(matched_students),
|
156 | len(list_of_quiz)))
|
157 | if verbose:
|
158 | for student in matched_students:
|
159 | print(' {:15} {:15} n°{:3} {}'.format(student.surname,
|
160 | student.name,
|
161 | student.number,
|
162 | student.quiz))
|
163 | if len(matched_students) != len(list_of_quiz):
|
164 | print('Unmatched file(s):')
|
165 | for quiz_path in unmatched_quiz:
|
166 | print(' {}'.format( quiz_path ))
|
167 | cancel = input('Do you want to continue? (y/n) ')
|
168 | if cancel.lower() == 'n':
|
169 | quit('\nScript cancelled !')
|
170 | return matched_students
|
171 |
|
172 |
|
173 | def connect_owncloud(address, username, password=None):
|
174 | |
175 |
|
176 | Return a Client object.
|
177 | """
|
178 | oc = owncloud.Client(address)
|
179 | if password == None:
|
180 | password = getpass.getpass('\nEnter Owncloud password: ')
|
181 | print('\nConnecting to Owncloud... ', end="")
|
182 | try:
|
183 | oc.login(username, password)
|
184 | except Exception as e:
|
185 | print('Error logging in! {}'.format(e))
|
186 | retry = input('Try again? (y/n) ')
|
187 | if retry.lower() == 'y':
|
188 | oc = connect_owncloud(address, username, password=None)
|
189 | else:
|
190 | quit()
|
191 |
|
192 | print('Connected !')
|
193 | return oc
|
194 |
|
195 |
|
196 | def connect_owncloud_behind_sso(owncloud_address, username, password=None):
|
197 | |
198 |
|
199 | Return a Client object.
|
200 | """
|
201 |
|
202 | s = requests.session()
|
203 | sso_address = s.get(owncloud_address).url
|
204 | login = s.get(sso_address)
|
205 | login_html = lxml.html.fromstring(login.text)
|
206 | hidden_inputs = login_html.xpath(r'//form//input[@type="hidden"]')
|
207 | form = {x.attrib["name"]: x.attrib["value"] for x in hidden_inputs}
|
208 |
|
209 |
|
210 | if password == None:
|
211 | password = getpass.getpass('\nEnter Owncloud password: ')
|
212 | form['username'] = username
|
213 | form['password'] = password
|
214 | response = s.post(sso_address, data=form)
|
215 | print(response.url)
|
216 | print('\nConnecting to Owncloud... ', end="")
|
217 | oc = owncloud.Client(owncloud_address)
|
218 | oc._session = s
|
219 |
|
220 |
|
221 | try:
|
222 | oc._update_capabilities()
|
223 | print('Ok !')
|
224 | except Exception as e:
|
225 | print('Error logging in! {}'.format(e))
|
226 | retry = input('Try again? (y/n) ')
|
227 | if retry.lower() == 'y':
|
228 | oc = connect_owncloud_behind_sso( owncloud_address,
|
229 | login, password=None)
|
230 | else:
|
231 | quit()
|
232 |
|
233 |
|
234 | oc._session = requests.session()
|
235 | oc._session.verify = oc._verify_certs
|
236 | oc._session.auth = (username, password)
|
237 |
|
238 | return oc
|
239 |
|
240 |
|
241 | def upload_and_share_quiz(owncloud_client, list_of_students, folder_base,
|
242 | quiz_name = None):
|
243 | |
244 |
|
245 | Create remote folders for each students (if not already there):
|
246 | "/folder_base/Group/Surname - Name (Number) - Interros Maths/"
|
247 |
|
248 | Upload students quizzes:
|
249 | "User input - Surname Name (Number).ext"
|
250 |
|
251 | Share folders with the corresponding students (if not already done).
|
252 | """
|
253 |
|
254 | print('\nUploading files...')
|
255 | try:
|
256 | owncloud_client.mkdir(folder_base)
|
257 | print('Created folder ' + folder_base)
|
258 | except:
|
259 | pass
|
260 | for student in list_of_students:
|
261 | folder_group = student.group + '/'
|
262 | folder_student = ( student.surname
|
263 | + ' ' + student.name
|
264 | + ' (' + student.number + ')'
|
265 | + ' - Interros Maths/')
|
266 | remote_folder = folder_base + folder_group + folder_student
|
267 | try:
|
268 | owncloud_client.mkdir(folder_base + folder_group)
|
269 | print('Created folder ' + folder_base + folder_group)
|
270 | except:
|
271 | pass
|
272 | try:
|
273 | owncloud_client.mkdir(remote_folder)
|
274 | print('Created folder ' + remote_folder)
|
275 | except:
|
276 | pass
|
277 |
|
278 |
|
279 | if quiz_name == None:
|
280 | quiz_name = input('Enter quiz name: ')
|
281 | remote_quiz_name = ( quiz_name + ' - '
|
282 | + student.surname
|
283 | + ' ' + student.name
|
284 | + ' (' + student.number + ')'
|
285 | + '.' + student.quiz.split(".")[-1])
|
286 | remote_quiz_path = ( remote_folder
|
287 | + remote_quiz_name)
|
288 | try:
|
289 | owncloud_client.put_file(remote_quiz_path, student.quiz)
|
290 | print('Sent file ' + remote_quiz_path)
|
291 | except:
|
292 | print("Can't send file " + remote_quiz_path)
|
293 |
|
294 |
|
295 | is_shared = False
|
296 | for file_share in owncloud_client.get_shares(remote_folder):
|
297 | if file_share.get_share_with() == student.owncloud:
|
298 | is_shared = True
|
299 | break
|
300 | if is_shared == False:
|
301 | try:
|
302 | if '@' in student.owncloud:
|
303 | owncloud_client.share_file_with_user( remote_folder,
|
304 | student.owncloud + '/',
|
305 | remote_user=True)
|
306 | else:
|
307 | owncloud_client.share_file_with_user( remote_folder,
|
308 | student.owncloud)
|
309 | print( remote_folder + " shared with " + student.owncloud )
|
310 | except:
|
311 | print( "Can't share folder " + remote_folder
|
312 | + " with " + student.owncloud)
|
313 |
|
314 |
|
315 |
|
316 | list_of_quiz = get_files_paths_from_nautilus()
|
317 |
|
318 | dict_of_students = get_students_from_csv(CSV_FILE_PATH, verbose=False)
|
319 |
|
320 | list_of_students = associate_quiz_to_student(list_of_quiz, dict_of_students, verbose=True)
|
321 |
|
322 | owncloud_client = connect_owncloud(OWNCLOUD_ADDRESS, OWNCLOUD_USERNAME)
|
323 |
|
324 |
|
325 | upload_and_share_quiz(owncloud_client, list_of_students, OWNCLOUD_FOLDER)
|