Exemplo n.º 1
0
 def decode(cls, registry: Registry, args):
     local = registry.get_service(args.local_service, 'local')
     # TODO: use disambiguate
     if args.all:
         raw_resources = registry.find_all_resources(service=args.service,
                                                     category=cls.name)
     else:
         raw_resources = [
             registry.find_resource(title=args.title,
                                    service=args.service,
                                    category=cls.name)
         ]
     for raw_resource in raw_resources:
         try:
             destination_path = local.find_existing(registry,
                                                    raw_resource.title)
         except FileNotFoundError:
             destination_path = local.make_markdown_filename(
                 raw_resource.title)
             if args.destination:
                 destination_path = os.path.join(args.destination,
                                                 destination_path)
         decoded_markdown, extra_files = cls.decode_json(
             registry, raw_resource.data, args)
         local.write(destination_path, decoded_markdown)
         for path, data in extra_files:
             local.write(path, data)
Exemplo n.º 2
0
 def download(cls, registry: Registry, args):
     blockpy = registry.get_service(args.service, "blockpy")
     courses = blockpy.api.get('list/courses/')['courses']
     potentials = [
         course for course in courses if course['url'] == args.title
     ]
     if len(potentials) > 1:
         raise WaltzAmbiguousResource(
             "Too many courses with URL '{}'".format(args.title))
     elif not potentials:
         raise WaltzResourceNotFound(
             "No course with URL '{}' found.".format(args.title))
     bundle = blockpy.api.get('export/',
                              json={'course_id': potentials[0]['id']})
     # Assignments
     for assignment in bundle['assignments']:
         registry.store_resource(blockpy.name, 'problem', assignment['url'],
                                 "", json.dumps(assignment))
     # Memberships
     groups_assignments = {}
     for membership in bundle['memberships']:
         if membership['assignment_group_url'] not in groups_assignments:
             groups_assignments[membership['assignment_group_url']] = []
         groups_assignments[membership['assignment_group_url']].append(
             membership['assignment_url'])
     # Groups
     for group in bundle['groups']:
         group['problems'] = groups_assignments.get(group['url'], [])
         registry.store_resource(blockpy.name, 'blockpy_group',
                                 group['url'], "", json.dumps(group))
Exemplo n.º 3
0
 def download_all(cls, registry: Registry, args):
     canvas = registry.get_service(args.service, "canvas")
     local = registry.get_service('local')
     resources = canvas.api.get(cls.endpoint, retrieve_all=True)
     rows = []
     for resource in tqdm(natsorted(resources, key=cls.sort_resource)):
         try:
             path = local.find_existing(registry,
                                        resource[cls.title_attribute])
             rows.append(("Yes", "Yes", resource[cls.title_attribute],
                          os.path.relpath(path)))
         except WaltzAmbiguousResource as war:
             paths = "\n".join(
                 os.path.relpath(path) for path in war.args[0])
             rows.append(
                 ("Yes", "Multiple", resource[cls.title_attribute], paths))
         except FileNotFoundError:
             rows.append(("Yes", "No", resource[cls.title_attribute], ""))
         full_resource = canvas.api.get(cls.endpoint +
                                        str(resource[cls.id]))
         registry.store_resource(canvas.name, cls.name,
                                 resource[cls.title_attribute], "",
                                 json.dumps(full_resource))
     print(tabulate(rows, ('Remote', 'Local', 'Title', 'Path')))
     print("Downloaded", len(resources), cls.name_plural)
Exemplo n.º 4
0
 def encode(cls, registry: Registry, args):
     local = registry.get_service(args.local_service, 'local')
     source_path = local.find_existing(registry,
                                       args.title,
                                       folder_file=cls.folder_file)
     decoded_markdown = local.read(source_path)
     data = cls.encode_json(registry, decoded_markdown, args)
     registry.store_resource(args.service, cls.name, args.title, "", data)
Exemplo n.º 5
0
 def upload(cls, registry: Registry, args):
     canvas = registry.get_service(args.service, "canvas")
     raw_resource = registry.find_resource(title=args.title, service=args.service,
                                           category=args.category, disambiguate=args.url)
     full_page = json.loads(raw_resource.data)
     canvas.api.put("pages/{url}".format(url=full_page['title']), data={
         'wiki_page[title]': full_page['title'],
         'wiki_page[body]': full_page['body'],
         'wiki_page[published]': full_page['published']
     })
