Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DS-152] CLI subcommands #123

Merged
merged 12 commits into from
Nov 9, 2023
15 changes: 7 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@

GROUP_ID ?= com.yetanalytics
ARTIFACT_ID ?= datasim
VERSION ?= 0.3.0
MAIN_NS ?= com.yetanalytics.datasim.main
VERSION ?= 0.4.0

clean:
rm -rf target

target/bundle/datasim_cli.jar:
mkdir -p target/bundle
rm -f pom.xml
clojure -X:depstar uberjar :no-pom false :sync-pom true :aliases '[:cli]' :aot true :group-id $(GROUP_ID) :artifact-id $(ARTIFACT_ID)-cli :version '"$(VERSION)"' :jar target/bundle/datasim_cli.jar :main-class com.yetanalytics.datasim.main
clojure -X:depstar uberjar :no-pom false :sync-pom true :aliases '[:cli]' :aot true :group-id $(GROUP_ID) :artifact-id $(ARTIFACT_ID)-cli :version '"$(VERSION)"' :jar target/bundle/datasim_cli.jar :main-class com.yetanalytics.datasim.cli
rm -f pom.xml

target/bundle/datasim_server.jar: # no AOT for this one
Expand All @@ -35,7 +34,7 @@ target/bundle: target/bundle/bin target/bundle/datasim_cli.jar target/bundle/dat

bundle: target/bundle


# Tests

test-unit:
clojure -Adev:cli:run-tests
Expand All @@ -44,16 +43,16 @@ test-unit-onyx:
clojure -Adev:cli:onyx:run-onyx-tests

test-cli:
clojure -A:cli:run -p dev-resources/profiles/cmi5/fixed.json -a dev-resources/personae/simple.json -m dev-resources/models/simple.json -o dev-resources/parameters/simple.json validate-input dev-resources/input/simple.json
clojure -A:cli:run validate-input -p dev-resources/profiles/cmi5/fixed.json -a dev-resources/personae/simple.json -m dev-resources/models/simple.json -o dev-resources/parameters/simple.json -v dev-resources/input/simple.json

test-cli-comprehensive:
clojure -A:cli:run -i dev-resources/input/simple.json validate-input dev-resources/input/simple.json
clojure -A:cli:run validate-input -i dev-resources/input/simple.json -v dev-resources/input/simple.json

test-cli-output:
clojure -A:cli:run -i dev-resources/input/simple.json generate
clojure -A:cli:run generate -i dev-resources/input/simple.json

test-bundle-output: bundle
cd target/bundle; bin/run.sh -i ../../dev-resources/input/simple.json generate
cd target/bundle; bin/run.sh generate -i ../../dev-resources/input/simple.json

validate-template:
AWS_PAGER="" aws cloudformation validate-template --template-body file://template/0_vpc.yml
Expand Down
111 changes: 68 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,55 +181,80 @@ In the form of a CLI application, DATASIM takes the inputs listed above as JSON

For the CLI the first step is to build the project so that it can be run on a JVM.

```
make bundle
```

Now that we have this, navigate to target/bundle and run

```
bin/run.sh
```

With no commands or `--help` it will give you the list of subcommands:

| Subcommand | Description
| --- | ---
| `validate-input` | Validate the input and create an input JSON file.
| `generate` | Generate statements from input and print to stdout.
| `generate-post` | Generate statements from input and POST them to an LRS.

The `validate-input` subcommand is used to validate and combine input files. These are its arguments:

| Argument | Description
| --- | ---
| `-p, --profile URI` | The location of an xAPI profile, can be used multiple times.
| `-a, --actor-personae URI` | The location of an Actor Personae document indicating the actors in the sim.
| `-m, --models URI` | The location of an Persona Model document, to describe alignments and overrides for the personae.
| `-o, -parameters URI` | The location of simulation parameters document. Uses the current time and timezone as defaults if they are not present. (The "o" stands for "options.")
| `-i, --input URI` | The location of a JSON file containing a combined simulation input spec.
| `-v, --validated-input URI` | The location of the validated input to be produced.

The `generate` subcommand is used to generate statements from an input and print them to standard output. The inputs can be a combined `--input` location or a combination of `-p`, `-a`, `-m`, and `-o` inputs. The additional arguments are as follows:
| Argument | Description
| --- | ---
| `--seed SEED` | An integer seed to override the one in the input spec. Use -1 for a random seed.
| `--actor AGENT_ID` | Pass an agent id in the format 'mbox::mailto:[email]' to select actor(s)
| `--gen-profile IRI` | Only generate based on primary patterns in the given profile. May be given multiple times to include multiple profiles.
| `--gen-pattern IRI` | Only generate based on the given primary pattern. May be given multiple times to include multiple patterns.

The `generate-post` subcommand is used to generate statements from an input and POST them to an LRS. In addition to the `generate` arguments, this subcommands has these additional arguments:
| Argument | Description | Default
| --- | --- | ---
| `-E, --endpoint URI` | The xAPI endpoint of an LRS to POST to, ex: `https://lrs.example.org/xapi` | N/A
| `-U, --username URI` | The Basic Auth username for the LRS. | N/A
| `-P, --password URI` | The Basic Auth password for the LRS. | N/A
| `-B, --batch-size SIZE` | The batch size, i.e. how many statements to send at a time, for POSTing. | `25`
| `-C, --concurrency CONC` | The max concurrency of the LRS POST pipeline. | `4`
| `-L, --post-limit LIMIT` | The total number of statements that will be sent to the LRS before termination. Overrides sim params. Set to -1 for no limit. | `999`
| `-A, --[no-]async` | Async operation. Use `--no-async` if statements must be sent to server in timestamp order. | `true`

