Exemplo n.º 1
0
    def handle(self, *args, **options):
        fn = options['data.yaml']
        basedir = os.path.dirname(fn)

        # Pre-process command-line arguments.
        self.load_startapps_catalog(options)

        # Open the end-user data file in a manner that we can.
        # update it. If --init is used, then instantiate a
        # new file for a new app.
        default = None
        if options["init"]:
            # If --init is given, allow creating a new file.
            default = {}
        with rtyaml.edit(fn, default=default) as data:
            if not isinstance(data, dict):
                raise ValueError("File does not contain a YAML mapping.")

            # If --init is given, it holds a path to an app.
            # Construct a new YAML file with an empty organization
            # block and an app pointing to the given app.
            if options["init"]:
                if data:
                    raise ValueError(
                        "--init cannot be used if file is not empty.")
                data["organization"] = {"name": None}
                data["app"] = os.path.relpath(
                    options["init"], basedir
                )  # compute path relative to the output file, not the current working directory

            # Run the file.
            self.AssembleApp(data, basedir, options)
Exemplo n.º 2
0
    def handle(self, *args, **options):
        if not options["appsource"]:
            # Show valid appsources.
            print("Specify one of the following app sources plus an app name:")
            for appsrc in AppSource.objects.all():
                if appsrc.spec["type"] == "local" and appsrc.spec.get("path"):
                    print(appsrc.slug, "(path: " + appsrc.spec["path"] + ")")
            print("")
            print("Or create a new AppSource using '--path path/to/apps appsource'.")

        elif options["path"]:
            if options["appname"]:
                print("You cannot use --path together with an appname.")
                return

            # Make directory if it doesn't exist.
            os.makedirs(options["path"], exist_ok=True)

            # Create a new AppSource.
            appsrc = AppSource.objects.create(
                slug=options["appsource"],
                spec={ "type": "local", "path": options["path"] }
            )

            print("Created new AppSource", appsrc, "using local path", options["path"])

        elif not options["appname"]:
            print("You must specify an app name, which should be a valid directory name (i.e. no spaces), or  or --path to create a new AppSource.")

        else:
            # Ok do the real work.

            appsrc = AppSource.objects.get(slug=options["appsource"])

            # Do we have a path?
            if not appsrc.spec.get("path"):
                print("AppSource does not have a local path specified!")
                return

            # What's the path to the app?
            path = os.path.join(appsrc.spec["path"], options["appname"])

            # Does this app already exist?
            if os.path.exists(path):
                print("An app with that name already exists.")
                return

            # Copy stub files.
            guidedmodules_path = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
            shutil.copytree(os.path.join(guidedmodules_path, "stubs", "q-files","stub_app"), path, copy_function=shutil.copy)

            # Edit the app title.
            with rtyaml.edit(os.path.join(path, "app.yaml")) as app:
                app['title'] = options["appname"]

            # Which AppSource is used?
            print("Created new app in AppSource", appsrc, "at", path)
