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

Command Expansion Deploy Tool #118

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 15 additions & 16 deletions src/aerie_cli/aerie_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,8 @@ def create_expansion_rule(
activity_name: str,
model_id: str,
command_dictionary_id: str,
name: str = None,
description: str = None
) -> int:
"""Submit expansion logic to an Aerie instance

Expand All @@ -861,34 +863,31 @@ def create_expansion_rule(
activity_name (str): Name of the activity
model_id (str): Aerie model ID
command_dictionary_id (str): Aerie command dictionary ID
name (str, Optional): Name of the expansion rule
description (str, Optional): Description of the expansion rule

Returns:
int: Expansion Rule ID in Aerie
"""

create_expansion_logic_query = """
mutation UploadExpansionLogic(
$activity_type_name: String!
$expansion_logic: String!
$command_dictionary_id: Int!
$mission_model_id: Int!
) {
addCommandExpansionTypeScript(
activityTypeName: $activity_type_name
expansionLogic: $expansion_logic
authoringCommandDictionaryId: $command_dictionary_id
authoringMissionModelId: $mission_model_id
) {
mutation CreateExpansionRule($rule: expansion_rule_insert_input!) {
createExpansionRule: insert_expansion_rule_one(object: $rule) {
id
}
}
"""
rule = {
"activity_type": activity_name,
"authoring_command_dict_id": command_dictionary_id,
"authoring_mission_model_id": model_id,
"expansion_logic": expansion_logic,
"name": name if (name is not None) else activity_name + arrow.utcnow().format("_YYYY-MM-DDTHH-mm-ss"),
"description": description if (description is not None) else ""
}
data = self.aerie_host.post_to_graphql(
create_expansion_logic_query,
activity_type_name=activity_name,
expansion_logic=expansion_logic,
mission_model_id=model_id,
command_dictionary_id=command_dictionary_id,
rule=rule
)

return data["id"]
Expand Down
106 changes: 105 additions & 1 deletion src/aerie_cli/commands/expansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
from pathlib import Path
import fnmatch

import arrow

from rich.console import Console
from rich.table import Table

from aerie_cli.commands.command_context import CommandContext
from aerie_cli.utils.prompts import select_from_list
from aerie_cli.schemas.client import ExpansionRun
from aerie_cli.schemas.client import ExpansionRun, ExpansionDeployConfiguration

app = typer.Typer()
sequences_app = typer.Typer()
Expand All @@ -19,6 +21,108 @@
app.add_typer(runs_app, name='runs', help='Commands for expansion runs')
app.add_typer(sets_app, name='sets', help='Commands for expansion sets')

# === Bulk Deploy Command ===

@app.command('deploy')
def bulk_deploy(
model_id: int = typer.Option(
..., '--model-id', '-m', prompt='Mission Model ID',
help='Mission Model ID'
),
command_dictionary_id: int = typer.Option(
..., '--command-dict-id', '-d', prompt='Command Dictionary ID',
help='Command Dictionary ID'
),
config_file: str = typer.Option(
..., "--config-file", "-c", prompt="Configuration file",
help="Deploy configuration JSON file"
),
rules_path: Path = typer.Option(
Path.cwd(), help="Path to folder containing expansion rule files"
),
time_tag: bool = typer.Option(False, help="Append time tags to create unique expansion rule/set names")
):
"""
Bulk deploy command expansion rules and sets to an Aerie instance according to a JSON configuration file.

The configuration file contains a list of rules and a list of sets:

```
{
"rules": [...],
"sets": [...]
}
```

Each rule must provide a unique rule name, the activity type name, and the name of the file with expansion logic:

```
{
"name": "Expansion Rule Name",
"activity_type": "Activity Type Name",
"file_name": "my_file.ts"
}
```

Each set must provide a unique set name and a list of rule names to add:

```
{
"name": "Expansion Set Name",
"rules": ["Expansion Rule Name", ...]
}
```
"""

client = CommandContext.get_client()

with open(Path(config_file), "r") as fid:
configuration: ExpansionDeployConfiguration = ExpansionDeployConfiguration.from_dict(json.load(fid))

name_suffix = arrow.utcnow().format("_YYYY-MM-DDTHH-mm-ss") if time_tag else ""

# Loop and upload all expansion rules
uploaded_rules = {}
for rule in configuration.rules:
try:
with open(rules_path.joinpath(rule.file_name), "r") as fid:
expansion_logic = fid.read()

