コード例 #1
0
def pull(db, course_, quiz_, dry_run):
    course_id = course_.canvas_id
    quiz_id = quiz_.get('id')
    if not quiz_id:
        logging.error(f"Quiz {quiz_id} does not exist for course {course_id}")
        return None, None

    cid = canvas_id.find_by_id(db, course_id, quiz_id)
    if cid:
        quiz_['filename'] = cid.filename
    else:
        quiz_['filename'] = component.gen_filename(QUIZZES_DIR,
                                                   quiz_.get('title', ''))
        cid = canvas_id.CanvasID(quiz_['filename'], course_id)
        cid.canvas_id = quiz_.get('id')
        cid.save(db)

    # check assignment_group_id to fill in assignment_group by name
    agid = quiz_.get('assignment_group_id')
    if agid:
        # first check if we have a cid for the assignment group
        agcid = canvas_id.find_by_id(db, course_id, agid)
        if agcid:
            ag = helpers_yaml.read(agcid.filename)
            if ag:
                quiz_['assignment_group'] = ag.name
            else:
                logging.error("failed to find the assignment group for "
                              f"the assignment group with id {agid}. Your "
                              ".easeldb may be out of sync")
        else:
            # we could look at all the local assignment group files if we
            # don't have a cid for it but chances are there isn't a file.
            # so might as well just go back to canvas and ask for it
            agpath = assignment_group.ASSIGN_GROUP_PATH.format(course_id, agid)
            r = helpers.get(agpath, dry_run=dry_run)
            if 'name' in r:
                quiz_['assignment_group'] = r['name']
            else:
                logging.error("TODO: invalid response from canvas for "
                              "the assignment group: " +
                              json.dumps(r, indent=4))

    # quiz questions
    quiz_questions_path = QUIZ_PATH.format(course_.canvas_id,
                                           quiz_id) + "/questions"
    quiz_questions = helpers.get(quiz_questions_path)
    quiz_['quiz_questions'] = quiz_questions

    return Quiz.build(quiz_), cid
コード例 #2
0
 def pull(self, db, course_, dry_run):
     path = NAV_TABS_PATH.format(course_.canvas_id)
     resp = helpers.get(path, dry_run=dry_run)
     nav_tabs = []
     for nav_tab in resp:
         nav_tabs.append(NavigationTab(**nav_tab))
     return NavigationTabs(nav_tabs)
コード例 #3
0
ファイル: component.py プロジェクト: renquinn/easel-py
 def pull(self, db, course_, dry_run):
     cid = canvas_id.CanvasID(self.filename, course_.canvas_id)
     cid.find_id(db)
     path = self.format_update_path(db, course_.canvas_id, cid.canvas_id)
     resp = helpers.get(path, dry_run=dry_run)
     remote = self.__class__.build(resp)
     remote.filename = self.filename
     return remote
コード例 #4
0
def pull_all(db, course_, dry_run):
    r = helpers.get(PAGES_PATH.format(course_.canvas_id), dry_run=dry_run)
    pages = []
    print("pulling page contents")
    for p in tqdm(r):
        page_, _ = pull_page(db, course_.canvas_id, p.get('url'), dry_run)
        pages.append(page_)
    return pages
コード例 #5
0
def pull_all(db, course_, dry_run):
    r = helpers.get(QUIZZES_PATH.format(course_.canvas_id), dry_run=dry_run)
    quizzes = []
    print("pulling quiz questions")
    for quiz_json in tqdm(r):
        quiz_, _ = pull(db, course_, quiz_json, dry_run)
        if quiz_:
            quizzes.append(quiz_)
    return quizzes
コード例 #6
0
ファイル: assignment.py プロジェクト: renquinn/easel-py
def pull(db, course_, assignment_id, dry_run):
    course_id = course_.canvas_id
    a = helpers.get(ASSIGNMENT_PATH.format(course_id,
        assignment_id), dry_run=dry_run)
    if not a.get('id'):
        logging.error(f"Assignment {assignment_id} does not exist for course {course_id}")
        return None, None
    cid = canvas_id.find_by_id(db, course_id, a.get('id'))
    if cid:
        a['filename'] = cid.filename
    else:
        a['filename'] = component.gen_filename(ASSIGNMENTS_DIR, a.get('name',''))
        cid = canvas_id.CanvasID(a['filename'], course_id)
        cid.canvas_id = a.get('id')
        cid.save(db)

    # check assignment_group_id to fill in assignment_group by name
    agid = a.get('assignment_group_id')
    if agid:
        # first check if we have a cid for the assignment group
        agcid = canvas_id.find_by_id(db, course_id, agid)
        if agcid:
            ag = helpers_yaml.read(agcid.filename)
            if ag:
                a['assignment_group'] = ag.name
            else:
                logging.error("failed to find the assignment group for "
                        f"the assignment group with id {agid}. Your "
                        ".easeldb may be out of sync")
        else:
            # we could look at all the local assignment group files if we
            # don't have a cid for it but chances are there isn't a file.
            # so might as well just go back to canvas and ask for it
            agpath = assignment_group.ASSIGN_GROUP_PATH.format(course_id, agid)
            r = helpers.get(agpath, dry_run=dry_run)
            if 'name' in r:
                a['assignment_group'] = r['name']
            else:
                logging.error("TODO: invalid response from canvas for "
                        "the assignment group: " + json.dumps(r, indent=4))

    return Assignment.build(a), cid