Exemplo n.º 3
0
    def handle(self, *args, **options):
        if not options["appsource"]:
            # Show valid appsources.
            print("Specify one of the following app sources plus an app name:")
            for appsrc in AppSource.objects.all():
                if appsrc.spec["type"] == "local" and appsrc.spec.get("path"):
                    print(appsrc.slug, "(path: " + appsrc.spec["path"] + ")")
            print("")
            print(
                "Or create a new AppSource using '--path path/to/apps appsource'."
            )

        elif options["path"]:
            if options["appname"]:
                print("You cannot use --path together with an appname.")
                return

            # Make directory if it doesn't exist.
            os.makedirs(options["path"], exist_ok=True)

            # Create a new AppSource.
            appsrc = AppSource.objects.create(slug=options["appsource"],
                                              spec={
                                                  "type": "local",
                                                  "path": options["path"]
                                              })

            print("Created new AppSource", appsrc, "using local path",
                  options["path"])

        elif not options["appname"]:
            print(
                "You must specify an app name, which should be a valid directory name (i.e. no spaces), or  or --path to create a new AppSource."
            )

        else:
            # Ok do the real work.

            appsrc = AppSource.objects.get(slug=options["appsource"])

            # Do we have a path?
            if not appsrc.spec.get("path"):
                print("AppSource does not have a local path specified!")
                return

            # What's the path to the app?
            path = os.path.join(appsrc.spec["path"], options["appname"])

            # Does this app already exist?
            if os.path.exists(path):
                print("An app with that name already exists.")
                return

            # Copy stub files.
            guidedmodules_path = os.path.dirname(
                os.path.dirname(os.path.dirname(__file__)))
            shutil.copytree(os.path.join(guidedmodules_path, "stub_app"),
                            path,
                            copy_function=shutil.copy)

            # Edit the app title.
            with rtyaml.edit(os.path.join(path, "app.yaml")) as app:
                app['title'] = options["appname"]

            # Create a unique icon for the app and delete the existing app icon
            # svg file that we know is in the stub.
            from mondrianish import generate_image
            colors = ("#FFF8F0", "#FCAA67", "#7DB7C0", "#932b25", "#498B57")
            with open(os.path.join(path, "assets", "app.png"), "wb") as f:
                generate_image("png", (128, 128), 3, colors, f)
            os.unlink(os.path.join(path, "assets", "app.svg"))

            # Which AppSource is used?
            print("Created new app in AppSource", appsrc, "at", path)
Exemplo n.º 4
0
def get_file_content(component_fn, controls_fn):
    controls_fn = os.path.join(os.path.dirname(component_fn), controls_fn)
    with rtyaml.edit(controls_fn) as controls:
        return controls.get("satisfies", [])
Exemplo n.º 5
0
# The files in this example use some non-conformant changes to the
# OpenControl file formats. This script undoes those changes.

import glob
import os.path
import rtyaml


# Component files can list other files that hold control narratives.
# Put them back into the main component file.
def get_file_content(component_fn, controls_fn):
    controls_fn = os.path.join(os.path.dirname(component_fn), controls_fn)
    with rtyaml.edit(controls_fn) as controls:
        return controls.get("satisfies", [])


for fn in glob.glob("components/*/component.yaml"):
    with rtyaml.edit(fn) as component:
        if "satisfies" in component:
            satisfies = []
            for item in component['satisfies']:
                satisfies.extend(get_file_content(fn, item))
            component['satisfies'] = satisfies