rule_id = client.create_expansion_rule(
expansion_logic=expansion_logic,
activity_name=rule.activity_type,
model_id=model_id,
command_dictionary_id=command_dictionary_id,
name=rule.name + name_suffix
)
typer.echo(f"Created expansion rule {rule.name + name_suffix}: {rule_id}")
uploaded_rules[rule.name] = rule_id
except:
typer.echo(f"Failed to create expansion rule {rule.name}")

for set in configuration.sets:
try:
rule_ids = []
for rule_name in set.rules:
if rule_name in uploaded_rules.keys():
rule_ids.append(uploaded_rules[rule_name])
else:
typer.echo(f"No uploaded rule {rule_name} for set {set.name}")

assert len(rule_ids)

set_id = client.create_expansion_set(
command_dictionary_id=command_dictionary_id,
model_id=model_id,
expansion_ids=rule_ids,
name=set.name + name_suffix
)

typer.echo(f"Created expansion set {set.name + name_suffix}: {set_id}")
except:
typer.echo(f"Failed to create expansion set {set.name}")


# === Commands for expansion runs ===


Expand Down
25 changes: 25 additions & 0 deletions src/aerie_cli/schemas/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,3 +392,28 @@ class ExpansionRule(ClientSerialize):
class ResourceType(ClientSerialize):
name: str
schema: Dict


@define
class ExpansionDeployRule(ClientSerialize):
name: str
activity_type: str
file_name: str


@define
class ExpansionDeploySet(ClientSerialize):
name: str
rules: List[str]


@define
class ExpansionDeployConfiguration(ClientSerialize):
rules: List[ExpansionDeployRule] = field(
converter=converters.optional(
lambda x: [ExpansionDeployRule.from_dict(d) if isinstance(d, dict) else d for d in x])
)
sets: List[ExpansionDeploySet] = field(
converter=converters.optional(
lambda x: [ExpansionDeploySet.from_dict(d) if isinstance(d, dict) else d for d in x])
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default function MyExpansion(props: {
activityInstance: ActivityType
}): ExpansionReturn {
const { activityInstance } = props
return []
}
6 changes: 6 additions & 0 deletions tests/integration_tests/files/expansion/BiteBanana_exp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default function MyExpansion(props: {
activityInstance: ActivityType
}): ExpansionReturn {
const { activityInstance } = props
return []
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"rules": [
{
"name": "integration_test_BakeBananaBread",
"activity_type": "BakeBananaBread",
"file_name": "BakeBananaBread_exp.ts"
},
{
"name": "integration_test_BiteBanana",
"activity_type": "BiteBanana",
"file_name": "BiteBanana_exp.ts"
},
{
"name": "integration_test_bad",
"activity_type": "Fake",
"file_name": "this path no exist"
}
],
"sets": [
{
"name": "integration_test_set",
"rules": [
"integration_test_BakeBananaBread",
"integration_test_BiteBanana"
]
}
]
}
29 changes: 29 additions & 0 deletions tests/integration_tests/test_expansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
# Expansion Variables
expansion_set_id = -1
expansion_sequence_id = 1
EXPANSION_FILES_PATH = os.path.join(FILES_PATH, "expansion")
EXPANSION_DEPLOY_CONFIG_PATH = os.path.join(EXPANSION_FILES_PATH, "expansion_deploy_config.json")

@pytest.fixture(scope="module", autouse=True)
def set_up_environment(request):
Expand Down Expand Up @@ -127,6 +129,33 @@ def test_expansion_sequence_delete():
# Uses model, command dictionary, and activity types
#######################


def test_expansion_deploy():
result = runner.invoke(
app,
[
"expansion",
"deploy",
"-m",
str(model_id),
"-d",
str(command_dictionary_id),
"-c",
EXPANSION_DEPLOY_CONFIG_PATH,
"--rules-path",
EXPANSION_FILES_PATH,
"--time-tag"
],
catch_exceptions=False
)
assert result.exit_code == 0, \
f"{result.stdout}"\
f"{result.stderr}"
assert "Created expansion rule integration_test_BakeBananaBread" in result.stdout
assert "Created expansion rule integration_test_BiteBanana" in result.stdout
assert "Failed to create expansion rule integration_test_bad" in result.stdout
assert "Created expansion set integration_test_set" in result.stdout

def test_expansion_set_create():
client.create_expansion_rule(
expansion_logic="""
Expand Down
Loading