コード例 #7
0
def pull_page(db, course_id, page_url, dry_run):
    page_ = helpers.get(PAGE_PATH.format(course_id, page_url), dry_run=dry_run)
    cid = canvas_id.find_by_id(db, course_id, page_.get('url'))
    if cid:
        page_['filename'] = cid.filename
    else:
        page_['filename'] = component.gen_filename(PAGES_DIR,
                                                   page_.get('title', ''))
        cid = canvas_id.CanvasID(page_['filename'], course_id)
        cid.canvas_id = page_.get('url')
        cid.save(db)
    return Page.build(page_), cid
コード例 #8
0
ファイル: assignment.py プロジェクト: renquinn/easel-py
def pull_all(db, course_, dry_run):
    r = helpers.get(ASSIGNMENTS_PATH.format(course_.canvas_id),
            dry_run=dry_run)
    assignments = []
    print("pulling assignment contents")
    for assignment in tqdm(r):
        # skip quizzes (handle in quiz.py)
        if assignment.get("is_quiz_assignment"):
            continue

        assignment_, _ = pull(db, course_, assignment.get('id'), dry_run)
        if assignment_:
            assignments.append(assignment_)
    return assignments
コード例 #9
0
ファイル: files.py プロジェクト: renquinn/easel-py
def pull_all(db, course_, dry_run):
    r = helpers.get(COURSE_FOLDERS_PATH.format(course_.canvas_id),
                    dry_run=dry_run)
    for folder in r:
        path = folder['full_name'][len("course "):]
        print(f"pulling list of files in {path}")
        os.makedirs(path, exist_ok=True)
        files_path = urllib.parse.urlparse(folder['files_url']).path
        r = helpers.get(files_path, dry_run=dry_run)
        print(f"downloading files in {path}")
        for file_ in tqdm(r):
            if 'filename' not in file_ or 'url' not in file_ or 'id' not in file_:
                logging.error("Invalid file response from canvas:")
                print(file_)
                continue
            filepath = path + '/' + file_['filename']
            pull_file(db, course_.canvas_id, file_['url'], file_['id'],
                      filepath)
    # normally we'd return a list of file objects for diffing the local ones,
    # but since we don't track files in the db we won't be able to diff
    # anything (in the the way it's done in commands.py)
    # TODO: but it might be worth returning the contents of the files, although
    # we'd want to optimize for memory usage in case of large files
    return []
コード例 #10
0
def pull_all(db, course_, dry_run):
    r = helpers.get(ASSIGN_GROUPS_PATH.format(course_.canvas_id),
                    dry_run=dry_run)
    ags = []
    for ag in tqdm(r):
        cid = canvas_id.find_by_id(db, course_.canvas_id, ag.get('id'))
        if cid:
            ag['filename'] = cid.filename
        else:
            ag['filename'] = component.gen_filename(ASSIGN_GROUPS_DIR,
                                                    ag.get('name', ''))
            cid = canvas_id.CanvasID(ag['filename'], course_.canvas_id)
            cid.canvas_id = ag.get('id')
            cid.save(db)
        ags.append(AssignmentGroup.build(ag))
    return ags
コード例 #11
0
ファイル: commands.py プロジェクト: renquinn/easel-py
def cmd_login(db, args):
    hostname = args.hostname
    token = args.token

    protocol = "https://"
    if hostname.startswith(protocol):
            hostname = hostname[len(protocol):]
    if hostname.endswith("/"):
            hostname = hostname[:len(hostname)-1]

    if not helpers.write_config(hostname, token, args.dry_run):
        return

    # confirm user logged in correctly
    resp = helpers.get("/api/v1/users/self")
    if resp and isinstance(resp, dict) and "name" in resp and resp["name"]:
        print("Found user:"******"There was an issue logging you in. Check the hostname "
                "of your canvas instance as well as your token.")
        logging.debug(resp)