Exemplo n.º 6
0
    def loadsend_file(self, filename):
        # Verify file can be opened.
        if not os.path.exists(filename): raise FileNotFoundError(filename)

        file_templ_vars = dict(
            file_name=os.path.abspath(filename),
            file_dir=os.path.abspath(os.path.dirname(filename)),
            file_uuid1=uuid.uuid1(),
            work_dir=self.opts.workdir,
        )

        log.info('Processing "%s"...', filename)
        with rtyaml.edit(filename, default={}) as data:
            query_results, defaults = self.query_notes(self.opts.query, data)
            if query_results.empty:
                log.warning("Query returned no results.")
            else:
                if self.opts.question:
                    log.info("")
                    log.info("Running in question mode.")
                    log.info("Query results:\n %s", str(query_results))
                    log.info("")
                    log.info("Query result details below.")
                    log.info("")
                else:
                    if self.opts.annotations:
                        log.info("")
                        log.info("Running in annotations mode.")
                        log.info("")
                    log.debug("Query results:\n %s", str(query_results))

            annotations_field = defaults['annotationsField']
            for i in query_results.index:
                rtnote = query_results.rtnote[i]
                note_id = str(rtnote['id'])
                skip = query_results.skip[i]
                deck = query_results.deckName[i]
                model = query_results.modelName[i]
                use_md = query_results.useMarkdown[i]
                md_sty = query_results.markdownStyle[i]
                md_lineno = query_results.markdownLineNums[i]
                md_tablen = query_results.markdownTabLength[i]
                md_mathext = query_results.useMarkdownMathExt[i]
                tags = sorted(query_results.tags[i].replace(',', '\n').split())
                fields = query_results.fields[i]
                media = query_results.media[i]
                description = "{}:{}".format(note_id, fields)

                if self.opts.question:
                    log.info("*** ID: {}".format(note_id))
                    log.info("--- Should skip: {}".format(skip))
                    log.info("--- Note tags:")
                    log.info(rtyaml.dump(tags))
                    log.info("--- Note annotations:")
                    log.info(rtyaml.dump(query_results.annotations[i]))
                    log.info("--- Note fields:")
                    log.info(rtyaml.dump(fields))
                    log.info("--- Raw fields:")
                    log.info(fields)
                    log.info("")
                    continue

                if skip:
                    log.info("Skipping note with ID: {}".format(note_id))
                    continue

                log.info("Processing note with ID: {}".format(note_id))
                log.debug("Note fields: {}".format(fields))

                # Check for note with given ID.
                # Get info for existing note.
                creating_new_note = True
                note_info = self.anki.notesInfo([note_id])

                if note_info.get("error", None) or not note_info['result'][0]:
                    if self.opts.annotations:
                        log.info(
                            "Can't find note with ID %s; skipping annotations for this note.",
                            note_id)
                        continue
                    else:
                        log.info(
                            "Can't find note with ID %s; a new note will be created.",
                            note_id)
                else:
                    creating_new_note = False

                note_templ_vars = dict(file_templ_vars)
                note_templ_vars['note_id'] = note_id
                note_uuid1 = uuid.uuid1()
                note_templ_vars['note_uuid1'] = note_uuid1
                file_uuid1 = note_templ_vars['file_uuid1']

                if creating_new_note:
                    # No provided ID; assume new note should be created.
                    log.debug("Creating new note...")

                    temporary_fields = {
                        k: self.format_text(str(v), False, md_sty, md_lineno,
                                            md_tablen, md_mathext,
                                            **note_templ_vars)
                        for (k, v) in fields.items()
                    }

                    # Create, obtaining returned ID
                    anki_result = self.anki.addNote(deck,
                                                    model,
                                                    temporary_fields,
                                                    tags=tags)
                    if anki_result.get("error", None):
                        log.warning("Can't create note: %s", description)
                    else:
                        # Add ID to note_node
                        note_id = anki_result['result']
                        note_templ_vars['note_id'] = note_id
                        prev_id, rtnote['id'] = rtnote['id'], note_id
                        log.info("ID %s replaced with %s.", prev_id, note_id)

                log.debug("Updating note...")
                # Assume provided ID is valid for existing note to be updated.
                # Convert each field from Markdown (if `use_md` is True).

                # Special handling for the annotations field. If we can find this note
                # in Anki's flashcard deck, then we'll grab any annotations the user has
                # made for that note, and store them in the YAML file. If we are instead
                # creating a new note, we'll transfer any annotations from the YAML file
                # to the new note.
                #
                # Because of this special handling, we remove annotations from the
                # shallow copy of the note. We'll instead use the round-trip version.

                if self.opts.annotations:
                    pass
                else:
                    if annotations_field in fields:
                        del fields[annotations_field]
                    converted_fields = {
                        k: self.format_text(str(v), use_md, md_sty, md_lineno,
                                            md_tablen, md_mathext, field_no,
                                            **note_templ_vars)
                        for (field_no, (k, v)) in enumerate(fields.items())
                    }
                    for media_item in media:
                        item_path = media_item['path']
                        item_path = string.Template(item_path).safe_substitute(
                            note_templ_vars)
                        item_name = media_item.get('name',
                                                   os.path.basename(item_path))
                        item_name = string.Template(item_name).safe_substitute(
                            note_templ_vars)

                        log.info("Considering sending media item...")
                        log.info("    local path: {}".format(item_path))
                        log.info("    remote name: {}".format(item_name))
                        anki_result = self.anki.statMediaFile(item_name)
                        must_send_new_media_item = False
                        item_data = None
                        if anki_result.get("error", None):
                            log.info(
                                "Can't get remote media file status (probably missing)..."
                            )
                            must_send_new_media_item = True
                        else:
                            if not anki_result['result']:
                                log.info(
                                    "... Media item is not present on remote..."
                                )
                                must_send_new_media_item = True
                            else:
                                log.info(
                                    "... Media item is already present on remote..."
                                )
                                log.info("... Reading local data...")
                                item_data = open(item_path, 'rb').read()
                                item_adler32 = zlib.adler32(item_data)
                                remote_adler32 = anki_result['result'][
                                    'adler32']
                                log.info("    Remote checksum: {}".format(
                                    remote_adler32))
                                log.info("    Local checksum: {}".format(
                                    item_adler32))
                                if remote_adler32 == item_adler32:
                                    log.info(
                                        "... Remote checksum matches that of local version..."
                                    )
                                else:
                                    log.info(
                                        "... Remote checksum is not the same as local..."
                                    )
                                    must_send_new_media_item = True
                        if must_send_new_media_item:
                            if item_data is None:
                                log.info("... Reading local data...")
                                item_data = open(item_path, 'rb').read()
                            log.info("... Encoding {} bytes of local data...".
                                     format(len(item_data)))
                            item_base64 = base64.b64encode(item_data).decode(
                                "utf-8")
                            log.info(
                                "... Sending {} bytes of encoded data to remote..."
                                .format(len(item_base64)))
                            anki_result = self.anki.storeMediaFile(
                                item_name, item_base64)
                            if anki_result.get("error", None):
                                log.warning("Can't store media file: %s",
                                            item_name)
                        log.info("... Done with media item.")

                # If we found this note in Anki's flashcard deck, then we'll grab any
                # annotations the user has made for that note, and store them in the
                # YAML file. If we are instead creating a new note, we'll transfer any
                # annotations from the YAML file to the new note.
                if creating_new_note:
                    log.debug("Transferring annotations to new note...")
                    # Transfer annotations from YAML file to new note.
                    annotations = rtnote['fields'].get(annotations_field, "")
                else:
                    log.debug("Transferring annotations from existing note...")
                    upstream_fields = note_info['result'][0]['fields']
                    annotations = upstream_fields.get(annotations_field,
                                                      dict(value=''))['value']
                    # Transfer annotations from existing note to YAML file.
                    rtnote['fields'][annotations_field] = annotations

                if not self.opts.annotations:
                    converted_fields[annotations_field] = annotations

                    # Update converted note fields...
                    result = self.anki.updateNoteFields(
                        note_id, converted_fields)
                    if result.get("error", None):
                        log.warning("Can't update note: %s", description)
                        continue

                # Update note tags...
                ## First get existing note tags.
                note_info = self.anki.notesInfo([note_id])
                if note_info.get("error", None):
                    log.warning("Can't get tags for note: %s", description)
                    continue

                current_tags = sorted(note_info['result'][0]['tags'])
                if current_tags != tags:
                    rt_non_annot_tags = set(
                        filter(lambda s: not s.startswith('ann:'),
                               rtnote.get('tags', list())))
                    non_annot_tags = set(
                        filter(lambda s: not s.startswith('ann:'), tags))
                    cur_non_annot_tags = set(
                        filter(lambda s: not s.startswith('ann:'),
                               current_tags))
                    cur_annot_tags = set(
                        filter(lambda s: s.startswith('ann:'), current_tags))
                    tags = sorted(list(non_annot_tags.union(cur_annot_tags)))
                    rt_tags = sorted(
                        list(rt_non_annot_tags.union(cur_annot_tags)))
                    rtnote['tags'] = rt_tags

                    ## Remove existing note tags.
                    log.info("Removing tags %s...", cur_non_annot_tags)
                    result = self.anki.removeTags([note_id],
                                                  " ".join(cur_non_annot_tags))
                    if result.get("error", None):
                        log.warning("Can't remove tags for note: %s",
                                    description)

                    ## Add new note tags.
                    log.info("Replacing with tags %s...", tags)
                    result = self.anki.addTags([note_id], " ".join(tags))
                    if result.get("error", None):
                        log.warning("Can't add tags for note: %s", description)

                    note_info = self.anki.notesInfo([note_id])