From 3ccce4cd3448a49015187df26c45cf9198360e7a Mon Sep 17 00:00:00 2001 From: John Hui Date: Mon, 24 Jun 2019 14:21:18 -0400 Subject: [PATCH] un hardcode the URL opttion + bug fix (#12) * fixed case sensitivity issue in boatwain * updated url option * fixes --- README.md | 17 +++++++++++++++ boatswain_env.py | 53 +++++++++++++++++++++++++++++++++++++--------- canvas-wrangler.py | 4 +--- interactive.py | 26 +++++++++++------------ 4 files changed, 74 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 9078545..7a0a945 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,23 @@ Boatswain will be expanded to support arbitrary arguments in this config file which could save you some typing when running commands, but for now just accept that it's a little overkill. +If you don't need either one of Canvas or GitHub integration, you can just +answer "no" when it asks if you'd like to configure one or the other. If you +answer "yes", make sure you have the respective auth tokens ready. + +When configuring Canvas, it will also ask for a URL. You should pass in the URL +that corresponds to the host name where Canvas is hosted. For example, for +Columbia, you should enter: + + https://courseworks2.columbia.edu/ + +If you are using a version of Canvas Wrangler from before May 14, 2019, you may +also add the url configuration to your `boatswain.ini` manually, e.g.: + + [canvas] + token = + url = https://courseworks2.columbia.edu/ + Canvas Wrangler =============== diff --git a/boatswain_env.py b/boatswain_env.py index cc18e92..e6da2ad 100755 --- a/boatswain_env.py +++ b/boatswain_env.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 import configparser import os @@ -10,6 +10,7 @@ CANVAS_SECTION = 'canvas' CANVAS_TOKEN = 'token' +CANVAS_URL = 'url' GITHUB_SECTION = 'github' GITHUB_TOKEN = 'token' @@ -33,6 +34,9 @@ def __init__(self, path=DEFAULT_INI_PATH): def canvasToken(self): return self.get(CANVAS_SECTION, CANVAS_TOKEN) + def canvasUrl(self): + return self.get(CANVAS_SECTION, CANVAS_URL) + def githubToken(self): return self.get(GITHUB_SECTION, GITHUB_TOKEN) @@ -50,6 +54,11 @@ def canvasToken(self): return self.config.canvasToken() return self.canvas_token + def canvasUrl(self): + if self.canvas_url is None: + return self.config.canvasUrl() + return self.canvas_url + def githubToken(self): if self.github_token is None: return self.config.githubToken() @@ -161,9 +170,6 @@ def ParseOption( help='debug mode; enable all output', ) - - - config = BoatswainConfig(config_path) if req_canvas: @@ -175,15 +181,36 @@ def ParseOption( help='override Boatswain Canvas token', metavar='', ) - # handle incomplete config; don't handle NoSectionError because we - # assume that the canvas and github sections at least exist except configparser.NoOptionError: + print('[WARN]: You appear to be missing a Canvas token in your ' + + 'Boatswain configuration ({}). Please add this information ' + + 'to your config file under section [canvas] with key "token".' + + '\n'.format(config_path)) parser.add_argument('canvas_token', type=str, help='Canvas LMS auth token', metavar='', ) + try: + canvasUrl = config.canvasUrl() + parser.add_argument('--canvas-url', + default=canvasUrl, + type=str, + help='override Boatswain Canvas URL', + metavar='', + ) + except configparser.NoOptionError: + print('[WARN]: You appear to be missing the Canvas URL in your ' + + 'Boatswain configuration ({}). Please add this information ' + + 'to your config file under section [canvas] with key "url".' + + '\n'.format(config_path)) + parser.add_argument('canvas_url', + type=str, + help='Canvas LMS URL', + metavar='', + ) + if req_github: try: githubToken = config.githubToken() @@ -193,9 +220,11 @@ def ParseOption( help='override Boatswain GitHub token', metavar='', ) - # handle incomplete config; don't handle NoSectionError because we - # assume that the canvas and github sections at least exist except configparser.NoOptionError: + print('[WARN]: You appear to be missing a GitHub token in your ' + + 'Boatswain configuration ({}). Please add this information ' + + 'to your config file under section [github] with key "token".' + + '\n'.format(config_path)) parser.add_argument('github_token', type=str, help='GitHub auth token', @@ -241,15 +270,19 @@ def newPopulatedConfigInteractive(): config.add_section(CANVAS_SECTION) config.add_section(GITHUB_SECTION) - i = itv.promptSelect('Configure Canvas token?', ['n'], default='y') + i = itv.promptSelect('Configure Canvas?', ['n'], default='y') if i == 'y': itv.output('You can generate a new Canvas token by going to your ' 'Canvas Profile -> Settings -> Approved Integrations ' 'and clicking on "+New Access Token"') + + canvasUrl = itv.promptInput('Enter Canvas URL') + config.set(CANVAS_SECTION, CANVAS_URL, canvasUrl) + canvasToken = itv.promptInput('Enter Canvas auth token') config.set(CANVAS_SECTION, CANVAS_TOKEN, canvasToken) - i = itv.promptSelect('Configure GitHub token?', ['n'], default='y') + i = itv.promptSelect('Configure GitHub?', ['n'], default='y') if i == 'y': githubToken = itv.promptInput('Enter GitHub auth token') config.set(GITHUB_SECTION, GITHUB_TOKEN, githubToken) diff --git a/canvas-wrangler.py b/canvas-wrangler.py index f4e43c3..c36b6f3 100755 --- a/canvas-wrangler.py +++ b/canvas-wrangler.py @@ -141,9 +141,7 @@ def wrangle_canvas(opt): grade_data = build_grade_data(grades, student_i, grade_i, comment_i, opt) - canvasURL = 'https://courseworks2.columbia.edu/' # TODO: don't hard - - canvas = Canvas(canvasURL, opt.canvasToken()) + canvas = Canvas(opt.canvasUrl(), opt.canvasToken()) course = canvas.get_course(opt.course_id) assignment = course.get_assignment(opt.assignment_id) diff --git a/interactive.py b/interactive.py index 6584601..9818b58 100644 --- a/interactive.py +++ b/interactive.py @@ -17,7 +17,6 @@ def promptInput(prompt, fmt='', default=''): while True: inp = input(promptf) - inp = inp.lower() if inp != '': return inp elif default != '' and inp == '': @@ -35,6 +34,18 @@ def promptValidate(prompt, validator, fmt='', default=''): output('Invalid input (case-insensitive): {}'.format(v)) +def selectorValidator(options, default=''): + def validator(inp): + i = inp.lower() + if default != '': + if i == default.lower() or i == '': + return '' + if i in options: + return '' + return i + return validator + + ''' e.g. promptInput('Do you want to continue?', ['n'], default='y') input with validation @@ -52,18 +63,7 @@ def promptSelect(prompt, alts, default=''): validator = selectorValidator(options, default=default) inp = promptValidate(prompt, validator, fmt=fmt, default=default) - return inp - - -def selectorValidator(options, default=''): - def validator(inp): - if default != '': - if inp == default.lower() or inp == '': - return '' - if inp in options: - return '' - return inp - return validator + return inp.lower() def newFileValidator():