Exemplo n.º 6
0
def Init(args):
    if Registry.exists(args.directory):
        logging.warning("Existing Waltz registry in this directory.")
        if args.overwrite:
            Registry.delete(args.directory)
        else:
            return Registry.load(args.directory)
    registry = Registry.init(args.directory)
    registry.configure_service(Local(args.directory, {'path': args.directory}))
    registry.save_to_file()
    return registry
Exemplo n.º 7
0
 def upload(cls, registry: Registry, args):
     canvas = registry.get_service(args.service, "canvas")
     raw_resource = registry.find_resource(title=args.title, service=canvas.name,
                                           category=cls.name, disambiguate="")
     full_assignment = json.loads(raw_resource.data)
     assignment_data = cls._make_canvas_upload(registry, full_assignment, args)
     remote_assignment = cls.find(canvas, args.title)
     if remote_assignment is None:
         canvas.api.post('assignments/', data=assignment_data)
     else:
         canvas.api.put("assignments/{aid}".format(aid=remote_assignment['id']), data=assignment_data)
Exemplo n.º 8
0
 def upload(cls, registry: Registry, args):
     blockpy = registry.get_service(args.service, cls.default_service)
     raw_resource = registry.find_resource(title=args.title,
                                           service=args.service,
                                           category=args.category,
                                           disambiguate=args.url)
     full_data = json.loads(raw_resource.data)
     blockpy.api.post("import",
                      json={
                          'course_id': full_data['course_id'],
                          'assignments': [full_data]
                      })
Exemplo n.º 9
0
 def download(cls, registry: Registry, args):
     blockpy = registry.get_service(args.service, "blockpy")
     bundle = blockpy.api.get('export/',
                              json={'assignment_url': args.title})
     potentials = bundle['assignments']
     # Assignments
     if len(potentials) > 1:
         raise WaltzAmbiguousResource(
             f"Too many problems with URL '{args.title}'")
     elif not potentials:
         raise WaltzResourceNotFound(
             f"No problem with URL '{args.title}' found.")
     assignment = potentials[0]
     registry.store_resource(blockpy.name, 'problem', assignment['url'], "",
                             json.dumps(assignment))
Exemplo n.º 10
0
 def upload(cls, registry: Registry, args):
     canvas = registry.get_service(args.service, "canvas")
     # Get the local version
     raw_resource = registry.find_resource(title=args.title,
                                           service=args.service,
                                           category=args.category,
                                           disambiguate=args.id)
     local_quiz = json.loads(raw_resource.data)
     # Get the remote version
     remote_quiz = cls.find(canvas, args.title)
     # Either put or post the quiz
     if remote_quiz is None:
         cls.upload_new(registry, local_quiz, args)
     else:
         cls.upload_edit(registry, remote_quiz, local_quiz, args)
Exemplo n.º 11
0
 def decode_question(cls, registry: Registry, question, quiz, args):
     question_type = cls.TYPES[question['question_type']]
     if args.combine:
         raw = question_type.decode_json_raw(registry, question, args)
         raw['text'] = h2m(raw['text'])
         return raw, None, None
     local = registry.get_service(args.local_service, 'local')
     title = question['question_name']
     try:
         destination_path = local.find_existing(registry,
                                                args.title,
                                                folder_file=title)
     except FileNotFoundError:
         destination_path = local.make_markdown_filename(title)
         if args.banks:
             first_bank_path = args.banks[0].format(
                 title=make_safe_filename(title),
                 id=question['id'],
                 quiz_title=make_safe_filename(quiz['title']),
                 quiz_id=quiz['id'])
             destination_path = os.path.join(first_bank_path,
                                             destination_path)
         else:
             first_bank_path = make_safe_filename(quiz['title'])
             if args.destination:
                 destination_path = os.path.join(args.destination,
                                                 first_bank_path,
                                                 destination_path)
             else:
                 destination_path = os.path.join(first_bank_path,
                                                 destination_path)
     decoded_markdown = question_type.decode_json(registry, question, args)
     return title, destination_path, decoded_markdown
