diff --git a/mycampusscript.py b/mycampusscript.py index 69358ed2ea463d1883bb97dc15c910e530cd3fdd..60ecc153c76a93a90d521938c0e114d0fd317508 100644 --- a/mycampusscript.py +++ b/mycampusscript.py @@ -1,9 +1,12 @@ import collections.abc import datetime as dt +import mimetypes +import os.path import random import re import sys import typing as tg +import urllib.parse import bs4 import yaml @@ -129,7 +132,8 @@ def main(scriptname: str, cmdname: str, configfile: str): dynamic=get_gradebook_categories) send_grades_to_gradebook_category: int = find_value_or_help(config, 'send_grades_to_gradebook_category') released_grade_notification_email: bool = find_value_or_help(config, 'released_grade_notification_email') - upload_attachments(config, attachments) + for attachment in attachments: + upload_attachment(config, attachment) def find_value_or_help(config: StrAnyDict, key: str, dynamic: ReplacementsFunc = None) -> tg.Any: @@ -235,7 +239,7 @@ def obtain_tokens(config: StrAnyDict): config[RBCS_TOKEN_CONFIGENTRY_NAME] = rbcstoken_input_elem['value'] -def upload_attachments(config: StrAnyDict, attachments: list[str]): +def upload_attachment(config: StrAnyDict, filepath: str): """ Make POST request with multipart/formdata body, many headers, and the following format of URL: POST /portal/site/<site-id>/tool/1f8f054a-4e58-4965-8d32-141b236e0023/sakai.filepicker.helper?special=upload&sakai_csrf_token=<hexstring>&sakai_action=doAttachupload @@ -249,7 +253,39 @@ def upload_attachments(config: StrAnyDict, attachments: list[str]): # See "POST Multiple Multipart-Encoded Files" in https://requests.readthedocs.io/en/latest/user/advanced/ # We have so far failed to obtain the HTML of the form to be posted, which we need to find the # name of the attachment file form element. - + # in c:/temp/a.json: + # Request 164 ist der POST-Request, den wir analog nachbilden müssen. + # Das JSON ist dafür unpraktisch, weil der ganze multipart-Rumpf nur als Text dasteht. + # In der Word-Datei findet sich die aufgeschlüsselte Form, die der Browser dafür anzeigt. + # Erzeugung eines solchen Requests, siehe https://stackoverflow.com/a/12385661 + filename = os.path.basename(filepath) + content_type, encoding = mimetypes.guess_type(filename) + multipart_body = dict( + source='0', + collectionId=f'/group/{site_id(config["site_url"])}', + sakai_action='', + navRoot='', + criteria='title', + rt_action='', + selectedItemId='', + upload=(filename, slurp(filepath), content_type), + url='', + sakai_csrf_token=config[CSRF_TOKEN_CONFIGENTRY_NAME] + ) + multipart_body['from'] = 'list' # separate because 'from' is a keyword + requests.post(...) + + +def site_id(site_url: str) -> str: + """From e.g. https://qmycampus.imp.fu-berlin.de/portal/site/724a527a-db9d, retrieve 724a527a-db9d""" + path = urllib.parse.urlparse(site_url).path + the_id = os.path.basename(path) + return the_id + + +def slurp(filename: str) -> bytes: + with open(filename, 'rb') as f: + return f.read() if __name__ == '__main__': if len(sys.argv) != 2+1 or sys.argv[1] != 'create_multigroup_assgmt':