gen3-dataops-toolkit 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- g3dt/__init__.py +0 -0
- g3dt/cli/__init__.py +5 -0
- g3dt/cli/_internal/__init__.py +1 -0
- g3dt/cli/_internal/dispatch.py +428 -0
- g3dt/cli/_internal/registry.py +65 -0
- g3dt/cli/_internal/resolve.py +22 -0
- g3dt/cli/_internal/runner.py +76 -0
- g3dt/cli/_internal/safety.py +110 -0
- g3dt/cli/config_cmds.py +202 -0
- g3dt/cli/delete_cmds.py +101 -0
- g3dt/cli/dict_cmds.py +102 -0
- g3dt/cli/ec2_cmds.py +114 -0
- g3dt/cli/indexd_cmds.py +57 -0
- g3dt/cli/jobs.py +83 -0
- g3dt/cli/k8s.py +54 -0
- g3dt/cli/main.py +110 -0
- g3dt/cli/metadata.py +76 -0
- g3dt/cli/synth.py +206 -0
- g3dt/config.py +393 -0
- g3dt/indexd/__init__.py +0 -0
- g3dt/indexd/indexd_registrar.py +244 -0
- g3dt/ingest/ingest.py +629 -0
- g3dt/resolver.py +163 -0
- g3dt/services/delete/delete_all_metadata_for_project.py +170 -0
- g3dt/services/delete/delete_metadata.sh +153 -0
- g3dt/services/delete/delete_metadata_by_guid.py +338 -0
- g3dt/services/dictionary/deploy_dd.sh +65 -0
- g3dt/services/dictionary/pull_dict.sh +59 -0
- g3dt/services/dictionary/upload_dictionary.py +109 -0
- g3dt/services/indexd/register_indexd.py +240 -0
- g3dt/services/k8s_ops/argocd_restart_etl.sh +140 -0
- g3dt/services/k8s_ops/argocd_restart_ms.sh +102 -0
- g3dt/services/k8s_ops/argocd_restart_schema.sh +106 -0
- g3dt/services/k8s_ops/login_to_pod.sh +110 -0
- g3dt/services/k8s_ops/restart_etl_and_ms.sh +56 -0
- g3dt/services/synthetic_data/delete_synth_metadata_sheepdog.py +183 -0
- g3dt/services/synthetic_data/full_deploy_dd_and_synth.sh +124 -0
- g3dt/services/synthetic_data/generate_synth_metadata.sh +133 -0
- g3dt/services/synthetic_data/upload_synth_metadata_sheepdog.py +165 -0
- g3dt/services/upload/metadata/upload_all_studies.sh +108 -0
- g3dt/services/upload/metadata/upload_metadata.py +152 -0
- g3dt/upload/__init__.py +1 -0
- g3dt/upload/metadata_deleter.py +265 -0
- g3dt/upload/metadata_submitter.py +1093 -0
- g3dt/upload/upload_synthdata_s3.py +164 -0
- g3dt/utils/athena_utils.py +834 -0
- g3dt/utils/dbt_utils.py +66 -0
- g3dt/utils/release_writer.py +188 -0
- g3dt/validate/validate.py +609 -0
- gen3_dataops_toolkit-2.0.0.dist-info/METADATA +125 -0
- gen3_dataops_toolkit-2.0.0.dist-info/RECORD +53 -0
- gen3_dataops_toolkit-2.0.0.dist-info/WHEEL +4 -0
- gen3_dataops_toolkit-2.0.0.dist-info/entry_points.txt +3 -0
g3dt/cli/jobs.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""`acdc jobs` — track EC2-dispatched runs by their friendly run id."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
|
|
8
|
+
from g3dt.cli._internal import dispatch, registry
|
|
9
|
+
|
|
10
|
+
app = typer.Typer(no_args_is_help=True, help="Track EC2-dispatched jobs.")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _status_color(state: str) -> str:
|
|
14
|
+
"""Map an SSM/derived status to a terminal colour for the all-runs listing."""
|
|
15
|
+
if state == "Success":
|
|
16
|
+
return typer.colors.GREEN
|
|
17
|
+
if state in ("Failed", "Cancelled", "TimedOut"):
|
|
18
|
+
return typer.colors.RED
|
|
19
|
+
if state in ("Pending", "InProgress", "Delayed", "pending"):
|
|
20
|
+
return typer.colors.YELLOW
|
|
21
|
+
return typer.colors.BRIGHT_BLACK # n/a (ssh) / unknown
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@app.command(name="list")
|
|
25
|
+
def list_runs() -> None:
|
|
26
|
+
"""List recorded EC2 dispatches (most recent first)."""
|
|
27
|
+
runs = registry.all_runs()
|
|
28
|
+
if not runs:
|
|
29
|
+
typer.echo("No dispatched runs recorded.")
|
|
30
|
+
return
|
|
31
|
+
for run_id in sorted(runs, reverse=True):
|
|
32
|
+
rec = runs[run_id]
|
|
33
|
+
typer.echo(
|
|
34
|
+
f"{run_id} env={rec.get('env')} "
|
|
35
|
+
f"via={rec.get('mechanism')} instance={rec.get('instance_id')}"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@app.command()
|
|
40
|
+
def status(
|
|
41
|
+
run_id: Optional[str] = typer.Argument(
|
|
42
|
+
None, help="Run id from dispatch. Omit to show all runs with their status."
|
|
43
|
+
),
|
|
44
|
+
) -> None:
|
|
45
|
+
"""Show SSM status for one run, or all recorded runs if no run id is given."""
|
|
46
|
+
if run_id is None:
|
|
47
|
+
runs = registry.all_runs()
|
|
48
|
+
if not runs:
|
|
49
|
+
typer.echo("No dispatched runs recorded.")
|
|
50
|
+
return
|
|
51
|
+
for rid in sorted(runs, reverse=True):
|
|
52
|
+
rec = runs[rid]
|
|
53
|
+
state = dispatch.status_label(rec)
|
|
54
|
+
line = f"{rid} env={rec.get('env')} via={rec.get('mechanism')} "
|
|
55
|
+
typer.echo(line, nl=False)
|
|
56
|
+
typer.secho(state, fg=_status_color(state))
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
inv = dispatch.status(run_id)
|
|
60
|
+
state = inv.get("Status", "unknown") if isinstance(inv, dict) else "unknown"
|
|
61
|
+
typer.secho(f"{run_id}: {state}", bold=True)
|
|
62
|
+
if isinstance(inv, dict):
|
|
63
|
+
out = inv.get("StandardOutputContent")
|
|
64
|
+
err = inv.get("StandardErrorContent")
|
|
65
|
+
if out:
|
|
66
|
+
typer.echo(out)
|
|
67
|
+
if err:
|
|
68
|
+
typer.secho(err, fg=typer.colors.RED)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@app.command()
|
|
72
|
+
def stop(run_id: str = typer.Argument(..., help="Run id from dispatch.")) -> None:
|
|
73
|
+
"""Stop (cancel) a running EC2-dispatched job."""
|
|
74
|
+
dispatch.stop(run_id)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@app.command()
|
|
78
|
+
def logs(
|
|
79
|
+
run_id: str = typer.Argument(..., help="Run id from dispatch."),
|
|
80
|
+
follow: bool = typer.Option(False, "--follow", "-f", help="Stream new output as it arrives."),
|
|
81
|
+
) -> None:
|
|
82
|
+
"""Print (and optionally follow) the CloudWatch logs for a dispatched run."""
|
|
83
|
+
dispatch.logs(run_id, follow=follow)
|
g3dt/cli/k8s.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""`g3dt k8s` — restart Gen3 microservices / ETL via ArgoCD (LOCAL only).
|
|
2
|
+
|
|
3
|
+
These use ``argocd login --sso`` (a browser flow), so they cannot run headless
|
|
4
|
+
on EC2. The wrapped scripts receive their settings as ``G3DT_*`` environment
|
|
5
|
+
variables resolved from SSM — they read no config files.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from g3dt.config import script_env
|
|
12
|
+
from g3dt.cli._internal import runner
|
|
13
|
+
from g3dt.cli._internal.resolve import env_of
|
|
14
|
+
|
|
15
|
+
app = typer.Typer(no_args_is_help=True, help="ArgoCD / Kubernetes restarts (local).")
|
|
16
|
+
|
|
17
|
+
_SCHEMA = "services/k8s_ops/argocd_restart_schema.sh"
|
|
18
|
+
_ETL = "services/k8s_ops/argocd_restart_etl.sh"
|
|
19
|
+
_ETL_AND_MS = "services/k8s_ops/restart_etl_and_ms.sh"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@app.command(name="restart-schema")
|
|
23
|
+
def restart_schema(
|
|
24
|
+
env: str = typer.Option(..., "--env", "-e", help="Environment, e.g. test."),
|
|
25
|
+
sync: bool = typer.Option(False, "--sync", "-s", help="argocd app sync first."),
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Restart sheepdog/peregrine/guppy/portal (schema microservices)."""
|
|
28
|
+
e = env_of(env)
|
|
29
|
+
args = ["-d", e.domain, "-a", e.app_name, "-n", e.namespace]
|
|
30
|
+
if sync:
|
|
31
|
+
args.append("-s")
|
|
32
|
+
runner.run(runner.bash_script(_SCHEMA, *args), env=script_env(e))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.command(name="restart-etl")
|
|
36
|
+
def restart_etl(
|
|
37
|
+
env: str = typer.Option(..., "--env", "-e", help="Environment, e.g. test."),
|
|
38
|
+
sync: bool = typer.Option(False, "--sync", "-s", help="argocd app sync first."),
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Create + run the ETL cronjob and wait for completion."""
|
|
41
|
+
e = env_of(env)
|
|
42
|
+
args = ["-e", env]
|
|
43
|
+
if sync:
|
|
44
|
+
args.append("-s")
|
|
45
|
+
runner.run(runner.bash_script(_ETL, *args), env=script_env(e))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@app.command(name="restart-ms")
|
|
49
|
+
def restart_ms(
|
|
50
|
+
env: str = typer.Option(..., "--env", "-e", help="Environment, e.g. test."),
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Restart both ETL and schema microservices (wraps restart_etl_and_ms.sh)."""
|
|
53
|
+
e = env_of(env)
|
|
54
|
+
runner.run(runner.bash_script(_ETL_AND_MS, env), env=script_env(e))
|
g3dt/cli/main.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Root ``g3dt`` Typer application.
|
|
2
|
+
|
|
3
|
+
Assembles every command group and the top-level ``version`` / ``docs`` helpers.
|
|
4
|
+
The console-script entry point in pyproject.toml points at :func:`main`.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from g3dt.cli import (
|
|
11
|
+
config_cmds,
|
|
12
|
+
delete_cmds,
|
|
13
|
+
dict_cmds,
|
|
14
|
+
ec2_cmds,
|
|
15
|
+
indexd_cmds,
|
|
16
|
+
jobs,
|
|
17
|
+
k8s,
|
|
18
|
+
metadata,
|
|
19
|
+
synth,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
app = typer.Typer(
|
|
23
|
+
no_args_is_help=True,
|
|
24
|
+
rich_markup_mode="rich",
|
|
25
|
+
help="Gen3 DataOps toolkit. Run [bold]g3dt docs[/bold] for an overview.",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
app.add_typer(dict_cmds.app, name="dict")
|
|
29
|
+
app.add_typer(synth.app, name="synth")
|
|
30
|
+
app.add_typer(metadata.app, name="metadata")
|
|
31
|
+
app.add_typer(delete_cmds.app, name="delete")
|
|
32
|
+
app.add_typer(k8s.app, name="k8s")
|
|
33
|
+
app.add_typer(indexd_cmds.app, name="indexd")
|
|
34
|
+
app.add_typer(ec2_cmds.app, name="ec2")
|
|
35
|
+
app.add_typer(jobs.app, name="jobs")
|
|
36
|
+
app.add_typer(config_cmds.app, name="config")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_DOCS = """\
|
|
40
|
+
Gen3 DataOps toolkit (g3dt) — operations overview
|
|
41
|
+
=================================================
|
|
42
|
+
|
|
43
|
+
Configuration: two kinds, nothing else
|
|
44
|
+
- INPUTS live in the CDK repo (gen3-aws-data-pipeline) as
|
|
45
|
+
config/<project>.<env>.json, read only by `cdk deploy`.
|
|
46
|
+
- Everything else is resolved live from SSM (/{project}/{env}/...), which
|
|
47
|
+
`cdk deploy` publishes. The only local file is the g3dt.yaml marker
|
|
48
|
+
(project/region/default_env, optional profiles:/studies: maps), searched
|
|
49
|
+
at ./g3dt.yaml, ~/.g3dt/g3dt.yaml, /etc/g3dt/g3dt.yaml.
|
|
50
|
+
|
|
51
|
+
Mental model: two execution planes
|
|
52
|
+
- Control plane (LOCAL): dict deploy, k8s restarts. These use the interactive
|
|
53
|
+
`argocd login --sso` browser flow and AWS named profiles, so they run on
|
|
54
|
+
your laptop only.
|
|
55
|
+
- Data plane (LONG jobs): metadata upload/delete, indexd register. Add
|
|
56
|
+
`--on ec2` to run them on the env's job box via SSM (disconnect-safe);
|
|
57
|
+
watch with `g3dt jobs status|logs <run-id> --follow`.
|
|
58
|
+
|
|
59
|
+
Discover everything
|
|
60
|
+
g3dt --help list all command groups
|
|
61
|
+
g3dt <group> --help commands + options for a group
|
|
62
|
+
g3dt config envs environments with a deployed SSM tree
|
|
63
|
+
g3dt config studies studies from your g3dt.yaml marker
|
|
64
|
+
g3dt config show --env test resolved settings (safe, read-only)
|
|
65
|
+
|
|
66
|
+
Typical release runbook (staging shown; repeat for prod with care)
|
|
67
|
+
1. g3dt dict deploy --env staging
|
|
68
|
+
2. g3dt metadata upload --study <study> --env staging --on ec2
|
|
69
|
+
3. g3dt jobs logs <run-id> --follow
|
|
70
|
+
4. g3dt k8s restart-etl --env staging
|
|
71
|
+
|
|
72
|
+
Synthetic data (test only, all local)
|
|
73
|
+
g3dt synth deploy --env test
|
|
74
|
+
|
|
75
|
+
EC2 / SSM prerequisites
|
|
76
|
+
- The env's job box is created by the CDK (ec2-job-runner stack): SSM-managed,
|
|
77
|
+
toolkit pre-installed by user-data, instance id published to SSM.
|
|
78
|
+
- Local profile needs: ssm:SendCommand / ssm:GetCommandInvocation,
|
|
79
|
+
s3:GetObject on the log prefix, ec2:Start/Stop/DescribeInstances.
|
|
80
|
+
|
|
81
|
+
NOT run by this CLI: the Glue jobs (validation, release-JSON) and the CodeBuild
|
|
82
|
+
dbt pipelines. Those are deployed and triggered via the CDK repo and the
|
|
83
|
+
project's dbt repo.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@app.command()
|
|
88
|
+
def docs() -> None:
|
|
89
|
+
"""Print the operations overview (mental model, runbook, prerequisites)."""
|
|
90
|
+
typer.echo(_DOCS)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@app.command()
|
|
94
|
+
def version() -> None:
|
|
95
|
+
"""Print the installed gen3-dataops-toolkit version."""
|
|
96
|
+
try:
|
|
97
|
+
from importlib.metadata import version as _v
|
|
98
|
+
|
|
99
|
+
typer.echo(_v("gen3-dataops-toolkit"))
|
|
100
|
+
except Exception: # pragma: no cover - fallback when not installed
|
|
101
|
+
typer.echo("unknown")
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def main() -> None:
|
|
105
|
+
"""Console-script entry point."""
|
|
106
|
+
app()
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
if __name__ == "__main__":
|
|
110
|
+
main()
|
g3dt/cli/metadata.py
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""`g3dt metadata` — upload real study metadata to Gen3 (data-plane).
|
|
2
|
+
|
|
3
|
+
These are the multi-hour jobs, so they support ``--on ec2`` to run on the
|
|
4
|
+
env's EC2 job box via SSM Run Command (disconnect-safe) instead of the laptop.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from g3dt.cli._internal import dispatch
|
|
11
|
+
from g3dt.cli._internal.dispatch import Target
|
|
12
|
+
from g3dt.cli._internal.resolve import study_of
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(no_args_is_help=True, help="Upload study metadata to Gen3.")
|
|
15
|
+
|
|
16
|
+
_UPLOAD = "services/upload/metadata/upload_metadata.py"
|
|
17
|
+
_UPLOAD_ALL = "services/upload/metadata/upload_all_studies.sh"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@app.command()
|
|
21
|
+
def upload(
|
|
22
|
+
study: str = typer.Option(..., "--study", "-s", help="Study, e.g. ausdiab."),
|
|
23
|
+
env: str = typer.Option(..., "--env", "-e", help="Environment, e.g. test."),
|
|
24
|
+
node: str = typer.Option(None, "--node", help="Submit only this node type."),
|
|
25
|
+
on: Target = typer.Option(Target.local, "--on", help="Run local or on ec2."),
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Upload a study's release metadata to Gen3 sheepdog.
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
g3dt metadata upload --study ausdiab --env staging
|
|
31
|
+
g3dt metadata upload --study ausdiab --env staging --on ec2
|
|
32
|
+
"""
|
|
33
|
+
s = study_of(study, env)
|
|
34
|
+
|
|
35
|
+
def build_args(env_name):
|
|
36
|
+
a = ["--study", s.key, "--env", env_name]
|
|
37
|
+
if node:
|
|
38
|
+
a += ["--specific-node", node]
|
|
39
|
+
return a
|
|
40
|
+
|
|
41
|
+
def remote_cli(env_name):
|
|
42
|
+
a = ["metadata", "upload", "--study", study, "--env", env_name]
|
|
43
|
+
if node:
|
|
44
|
+
a += ["--node", node]
|
|
45
|
+
return a
|
|
46
|
+
|
|
47
|
+
dispatch.run_or_dispatch(
|
|
48
|
+
on, env, _UPLOAD, build_args, "metadata-upload", remote_cli=remote_cli,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@app.command(name="upload-all")
|
|
53
|
+
def upload_all(
|
|
54
|
+
studies: str = typer.Option(
|
|
55
|
+
..., "--studies", help="Comma-separated studies, e.g. ausdiab,caughtcad."
|
|
56
|
+
),
|
|
57
|
+
env: str = typer.Option(..., "--env", "-e", help="Environment, e.g. test."),
|
|
58
|
+
on: Target = typer.Option(Target.local, "--on", help="Run local or on ec2."),
|
|
59
|
+
) -> None:
|
|
60
|
+
"""Upload several studies sequentially (wraps upload_all_studies.sh).
|
|
61
|
+
|
|
62
|
+
The wrapped script aborts on any 'prod' environment.
|
|
63
|
+
"""
|
|
64
|
+
names = [s.strip() for s in studies.split(",") if s.strip()]
|
|
65
|
+
keys = [study_of(name, env).key for name in names]
|
|
66
|
+
|
|
67
|
+
def build_args(env_name):
|
|
68
|
+
return ["--studies", ",".join(keys), "--env", env_name]
|
|
69
|
+
|
|
70
|
+
def remote_cli(env_name):
|
|
71
|
+
return ["metadata", "upload-all", "--studies", studies, "--env", env_name]
|
|
72
|
+
|
|
73
|
+
dispatch.run_or_dispatch(
|
|
74
|
+
on, env, _UPLOAD_ALL, build_args, "metadata-upload-all",
|
|
75
|
+
interpreter="bash", remote_cli=remote_cli,
|
|
76
|
+
)
|
g3dt/cli/synth.py
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"""`g3dt synth` — synthetic data lifecycle for any configured environment.
|
|
2
|
+
|
|
3
|
+
Generation uses **gen3-metadata-simulator** (schema-valid). It runs locally:
|
|
4
|
+
synthetic metadata is generated on the laptop (writing under
|
|
5
|
+
``~/.g3dt/synth_metadata/<version>/<study>/``) and uploaded/deleted from
|
|
6
|
+
there, so there is nothing to run on EC2.
|
|
7
|
+
|
|
8
|
+
Every command accepts ``--env``; targeting a **production** environment (any env
|
|
9
|
+
whose name contains ``prod``) shows a warning and requires typing the env name to
|
|
10
|
+
confirm — it cannot be bypassed.
|
|
11
|
+
|
|
12
|
+
Generation defaults to keyless ``random`` data (no API calls). Pass ``--llm`` for
|
|
13
|
+
LLM-realistic values, which needs an API key configured in a ``.env`` in the
|
|
14
|
+
working directory (``LLM_PROVIDER`` / ``LLM_MODEL`` / ``LLM_API_KEY_FILE``).
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
import typer
|
|
21
|
+
|
|
22
|
+
from g3dt.config import script_env
|
|
23
|
+
from g3dt.cli._internal import runner, safety
|
|
24
|
+
from g3dt.cli._internal.resolve import env_of
|
|
25
|
+
from g3dt.cli.dict_cmds import SCHEMA_DIR, dict_url
|
|
26
|
+
|
|
27
|
+
app = typer.Typer(
|
|
28
|
+
no_args_is_help=True,
|
|
29
|
+
help="Synthetic data lifecycle (local generation; prod requires typed confirmation).",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
SYNTH_DIR = Path("~/.g3dt/synth_metadata").expanduser()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.command()
|
|
36
|
+
def deploy(
|
|
37
|
+
env: str = typer.Option(
|
|
38
|
+
"test", "--env", "-e", help="Target environment (prod requires typed confirmation)."
|
|
39
|
+
),
|
|
40
|
+
) -> None:
|
|
41
|
+
"""Full end-to-end synthetic deploy (dict + LLM-generate + upload + restarts).
|
|
42
|
+
|
|
43
|
+
Wraps services/synthetic_data/full_deploy_dd_and_synth.sh (LLM-backed
|
|
44
|
+
generation). Requires an LLM key configured in .env.
|
|
45
|
+
"""
|
|
46
|
+
e = env_of(env)
|
|
47
|
+
safety.confirm_prod_strict("synthetic full deploy", env)
|
|
48
|
+
runner.run(
|
|
49
|
+
runner.bash_script(
|
|
50
|
+
"services/synthetic_data/full_deploy_dd_and_synth.sh", env
|
|
51
|
+
),
|
|
52
|
+
env=script_env(e),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@app.command()
|
|
57
|
+
def generate(
|
|
58
|
+
studies: str = typer.Argument(
|
|
59
|
+
...,
|
|
60
|
+
help="Simulated study id(s); comma-separated for many, e.g. AusDiab_Simulated.",
|
|
61
|
+
),
|
|
62
|
+
env: str = typer.Option(
|
|
63
|
+
"test", "--env", "-e", help="Target environment (prod requires typed confirmation)."
|
|
64
|
+
),
|
|
65
|
+
num_records: str = typer.Option(
|
|
66
|
+
None,
|
|
67
|
+
"--num-records",
|
|
68
|
+
"-n",
|
|
69
|
+
help="Records per study: one number for all, or a comma list (one per study).",
|
|
70
|
+
),
|
|
71
|
+
provider: str = typer.Option(
|
|
72
|
+
"random", "--provider", help="Value strategy: 'random' (default, keyless) or 'llm'."
|
|
73
|
+
),
|
|
74
|
+
llm: bool = typer.Option(
|
|
75
|
+
False,
|
|
76
|
+
"--llm",
|
|
77
|
+
help="Generate LLM-realistic values; reads LLM config from a .env in the "
|
|
78
|
+
"working directory. Default is keyless random data (no API key, no API calls).",
|
|
79
|
+
),
|
|
80
|
+
seed: int = typer.Option(None, "--seed", help="RNG seed for reproducible output."),
|
|
81
|
+
schema: str = typer.Option(
|
|
82
|
+
None, "--schema", help="Gen3 schema path (default: pulled for the version)."
|
|
83
|
+
),
|
|
84
|
+
version: str = typer.Option(
|
|
85
|
+
None, "--version", help="Version label for output dir (default: env dictionary_version)."
|
|
86
|
+
),
|
|
87
|
+
) -> None:
|
|
88
|
+
"""Generate synthetic metadata locally with gen3-metadata-simulator.
|
|
89
|
+
|
|
90
|
+
STUDIES is one simulated study id, or several comma-separated. Defaults to
|
|
91
|
+
keyless, schema-valid random data (no API key, no API calls). Pass --llm to
|
|
92
|
+
generate LLM-realistic values instead.
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
g3dt synth generate AusDiab_Simulated -n 5 --seed 1
|
|
96
|
+
g3dt synth generate AusDiab_Simulated --llm -n 5
|
|
97
|
+
g3dt synth generate "AusDiab_Simulated,Baker-Biobank_Simulated" -n "30,60"
|
|
98
|
+
"""
|
|
99
|
+
e = env_of(env)
|
|
100
|
+
safety.confirm_prod_strict("synthetic generation", env)
|
|
101
|
+
|
|
102
|
+
# A comma list of per-study counts must line up with the studies given.
|
|
103
|
+
if num_records and "," in num_records:
|
|
104
|
+
n_counts = len(num_records.split(","))
|
|
105
|
+
n_studies = len(studies.split(","))
|
|
106
|
+
if n_counts != n_studies:
|
|
107
|
+
typer.secho(
|
|
108
|
+
f"--num-records has {n_counts} values but {n_studies} studies "
|
|
109
|
+
f"were given (pass one count, or one per study).",
|
|
110
|
+
fg=typer.colors.RED,
|
|
111
|
+
err=True,
|
|
112
|
+
)
|
|
113
|
+
raise typer.Exit(1)
|
|
114
|
+
|
|
115
|
+
ver = version or e.dictionary_version
|
|
116
|
+
schema_path = schema or str(SCHEMA_DIR / f"acdc_schema_{ver}.json")
|
|
117
|
+
|
|
118
|
+
# Ensure the schema is available locally; pull it if missing.
|
|
119
|
+
if not Path(schema_path).exists():
|
|
120
|
+
typer.secho(f"Schema not found locally; pulling {ver}...", fg=typer.colors.YELLOW)
|
|
121
|
+
runner.run(
|
|
122
|
+
runner.bash_script("services/dictionary/pull_dict.sh", dict_url(e, ver)),
|
|
123
|
+
env=script_env(e),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
effective_provider = "llm" if llm else provider
|
|
127
|
+
args = [
|
|
128
|
+
"--schema", schema_path,
|
|
129
|
+
"--version", ver,
|
|
130
|
+
"--provider", effective_provider,
|
|
131
|
+
"--studies", studies,
|
|
132
|
+
]
|
|
133
|
+
if num_records:
|
|
134
|
+
args += ["--num-records", num_records]
|
|
135
|
+
if seed is not None:
|
|
136
|
+
args += ["--seed", str(seed)]
|
|
137
|
+
runner.run(
|
|
138
|
+
runner.bash_script(
|
|
139
|
+
"services/synthetic_data/generate_synth_metadata.sh", *args
|
|
140
|
+
),
|
|
141
|
+
env=script_env(e),
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@app.command()
|
|
146
|
+
def upload(
|
|
147
|
+
env: str = typer.Option(
|
|
148
|
+
"test", "--env", "-e", help="Target environment (prod requires typed confirmation)."
|
|
149
|
+
),
|
|
150
|
+
version: str = typer.Option(
|
|
151
|
+
None, "--version", help="Dictionary version dir (default: the env's version)."
|
|
152
|
+
),
|
|
153
|
+
) -> None:
|
|
154
|
+
"""Upload generated synthetic metadata to Gen3 (reads local files)."""
|
|
155
|
+
e = env_of(env)
|
|
156
|
+
safety.confirm_prod_strict("synthetic metadata upload", env)
|
|
157
|
+
v = version or e.dictionary_version
|
|
158
|
+
base_dir = str(SYNTH_DIR / v) + "/"
|
|
159
|
+
args = ["--base-dir", base_dir, "--aws-secret-name", e.aws_secret_name]
|
|
160
|
+
if e.aws_profile:
|
|
161
|
+
args += ["--aws-profile", e.aws_profile]
|
|
162
|
+
runner.run(
|
|
163
|
+
runner.python_script(
|
|
164
|
+
"services/synthetic_data/upload_synth_metadata_sheepdog.py", *args
|
|
165
|
+
),
|
|
166
|
+
env=script_env(e),
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@app.command()
|
|
171
|
+
def delete(
|
|
172
|
+
env: str = typer.Option(
|
|
173
|
+
"test", "--env", "-e", help="Target environment (prod requires typed confirmation)."
|
|
174
|
+
),
|
|
175
|
+
projects: str = typer.Option(
|
|
176
|
+
None, "--projects", "-p", help="Comma-separated simulated project ids."
|
|
177
|
+
),
|
|
178
|
+
import_order: str = typer.Option(
|
|
179
|
+
None,
|
|
180
|
+
"--import-order",
|
|
181
|
+
help="DataImportOrder.txt path (default: DataImportOrder.txt in the cwd).",
|
|
182
|
+
),
|
|
183
|
+
) -> None:
|
|
184
|
+
"""Delete previously-uploaded synthetic metadata from Gen3."""
|
|
185
|
+
e = env_of(env)
|
|
186
|
+
safety.confirm_prod_strict("synthetic metadata deletion", env)
|
|
187
|
+
order = import_order or "DataImportOrder.txt"
|
|
188
|
+
args = ["-i", order, "-s", e.aws_secret_name]
|
|
189
|
+
if e.aws_profile:
|
|
190
|
+
args += ["-profile", e.aws_profile]
|
|
191
|
+
if projects:
|
|
192
|
+
args += ["-p", projects]
|
|
193
|
+
runner.run(
|
|
194
|
+
runner.python_script(
|
|
195
|
+
"services/synthetic_data/delete_synth_metadata_sheepdog.py", *args
|
|
196
|
+
),
|
|
197
|
+
env=script_env(e),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@app.command(name="install-simulator")
|
|
202
|
+
def install_simulator() -> None:
|
|
203
|
+
"""Install the gen3-metadata-simulator generator (the 'synth' extra)."""
|
|
204
|
+
import sys
|
|
205
|
+
|
|
206
|
+
runner.run([sys.executable, "-m", "pip", "install", "gen3-metadata-simulator"])
|