Exemplo n.º 12
0
 def upload_new(cls, registry: Registry, local_quiz, args):
     canvas = registry.get_service(args.service, "canvas")
     quiz_data = cls._make_canvas_upload(registry, local_quiz, args)
     created_quiz = canvas.api.post('quizzes/', data=quiz_data)
     if 'errors' in created_quiz:
         pprint(created_quiz['errors'])
         raise WaltzException("Error loading data, see above.")
     print("Created quiz", local_quiz['title'], "on canvas")
     # Create the groups
     group_name_to_id = {}
     for group in local_quiz['groups'].values():
         group_data = QuizGroup._make_canvas_upload(registry, group, args)
         created_group = canvas.api.post(
             'quizzes/{quiz_id}/groups'.format(quiz_id=created_quiz['id']),
             data=group_data)
         created_group = created_group['quiz_groups'][
             0]  # acbart: Weird response type
         # acbart: Okay because names are strings and IDs are ints
         group_name_to_id[created_group['name']] = created_group['id']
         group_name_to_id[created_group['id']] = created_group['id']
     if local_quiz['groups']:
         print("Created quiz", local_quiz['title'], "groups on canvas")
     # Create the questions
     for question in local_quiz['questions']:
         if 'quiz_group_id' in question and question[
                 'quiz_group_id'] is not None:
             question['quiz_group_id'] = group_name_to_id[
                 question['quiz_group_id']]
         question_data = QuizQuestion._make_canvas_upload(
             registry, question, args)
         created_question = canvas.api.post(
             'quizzes/{quiz_id}/questions'.format(
                 quiz_id=created_quiz['id']),
             data=question_data)
     print("Created quiz", local_quiz['title'], "questions on canvas")
Exemplo n.º 13
0
 def download(cls, registry: Registry, args):
     if args.all:
         cls.download_all(registry, args)
         return
     canvas = registry.get_service(args.service, "canvas")
     resource_json = cls.find(canvas, args.title)
     if resource_json is not None:
         try:
             registry.find_resource(canvas.name, cls.name, args.title, "")
             print("Downloaded new version of {}: ".format(cls.name),
                   args.title)
         except WaltzException:
             print("Downloaded new {}:".format(cls.name), args.title)
         resource_json = json.dumps(resource_json)
         registry.store_resource(canvas.name, cls.name, args.title, "",
                                 resource_json)
         return resource_json
     cls.find_similar(registry, canvas, args)
Exemplo n.º 14
0
def Upload(args):
    """
    > waltz upload "Programming 37: For Loops"
        If service/category not in registry database,
        Then we can go ask all the services if they already know about
        this thing.
    """
    registry = Registry.load(args.waltz_directory)
    resource_category = registry.guess_resource_category(args)
    resource_category.upload(registry, args)
Exemplo n.º 15
0
 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)
Exemplo n.º 16
0
def Encode(args):
    """
    > waltz encode "Programming 37: For Loops"
    > waltz encode canvas assignment "Programming 37: For Loops"

    If we found out the resource category, we can include that in the Registry Database.
        That might also allow us to infer the Service.

    """
    registry = Registry.load(args.waltz_directory)
    resource_category = registry.guess_resource_category(args)
    resource_category.encode(registry, args)
Exemplo n.º 17
0
 def diff_extra_files(cls, registry: Registry, data, args):
     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:
             yield py_filename, ""
             continue
         print(source_path)
         yield source_path, local.read(source_path)
Exemplo n.º 18
0
 def decode(cls, registry: Registry, args):
     course = registry.find_resource(title=args.title, service=args.service, category=cls.name)
     data = json.loads(course.data)
     original_destination = args.destination
     for group in data['groups']:
         custom_args = SimpleNamespace(**vars(args))
         custom_args.title = group
         BlockPyGroup.decode(registry, custom_args)
     for group, problems in data['problems'].items():
         for problem in problems:
             custom_args = SimpleNamespace(**vars(args))
             custom_args.title = problem
             custom_args.destination = group
             Problem.decode(registry, custom_args)
Exemplo n.º 19
0
def List(args):
    registry = Registry.load(args.waltz_directory)
    if not args.service:
        print("The following services are available:")
        for service_type, services in registry.services.items():
            if services:
                print("\t", service_type+":")
                for service in services:
                    print("\t\t", service.name)
            else:
                print("\t", service_type + ":", "(none configured)")
    else:
        service = registry.get_service(args.service)
        service.list(registry, args)
    return registry
Exemplo n.º 20
0
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)
Exemplo n.º 21
0
 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)
Exemplo n.º 22
0
def Download(args):
    """
    > waltz download --filename for_loops.md
    > waltz download "Programming 37: For Loops"
    > waltz download assignment "Final Exam"
    > waltz download canvas "Final Exam"
    > waltz download canvas assignment "Final Exam"
    > waltz download canvas --id 234347437743

    > waltz download <Name>
    > waltz download <Service> <Name>
    > waltz download <Resource> <Name>
    > waltz download <Service> <Resource> <Name>
    > waltz download --parameter <Value>

    > waltz download --all

    """
    registry = Registry.load(args.waltz_directory)
    resource_category = registry.guess_resource_category(args)
    resource_category.download(registry, args)
    return registry