The following is an example of a simple run. We first create a combined input file using `validate-input`:
```
bin/run.sh validate-input \
-p dev-resources/profile/cmi5/fixed.json \
-a dev-resources/personae/simple.json \
-m dev-resources/models/simple.json \
-o dev-resources/parameters/simple.json \
-v dev-resources/input/simple.json
```

With no commands or `--help` it will give you the list of parameters:

-p, --profile URI The location of an xAPI profile, can be used multiple times.
-a, --actor-personae URI The location of an Actor Personae document indicating the actors in the sim, can be used multiple times.
-m, --models URI The location of an Personae Model Document.
-o, --parameters URI {...} The location of a Sim Parameters Document.
-i, --input URI The location of a JSON file containing a combined simulation input spec.
--seed SEED An integer seed to override the one in the input spec. Use -1 for random.
--actor AGENT_ID Pass an agent id in the format mbox::malto:[email protected] to select actor(s)
-E, --endpoint URI The xAPI endpoint of an LRS to POST to, ex: https://lrs.example.org/xapi
-U, --username URI The basic auth username for the LRS you wish to post to
-P, --password URI The basic auth password for the LRS you wish to post to
-B, --batch-size SIZE 25 The batch size for POSTing to an LRS
-C, --concurrency CONC 4 The max concurrency of the LRS POST pipeline
-L, --post-limit LIMIT 999 The total number of statements that will be sent to the LRS before termination. Overrides sim params. Set to -1 for no limit.
-A, --[no-]async Async operation. Use --no-async if statements must be sent to server in timestamp order.
--gen-profile IRI Only generate based on primary patterns in the given profile. May be given multiple times to include multiple profiles.
--gen-pattern IRI Only generate based on the given primary pattern. May be given multiple times to include multiple patterns.
-h, --help Show this list.

For a simple run, we will first create the simulation specification by combining the inputs, validating them, and outputting to a simulation input file like so:

bin/run.sh -p [profile json file] \
-a [actors json filename] \
-m [models json filename] \
-o [sim params json filename] \
validate-input [desired output filename]

Once we have that simulation specification, we can run the sim just from that like so:

bin/run.sh -i dev-resources/input/simple.json generate

###### CLI LRS POST

If we have an endpoint and credentials for an LRS we can direcly POST the statements to it:

bin/run.sh -i dev-resources/input/simple.json \
-E [LRS xAPI endpoint ex. https://lrs.example.org/xapi] \
-U [basic auth username] \
-P [basic auth password] \
-B [batch size] \
-L [limit statements posted, -1 is no limit] \
generate post
Once we have that sim specification, we can run the simulation using the `generate`:
```
bin/run.sh generate -i dev-resources/input/simple.json
```

If we have an endpoint and credentials for an LRS we can directly POST the simulated statements using `generate-post`:

```
bin/run.sh generate-post \
-i dev-resources/input/simple.json \
-E http://localhost:8080/xapi \
-U username \
-P password \
-B 20 \
-L 1000 \
```

As statements are successfully sent to the LRS their IDs will be sent to stdout.

Expand Down
3 changes: 2 additions & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
:aliases
{:cli {:extra-paths ["src/cli"]
:extra-deps {org.clojure/tools.cli {:mvn/version "1.0.219"}}}
:run {:main-opts ["-m" "com.yetanalytics.datasim.main"]}
;; TODO: More CLI-specific name for :run alias
:run {:main-opts ["-m" "com.yetanalytics.datasim.cli"]}
:dev {:extra-paths ["dev-resources" "src/dev"]
:extra-deps {incanter/incanter-core {:mvn/version "1.9.3"}
incanter/incanter-charts {:mvn/version "1.9.3"}
Expand Down
38 changes: 38 additions & 0 deletions src/cli/com/yetanalytics/datasim/cli.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
(ns com.yetanalytics.datasim.cli
(:require [clojure.tools.cli :as cli]
[com.yetanalytics.datasim.cli.input :as cli-input]
[com.yetanalytics.datasim.cli.generate :as cli-gen]
[com.yetanalytics.datasim.cli.util :as u])
(:gen-class))

(def top-level-options
[["-h" "--help" "Display the top-level help guide."]])

(def top-level-summary
(str "Usage: 'datasim <subcommand> <args>' or 'datasim [-h|--help]'.\n"
"\n"
"where the subcommand can be one of the following:\n"
" validate-input: Validate the input and create an input JSON file.\n"
" generate: Generate statements from input and print to stdout.\n"
" generate-post: Generate statements from input and POST them to an LRS.\n"
"\n"
"Run 'datasim <subcommand> --help' for more info on each subcommand."))

(defn -main [& args]
(let [{:keys [options arguments summary errors]}
(cli/parse-opts args top-level-options
:in-order true
:summary-fn (fn [_] top-level-summary))
[subcommand & rest-args]
arguments]
(cond
(:help options)
(println summary)
(not subcommand)
(print "No subcommand entered.\n\n" summary)
:else
(case subcommand
"validate-input" (cli-input/validate-input rest-args)
"generate" (cli-gen/generate rest-args)
"generate-post" (cli-gen/generate-post rest-args)
(u/bail! errors)))))
Loading