# Module `paroxython.cli_recommend`

Read and execute a pipeline of commands and report the learning costs.

## Usage

    paroxython recommend [options] DB_PATH

## Description

    Requires an existing database previously generated by the command:

        paroxython collect DIRECTORY

    ... and a pipeline of commands describing which concepts (taxa) are
    already known, which ones need to be illustrated or must be avoided. By
    default, it results in a list of recommended programs, along with their
    total learning costs and the individual learning cost of each of the
    concepts they feature. These results are presented in Markdown format.
    With the option `-o stdout` the program list is simply printed to stdout.

## Shortcut

    For DB_PATH, instead of the path of a tag database, you can provide the
    DIRECTORY previously collected. Paroxython will automatically look for the
    tag database at DIRECTORY/../DIRECTORY_db.json.

## Options

    -b --base=PATH      Value accessible, with the syntax "{base}", by any shell
                        command of the pipeline. If not specified, this is the
                        parent directory of DB_PATH. [default: ]
    -c --cost=STR       Learning cost assessment strategy. [default: zeno]
                        Currently available:
                        • zeno: the i-th edge of a taxon, if not already
                                imparted, costs 2^(-i). For instance, the taxon
                                old/new/new will costs 2^-2 + 2^-3 = 0.375.
                        • linear: simply count the number of new edges.
    -o --output=PATH    The path of the resulting report. If it is omitted, a
                        value of PREFIX_recommendations.md is inferred from the
                        path PREFIX_db.json of DB_PATH. If it is STDOUT, the
                        sorted list of recommended (but not hidden) programs, is
                        printed on the standard output. [default: ]
    -p --pipe=PATH      Path of the command pipeline. If it is omitted, a value
                        of PREFIX_pipe.py is inferred from the path
                        PREFIX_db.json of DB_PATH. If is is <code>\[]</code>, no filter is
                        applied. [default: ]
    -f --format=STR     The format of program titles in the report. This string
                        can contains the following identifiers (between braces):
                        • name: filename of the program.
                        • path: program key in the DB "programs" dictionary.
                        • prefix: name of DB_PATH, minus any "_db.json" suffix.
                        • absolute: absolute path to DB_PATH's parent folder.
                        • relative: relative path to DB_PATH's parent folder.
                        Shortcut: if format is "vscode", makes program names to
                            be clickable and opening in VS Code.
                        [default: `{name}`]

## Example

    Use `-o stdout` to pipe the results into another shell command. For instance,
    let's assume that:

    - your program repository is `path/to/programs`;
    - your tag database is at its default location `path/to/programs_db.json`;
    - your pipeline is at its default location `path/to/programs_pipe.py`.

    Then, to copy (not move) all the recommended programs to an existing
    directory DEST, just do:

    paroxython recommend -o stdout path/to/programs_db.json \
        | xargs  -I {} sh -c "cp path/to/programs/{} DEST"