Exemplo n.º 23
0
def handle_registry(args, registry):
    if registry is None:
        return Registry.load(args.registry_path)
    return registry
Exemplo n.º 24
0
def parse_command_line(args):
    parser = argparse.ArgumentParser(
        prog='waltz',
        description='Sync resources between services for a course')
    parser.add_argument(
        '--waltz_directory',
        type=str,
        default="./",
        help=
        "Path to the main waltz directory with the Waltz registry and DB file."
    )
    subparsers = parser.add_subparsers(help='Available commands')

    # Init Waltz
    parser_init = subparsers.add_parser('init',
                                        help='Initialize a new Waltz here')
    parser_init.add_argument(
        '--directory',
        "-d",
        type=str,
        default="./",
        help=
        "The local directory to use for this waltz; defaults to current directory."
    )
    parser_init.add_argument(
        '--overwrite',
        "-o",
        action="store_true",
        default=False,
        help="If used, then overwrites the existing waltz registry.")
    parser_init.set_defaults(func=actions.Init)

    # Reset Database
    parser_reset = subparsers.add_parser(
        'reset', help='Reset the Waltz database entirely.')
    parser_reset.set_defaults(func=actions.Reset)

    # Configure service
    parser_configure = subparsers.add_parser(
        'configure', help='Configure a new instance of the service.')
    parser_configure_services = parser_configure.add_subparsers(
        dest='type', help="The type of the service you are configuring.")
    for name, service_type in defaults.get_service_types().items():
        service_type.add_parser_configure(parser_configure_services)
    parser_configure.set_defaults(func=actions.Configure)

    # List Services or Resources
    parser_list = subparsers.add_parser(
        'list', help='List available services or resources')
    parser_list_services = parser_list.add_subparsers(
        dest='service', help="The service to search within.")
    for name, service_type in defaults.get_service_types().items():
        service_type.add_parser_list(parser_list_services)
    registry = Registry.load('./', False)
    if registry is not None:
        for name, services in registry.services.items():
            for service_type in services:
                service_type.add_parser_list(parser_list_services,
                                             service_type.name)
    parser_list.set_defaults(func=actions.List)

    # Show [Course|Service]

    # Search
    parser_search = subparsers.add_parser('search',
                                          help='Search for a resource.')
    parser_search.add_argument('category',
                               type=str,
                               help="The category of resource to search")
    parser_search.add_argument('what',
                               type=str,
                               help="The resource to download")
    parser_search.add_argument(
        "--service",
        type=str,
        help="The specific service to use in case of ambiguity.")
    parser_search.set_defaults(func=actions.Search)

    def add_id_and_url(subparser):
        subparser.add_argument(
            "--id",
            help=
            "A resource-specific ID to disambiguate this resource definitively."
        )
        subparser.add_argument(
            "--url",
            help=
            "A resource-specific URL to disambiguate this resource definitively."
        )
        subparser.add_argument("--all",
                               action='store_true',
                               help="Get all the resources of this type.")

    # Download
    parser_download = subparsers.add_parser(
        'download', help='Download the raw version of a resource.')
    parser_download.add_argument(
        'resource',
        nargs='+',
        type=str,
        help="The resource to download. Could be a "
        "resource title, filename, or some combination of those and the service and category."
    )
    add_id_and_url(parser_download)
    parser_download.set_defaults(func=actions.Download)

    # Upload
    parser_upload = subparsers.add_parser(
        'upload', help='Upload the raw version of a resource.')
    parser_upload.add_argument(
        'resource',
        nargs='+',
        type=str,
        help="The resource to download. Could be a "
        "resource title, filename, or some combination of those and the service and category."
    )
    add_id_and_url(parser_upload)
    parser_upload.set_defaults(func=actions.Upload)

    # Decode
    parser_decode = subparsers.add_parser(
        'decode', help='Convert a raw resource into a locally editable one.')
    parser_decode.add_argument(
        'resource',
        nargs='+',
        type=str,
        help="The resource to decode. Could be a "
        "filename, resource title, or some combination of those and the service and category."
    )
    parser_decode.add_argument(
        "--local_service",
        type=str,
        help="The specific local service to use as an override.")
    parser_decode.add_argument(
        "--destination",
        "-d",
        type=str,
        help="The destination directory for this resource.")
    parser_decode.add_argument(
        "--combine",
        "-c",
        action='store_true',
        default=False,
        help="Whether to combine all subresources into a single file.")
    parser_decode.add_argument(
        "--hide_answers",
        action='store_true',
        default=False,
        help="Whether to hide answers to any questions.")
    parser_decode.add_argument(
        "--banks",
        nargs="*",
        type=str,
        help=
        "The question bank folders to check. First one will be the location for new questions."
    )
    add_id_and_url(parser_decode)
    # TODO: Allow override of specific local, but otherwise assume default `local`?
    parser_decode.set_defaults(func=actions.Decode)

    # Encode
    parser_encode = subparsers.add_parser(
        'encode', help='Convert a locally editable resource into a raw one.')
    parser_encode.add_argument(
        'resource',
        nargs='+',
        type=str,
        help="The resource to encode. Could be a "
        "filename, resource title, or some combination of those and the service and category."
    )
    parser_encode.add_argument(
        "--local_service",
        type=str,
        help="The specific local service to use as an override.")
    parser_encode.add_argument("--banks",
                               nargs="*",
                               type=str,
                               help="The question bank folders to check.")
    add_id_and_url(parser_encode)
    parser_encode.set_defaults(func=actions.Encode)

    # Diff
    parser_diff = subparsers.add_parser(
        'diff',
        help='Compare the remote version of a resource and the local one.')
    parser_diff.add_argument(
        'resource',
        nargs='+',
        type=str,
        help="The resource to diff. Could be a "
        "filename, resource title, or some combination of those and the service and category."
    )
    parser_diff.add_argument(
        "--local_service",
        type=str,
        help="The specific local service to use as an override.")
    parser_diff.add_argument(
        "--console",
        action="store_true",
        help="Do not generate HTML file; just print to console.")
    parser_diff.add_argument(
        "--prevent_open",
        action="store_true",
        help=
        "Prevent the generated HTML file from being automatically opened in your browser."
    )
    parser_diff.add_argument("--banks",
                             nargs="*",
                             type=str,
                             help="The question bank folders to check.")
    parser_diff.add_argument(
        "--combine",
        "-c",
        action='store_true',
        default=False,
        help="Whether to combine all subresources into a single file.")
    parser_diff.add_argument("--hide_answers",
                             action='store_true',
                             default=False,
                             help="Whether to hide answers to any questions.")
    add_id_and_url(parser_diff)
    parser_diff.set_defaults(func=actions.Diff)

    # Push
    parser_push = subparsers.add_parser(
        'push',
        help='Convert a locally editable resource into a raw one and upload it.'
    )
    parser_push.add_argument(
        'resource',
        nargs='+',
        type=str,
        help="The resource to encode and upload. Could be a "
        "filename, resource title, or some combination of those and the service and category."
    )
    parser_push.add_argument(
        "--local_service",
        type=str,
        help="The specific local service to use as an override.")
    parser_push.add_argument("--banks",
                             nargs="*",
                             type=str,
                             help="The question bank folders to check.")
    add_id_and_url(parser_push)
    parser_push.set_defaults(func=actions.Push)

    # Pull
    parser_pull = subparsers.add_parser(
        'pull',
        help='Download a raw resource and convert it to a locally editable one.'
    )
    parser_pull.add_argument(
        'resource',
        nargs='+',
        type=str,
        help="The resource to download and decode. Could be a "
        "filename, resource title, or some combination of those and the service and category."
    )
    parser_pull.add_argument(
        "--local_service",
        type=str,
        help="The specific local service to use as an override.")
    parser_pull.add_argument("--banks",
                             nargs="*",
                             type=str,
                             help="The question bank folders to check.")
    parser_pull.add_argument(
        "--combine",
        "-c",
        action='store_true',
        default=False,
        help="Whether to combine all subresources into a single file.")
    parser_pull.add_argument("--hide_answers",
                             action='store_true',
                             default=False,
                             help="Whether to hide answers to any questions.")
    parser_pull.add_argument(
        "--destination",
        "-d",
        type=str,
        help="The destination directory for this resource.")
    add_id_and_url(parser_pull)
    parser_pull.set_defaults(func=actions.Pull)

    # Extract

    # Build

    # Undo

    # ... Conclude!
    parsed = parser.parse_args(args)
    return parsed.func(parsed)