コード例 #12
0
    def postprocess(self, db, course_, dry_run):
        course_id = course_.canvas_id
        cid = canvas_id.CanvasID(self.filename, course_id)
        cid.find_id(db)
        if not cid.canvas_id:
            print(f"failed to add QuizQuestions to {self}, we don't "
                  "have a canvas id for it")
            print("make sure the quiz has first been pushed to Canvas")
            return

        # delete any existing questions on the quiz
        # we'll use the local file as the source of truth and always update
        # canvas to match it
        quiz_path = QUIZ_PATH.format(course_id, cid.canvas_id)
        questions_path = quiz_path + "/questions"
        quiz_questions = helpers.get(questions_path, dry_run)
        for question in quiz_questions:
            if 'id' in question:
                path = questions_path + "/{}".format(question['id'])
                helpers.delete(path)

        # prepare actual QuizQuestion objects to be pushed to canvas
        questions = build_questions(self.quiz_questions)

        # push the questions
        for question in questions:
            print(f"\tpushing {question} to Quiz {self}")
            question.push(db, course_, dry_run, parent_component=self)

        # once I push the questions, canvas doesn't seem to update the
        # quiz's points possible until I save the entire quiz again...
        # https://community.canvaslms.com/t5/Question-Forum/Saving-Quizzes-w-API/td-p/226406
        # turns out that canvas won't do this if the quiz is unpublished when
        # you create the questions. so I'm hackily unpublishing and then
        # publishing (if the user wants to publish it)
        if self.remember_published:
            helpers.put(quiz_path, {"quiz": {"published": True}})
コード例 #13
0
def pull(db, course_id):
    params = {"include[]": "syllabus_body"}
    response = helpers.get(COURSE_PATH.format(course_id), params=params)
    response["canvas_id"] = response["id"]
    return Course(response["id"], response["name"], response["course_code"],
                  response["workflow_state"], response["syllabus_body"])
コード例 #14
0
def pull_all(db, course_, dry_run):
    modules = []
    m = helpers.get(MODULES_PATH.format(course_.canvas_id),
            params={'include': ['items']}, dry_run=dry_run)
    for module_ in m:
        if 'items' not in module_ or not module_['items']:
            items_path = urllib.parse.urlparse(module_['items_url']).path
            module_['items'] = helpers.get(items_path, dry_run=dry_run)
        cid = canvas_id.find_by_id(db, course_.canvas_id, module_.get('id'))
        if cid:
            module_['filename'] = cid.filename
        else:
            module_['filename'] = component.gen_filename(MODULES_DIR,
                    str(module_.get('position')) + '-' + module_.get('name',''))
            cid = canvas_id.CanvasID(module_['filename'], course_.canvas_id)
            cid.canvas_id = module_.get('id')
            cid.save(db)

        items = {}
        for item in module_['items']:
            item_id = 0
            if item['type'] == "Page":
                item_id = item['page_url']
            elif item['type'] in ['File', 'Assignment', 'Quiz']:
                item_id = item['content_id']
            # SubHeaders and ExternalUrls won't be in the db anyway
            icid = canvas_id.find_by_id(db, course_.canvas_id, item_id)
            if item_id == 0 or not icid:
                # we don't have the item locally so try to pull it
                if item['type'] == "Page":
                    _, icid = page.pull_page(db, course_.canvas_id,
                            item['page_url'], dry_run)
                elif item['type'] == "File":
                    # get file info
                    file_path = urllib.parse.urlparse(item['url']).path
                    file_ = helpers.get(file_path, dry_run=dry_run)
                    url = file_['url']
                    # get parent folder info
                    folder_path = files.COURSE_FOLDERS_PATH.format(course_.canvas_id)+"/"+str(file_['folder_id'])
                    folder = helpers.get(folder_path, dry_run=dry_run)
                    path = folder['full_name'][len("course "):]
                    filepath = path + file_['filename']
                    # download the file
                    icid = files.pull_file(db, course_.canvas_id, url, file_['id'], filepath)
                elif item['type'] == 'Assignment':
                    _, icid = assignment.pull(db, course_, item_id, dry_run)
                elif item['type'] == 'Quiz':
                    _, icid = quiz.pull(db, course_, item, dry_run)
                elif item['type'] == 'SubHeader':
                    built_item = {'title': item['title']}
                    if not item['published']:
                        built_item['published'] = False
                elif item['type'] == 'ExternalUrl':
                    built_item = {
                        'title': item.get('title', ''),
                        'external_url': item['external_url']}
                    if not item['published']:
                        built_item['published'] = False
                else:
                    logging.warn("I can't find the module item "
                            f"'{item['title']}' locally and I couldn't figure "
                            "out how to pull it because it is not an "
                            "easel-supported type: " + item['type'])
                    continue

            position = item.get('position', 0)
            if position not in items:
                items[position] = []
            if icid:
                built_item = {"item": icid.filename, "indent": item.get('indent', 0)}
                if item['type'] == 'File' and 'title' in item:
                    built_item['title'] = item['title']
            if 'indent' in built_item and built_item['indent'] == 0:
                del built_item['indent']
            items[position].append(built_item)

        # order items by position
        all_items = []
        for position in sorted(items.keys()):
            if position == 0:
                # we used 0 as default above so skip that for now
                continue
            all_items += items[position]
        # add positionless items to the end
        module_['items'] = all_items + items.get(0, [])
        modules.append(Module.build(module_))
    return modules