def find_existing(self, registry, title: str, check_front_matter=False, top_directories=None): # Get the path to the file safe_filename = self.make_markdown_filename(title) if not os.path.exists(safe_filename): if top_directories is None: top_directories = [registry.search_up_for_waltz_registry('./')] potential_files = [] for top_directory in top_directories: for root, dirs, files in os.walk(top_directory): for file in files: potential_file = os.path.join(root, file) if file == safe_filename: potential_files.append(potential_file) elif check_front_matter and file.endswith(".md"): _, waltz, _ = extract_front_matter( self.read(potential_file)) if waltz.get('title') == title: potential_files.append(potential_file) if len(potential_files) > 1: raise WaltzAmbiguousResource( "Ambiguous resource named {}:\n\t{}".format( safe_filename, "\n\t".join(potential for potential in potential_files)), potential_files) elif not potential_files: raise FileNotFoundError( "No resource named {} found.".format(safe_filename)) safe_filename, = potential_files return safe_filename
def list(self, registry, args): if args.category: category_names = registry.get_resource_category( args.category).category_names else: category_names = None rows = [] for root, dirs, files in os.walk(self.path): for name in natsorted(files): if name.endswith(".md"): path = os.path.join(root, name) decoded_markdown = self.read(path) try: regular, waltz, body = extract_front_matter( decoded_markdown) except: if category_names is None: rows.append( ("[invalid]", "", os.path.relpath(path))) continue resource = "[{}]".format(waltz.get('resource', 'unknown')) if category_names is None or waltz.get( 'resource') in category_names: rows.append( (resource, waltz.get("title", ""), os.path.relpath(path))) print(tabulate(rows, ("Resource", "Title", "Path")))
def encode_json(cls, registry: Registry, data: str, args): regular, waltz, body = extract_front_matter(data) settings = waltz.get('settings', {}) submission = settings.get('submission', {}) timing = settings.get('timing', {}) secrecy = settings.get('secrecy', {}) body = hide_data_in_html(regular, m2h(body)) return json.dumps({ 'name': waltz['title'], 'description': body, 'html_url': waltz['url'], 'published': waltz['published'], # General settings 'points_possible': settings['points_possible'], 'grading_type': settings['grading_type'], # Submissions 'allowed_extensions': submission.get('allowed_extensions'), 'submission_types': submission['submission_types'], # Timing 'due_at': from_friendly_date(timing['due_at']), 'unlock_at': from_friendly_date(timing['unlock_at']), 'lock_at': from_friendly_date(timing['lock_at']), # Secrecy 'anonymize_students': secrecy['anonymize_students'], 'anonymous_grading': secrecy['anonymous_grading'] })
def encode_json(cls, registry: Registry, data: str, args): regular, waltz, body = extract_front_matter(data) body = hide_data_in_html(regular, m2h(body)) return json.dumps({ 'title': waltz['title'], 'published': waltz['published'], 'body': body # TODO: Other fields })
def encode_question_by_title(cls, registry: Registry, title: str, args): local = registry.get_service(args.local_service, 'local') # TODO: By default limit search to "<Quiz> Questions/" folder? source_path = local.find_existing(registry, title, check_front_matter=True, top_directories=args.banks) decoded_markdown = local.read(source_path) regular, waltz, body = extract_front_matter(decoded_markdown) body = hide_data_in_html(regular, m2h(body)) waltz['question_text'] = body return cls.encode_question(registry, waltz, args)
def find_existing(self, registry, title: str, check_front_matter=False, top_directories=None, folder_file=None, extension='.md', args=None): # Get the path to the file if hasattr(args, 'filename') and args.filename and os.path.exists( args.filename): safe_filename = args.filename args.title = self.get_title(args.filename) else: safe_filename = self.make_markdown_filename( title, extension=extension, folder_file=folder_file) # Is the exact filepath here? if os.path.exists(safe_filename): return safe_filename # Ah, are we in the containing directory for the path? if os.path.exists(make_end_path(safe_filename)): return make_end_path(safe_filename) # Okay, search recursively from the .waltz file else: if top_directories is None: top_directories = [registry.search_up_for_waltz_registry('./')] potential_files = [] for top_directory in top_directories: for root, dirs, files in os.walk(top_directory): for file in files: potential_file = os.path.join(root, file) if all_path_parts_match(potential_file, safe_filename): potential_files.append(potential_file) elif check_front_matter and file.endswith(".md"): _, waltz, _ = extract_front_matter( self.read(potential_file)) if waltz.get('title') == title: potential_files.append(potential_file) if len(potential_files) > 1: raise WaltzAmbiguousResource( "Ambiguous resource named {}:\n\t{}".format( safe_filename, "\n\t".join(potential for potential in potential_files)), potential_files) elif not potential_files: raise FileNotFoundError( "No resource named {} found.".format(safe_filename)) safe_filename, = potential_files return safe_filename
def Push(args): registry = Registry.load(args.waltz_directory) resource_category = None if len(args.resource) == 1: local = registry.get_service('local', args.local_service) existing_file = local.find_existing(registry, args.resource[0], False, None) _, waltz, _ = extract_front_matter(local.read(existing_file)) if 'resource' in waltz: # TODO: validate resource category resource_category = registry.get_resource_category(waltz['resource']) args.category = waltz['resource'] args.title = args.resource[0] args.service = registry.get_service(resource_category.default_service).name if resource_category is None: resource_category = registry.guess_resource_category(args) resource_category.encode(registry, args) resource_category.upload(registry, args)
def diff_extra_files(cls, registry: Registry, data, args): local = registry.get_service(args.local_service, 'local') regular, waltz, body = extract_front_matter(data) for question in waltz['questions']: if isinstance(question, str): destination_path = local.find_existing( registry, args.title, folder_file=question, check_front_matter=True, top_directories=args.banks) yield destination_path, local.read(destination_path) elif 'group' in question: for inner_question in question['questions']: if isinstance(inner_question, str): destination_path = local.find_existing( registry, args.title, folder_file=inner_question) yield destination_path, local.read(destination_path)
def get_title(self, filename): data = self.read(filename) regular, waltz, body = extract_front_matter(data) filename_as_title = os.path.splitext(os.path.basename(filename))[0] return waltz.get('title', filename_as_title)
def encode_json(cls, registry: Registry, data: str, args): regular, waltz, body = extract_front_matter(data) # Grab out convenient groups visibility = waltz.get('visibility', {}) forked = waltz.get('forked', {}) identity = waltz.get('identity', {}) files = waltz.get('files', {}) # Grab any extra files extra_files = {} local = registry.get_service(args.local_service, 'local') for py_filename in ['on_run', 'starting_code', 'on_change', 'on_eval']: try: source_path = local.find_existing(registry, args.title, folder_file=py_filename, extension='.py') except FileNotFoundError: extra_files[py_filename] = "" continue extra_files[py_filename] = local.read(source_path) collected = {} for special, prepend in cls.SPECIAL_INSTRUCTOR_FILES_R.items(): for file in files.get(special, []): source_path = local.find_existing(registry, args.title, folder_file=file, extension="") collected[prepend + file] = local.read(source_path) if collected: extra_files['extra_instructor_files'] = json.dumps(collected) else: extra_files['extra_instructor_files'] = "" # And generate the rest of the JSON return json.dumps({ "_schema_version": 2, 'url': waltz['title'], 'name': waltz['display title'], 'type': waltz['type'], 'reviewed': waltz.get('human reviewed', False), 'hidden': visibility.get('hide status'), 'public': visibility.get('publicly indexed'), 'ip_ranges': visibility.get('ip ranges', ""), 'settings': json.dumps(waltz['additional settings']) if waltz['additional settings'] else None, 'forked_id': forked.get('id', None), 'forked_version': forked.get('version', None), 'owner_id': identity['owner id'], 'owner_id__email': identity['owner email'], 'course_id': identity['course id'], 'version': identity['version downloaded'], 'date_created': from_friendly_date(identity.get('created')), 'date_modified': from_friendly_date(identity.get('modified')), 'instructions': body, 'extra_starting_files': "", # TODO: Store sample submissions in BlockPy 'sample_submissions': [], # TODO: Store tags in BlockPy 'tags': [], **extra_files # TODO: Other fields })
def encode_json(cls, registry: Registry, data, args): regular, waltz, body = extract_front_matter(data) settings = waltz.get('settings', {}) timing = settings.get('timing', {}) secrecy = settings.get('secrecy', {}) body = hide_data_in_html(regular, m2h(body)) questions = [] groups = {} for question in waltz.get('questions', []): if isinstance(question, str): # Look up quiz question name questions.append( QuizQuestion.encode_question_by_title( registry, question, args)) elif 'group' in question: # This is a question group group = QuizGroup.encode_group(registry, question, args) groups[group['name']] = group questions.extend( QuizGroup.encode_questions(registry, question, args)) else: # This is an embedded question questions.append( QuizQuestion.encode_question(registry, question, args)) # TODO: total_estimated_points from the questions return json.dumps({ 'title': waltz['title'], 'published': waltz.get('published', False), 'description': body, # Settings 'quiz_type': settings.get('quiz_type', 'assignment'), 'points_possible': settings.get('points_possible'), 'allowed_attempts': settings.get('allowed_attempts'), 'scoring_policy': settings.get('scoring_policy'), # Timing 'due_at': from_friendly_date(timing.get('due_at')), 'unlock_at': from_friendly_date(timing.get('unlock_at')), 'lock_at': from_friendly_date(timing.get('lock_at')), # Secrecy 'one_question_at_a_time': int(secrecy.get('one_question_at_a_time', 0)), 'shuffle_answers': int(secrecy.get('shuffle_answers', 0)), 'time_limit': secrecy.get('time_limit'), 'cant_go_back': int(secrecy.get('cant_go_back', 0)), 'show_correct_answers': int(secrecy.get('show_correct_answers', 1)), 'show_correct_answers_last_attempt': secrecy.get('show_correct_answers_last_attempt'), 'show_correct_answers_at': secrecy.get('show_correct_answers_at'), 'hide_correct_answers_at': secrecy.get('hide_correct_answers_at'), 'hide_results': secrecy.get('hide_results'), 'one_time_results': int(secrecy.get('one_time_results', 0)), 'access_code': secrecy.get('access_code'), 'ip_filter': secrecy.get('ip_filter'), # Questions and Groups 'questions': questions, 'groups': groups })