Skip to content

Commit

Permalink
Merge pull request #123 from yetanalytics/cli-subcommands
Browse files Browse the repository at this point in the history
[DS-152] CLI subcommands
  • Loading branch information
kelvinqian00 authored Nov 9, 2023
2 parents 3fc93a4 + b7632f5 commit 5ebac8a
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 328 deletions.
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 @@ -189,55 +189,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:bob@example.org 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

0 comments on commit 5ebac8a

Please sign in to comment.