Expand source code Browse GitHub

    import json
    import sys
    from pathlib import Path

    import regex  # type: ignore
    from ast import literal_eval

    from .goodies import print_success, print_exit
    from .recommend_programs import Recommendations

    def cli_wrapper(args):
        db_path = Path(args["DB_PATH"])
        if not db_path.exists():
            print_exit(f"no file or directory at '{db_path.absolute()}': aborted.")
        parent_path = db_path.parent
        if db_path.is_dir():
            for suffix in ("_db", "-db"):
                db_path = parent_path / f"{db_path.name}{suffix}.json"
                if db_path.is_file():
                    print(f"Using database '{db_path}'.")
                    break
            else:
                print_exit(f"unable to locate a tag database in '{parent_path}': aborted.")
        m = regex.fullmatch(r"(.+[_-])db\.json", db_path.name)
        prefix = m[1] if m else ""
        pipeline_path = Path(args["--pipe"] or parent_path / f"{prefix}pipe.py")
        if pipeline_path.is_file():
            print(f"Using pipeline '{pipeline_path}'.")
            try:
                commands = literal_eval(pipeline_path.read_text())
            except Exception:  # Too many possible exceptions
                print_exit(f"Malformed pipeline: aborted.")
        elif args["--pipe"] == "[]":
            print("Using an empty pipeline.")
            commands = []
        else:
            print_exit(f"No pipeline at '{pipeline_path}': aborted.")
        stdout_backup = sys.stdout
        if args["--output"].upper() == "STDOUT":
            sys.stdout = sys.stderr
        title_format = args["--format"]
        if title_format.lower() == "vscode":
            title_format = "[`{name}`](vscode://file/{absolute}/{prefix}/{path})"
        title_format = title_format.format(
            name="{name}",
            path="{path}",
            prefix=prefix.rstrip("_-"),
            absolute=parent_path.resolve(),
            relative=parent_path,
        )
        rec = Recommendations(
            db=json.loads(db_path.read_text()),
            base_path=Path(args["--base"] or parent_path),
            assessment_strategy=args["--cost"],
            title_format=title_format,
        )
        rec.run_pipeline(commands)
        if args["--output"].upper() == "STDOUT":
            sys.stdout = stdout_backup
            return print("\n".join(sorted(rec.selected_programs - rec.hidden_programs)))
        output_path = Path(args["--output"] or parent_path / f"{prefix}recommendations.md")
        output_path.write_text(rec.get_markdown())
        print_success(f"Dumped: {output_path.resolve()}\n")

## Functions

`def cli_wrapper(args)`
  Expand source code Browse GitHub

      def cli_wrapper(args):
          db_path = Path(args["DB_PATH"])
          if not db_path.exists():
              print_exit(f"no file or directory at '{db_path.absolute()}': aborted.")
          parent_path = db_path.parent
          if db_path.is_dir():
              for suffix in ("_db", "-db"):
                  db_path = parent_path / f"{db_path.name}{suffix}.json"
                  if db_path.is_file():
                      print(f"Using database '{db_path}'.")
                      break
              else:
                  print_exit(f"unable to locate a tag database in '{parent_path}': aborted.")
          m = regex.fullmatch(r"(.+[_-])db\.json", db_path.name)
          prefix = m[1] if m else ""
          pipeline_path = Path(args["--pipe"] or parent_path / f"{prefix}pipe.py")
          if pipeline_path.is_file():
              print(f"Using pipeline '{pipeline_path}'.")
              try:
                  commands = literal_eval(pipeline_path.read_text())
              except Exception:  # Too many possible exceptions
                  print_exit(f"Malformed pipeline: aborted.")
          elif args["--pipe"] == "[]":
              print("Using an empty pipeline.")
              commands = []
          else:
              print_exit(f"No pipeline at '{pipeline_path}': aborted.")
          stdout_backup = sys.stdout
          if args["--output"].upper() == "STDOUT":
              sys.stdout = sys.stderr
          title_format = args["--format"]
          if title_format.lower() == "vscode":
              title_format = "[`{name}`](vscode://file/{absolute}/{prefix}/{path})"
          title_format = title_format.format(
              name="{name}",
              path="{path}",
              prefix=prefix.rstrip("_-"),
              absolute=parent_path.resolve(),
              relative=parent_path,
          )
          rec = Recommendations(
              db=json.loads(db_path.read_text()),
              base_path=Path(args["--base"] or parent_path),
              assessment_strategy=args["--cost"],
              title_format=title_format,
          )
          rec.run_pipeline(commands)
          if args["--output"].upper() == "STDOUT":
              sys.stdout = stdout_backup
              return print("\n".join(sorted(rec.selected_programs - rec.hidden_programs)))
          output_path = Path(args["--output"] or parent_path / f"{prefix}recommendations.md")
          output_path.write_text(rec.get_markdown())
          print_success(f"Dumped: {output_path.resolve()}\n")