Exemplo n.º 25
0
def Pull(args):
    registry = Registry.load(args.waltz_directory)
    resource_category = registry.guess_resource_category(args)
    resource_category.download(registry, args)
    resource_category.decode(registry, args)
Exemplo n.º 26
0
def Diff(args):
    registry = Registry.load(args.waltz_directory)
    resource_category = registry.guess_resource_category(args)
    resource_category.diff(registry, args)
Exemplo n.º 27
0
def Reset(args):
    registry = Registry.load(args.waltz_directory)
    registry.reset_database()
    registry.save_to_file()
    return registry
Exemplo n.º 28
0
def Configure(args):
    registry = Registry.load(args.waltz_directory)
    new_service = Service.from_type(args.type).configure(args)
    registry.configure_service(new_service)
    registry.save_to_file()
    return registry
Exemplo n.º 29
0
    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
        })
Exemplo n.º 30
0
 def decode_json(cls, registry: Registry, data: str, args):
     raw_data = json.loads(data)
     result = CommentedMap()
     result['title'] = raw_data['url']
     result['display title'] = raw_data['name']
     result['resource'] = cls.name
     result['type'] = raw_data['type']
     if raw_data['reviewed']:
         result['human reviewed'] = raw_data['reviewed']
     result['visibility'] = CommentedMap()
     result['visibility']['hide status'] = raw_data['hidden']
     result['visibility']['publicly indexed'] = raw_data['public']
     if raw_data['ip_ranges']:
         result['visibility']['ip ranges'] = raw_data['ip_ranges']
     result['additional settings'] = json.loads(raw_data['settings']
                                                or "{}")
     if raw_data['forked_id']:
         result['forked'] = CommentedMap()
         # TODO: Look up forked's url for more info; or perhaps automatically have it downloaded along?
         result['forked']['id'] = raw_data['forked_id']
         result['forked']['version'] = raw_data['forked_version']
     result['identity'] = CommentedMap()
     result['identity']['owner id'] = raw_data['owner_id']
     result['identity']['owner email'] = raw_data['owner_id__email']
     result['identity']['course id'] = raw_data['course_id']
     result['identity']['version downloaded'] = raw_data['version']
     result['identity']['created'] = to_friendly_date_from_datetime(
         blockpy_string_to_datetime(raw_data['date_created']))
     result['identity']['modified'] = to_friendly_date_from_datetime(
         blockpy_string_to_datetime(raw_data['date_modified']))
     # TODO: Tags
     # TODO: Sample Submissions. Have a "samples/" folder?
     # TODO: If args.combine, then put it all into one file
     files_path = raw_data['url']
     result['files'] = CommentedMap()
     result['files']['path'] = files_path
     result['files']['hidden but accessible files'] = []
     result['files']['instructor only files'] = []
     result['files']['extra starting files'] = []
     result['files']['read-only files'] = []
     # Check if index file exists; if so, that's our directory target
     local = registry.get_service(args.local_service, 'local')
     try:
         index_path = local.find_existing(registry,
                                          files_path,
                                          folder_file=cls.folder_file)
         files_path = os.path.dirname(index_path)
     except FileNotFoundError:
         pass
     if hasattr(args, 'destination') and args.destination:
         files_path = os.path.join(args.destination, files_path)
     # Then build up the extra instructor files
     extra_files = [(os.path.join(files_path,
                                  "on_run.py"), raw_data['on_run']),
                    (os.path.join(files_path, "starting_code.py"),
                     raw_data['starting_code'])]
     if raw_data['on_change']:
         extra_files.append(
             (os.path.join(files_path,
                           "on_change.py"), raw_data['on_change']))
     if raw_data['on_eval']:
         extra_files.append(
             (os.path.join(files_path, "on_eval.py"), raw_data['on_eval']))
     if raw_data['extra_instructor_files']:
         # TODO: Create special manifest file for listing special file types (e.g., "&" and "?")
         extra_instructor_files = json.loads(
             raw_data['extra_instructor_files'])
         for eif_filename, eif_contents in extra_instructor_files.items():
             if eif_filename[0] in "?!^&*":
                 new_path = os.path.join(files_path, eif_filename[1:])
                 extra_files.append((new_path, eif_contents))
                 special_file_type = cls.SPECIAL_INSTRUCTOR_FILES[
                     eif_filename[0]]
                 result['files'][special_file_type].append(new_path)
     # Put instructions up front and return the result
     return add_to_front_matter(raw_data['instructions'],
                                result), extra_files