aepp 0.5.1.post1__tar.gz → 0.5.2.post1__tar.gz
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.
- {aepp-0.5.1.post1/aepp.egg-info → aepp-0.5.2.post1}/PKG-INFO +2 -1
- aepp-0.5.2.post1/aepp/__version__.py +1 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/classmanager.py +59 -29
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/cli/__main__.py +198 -34
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/datatypemanager.py +60 -27
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/fieldgroupmanager.py +100 -65
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/schemamanager.py +140 -78
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/synchronizer.py +77 -57
- {aepp-0.5.1.post1 → aepp-0.5.2.post1/aepp.egg-info}/PKG-INFO +2 -1
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp.egg-info/requires.txt +1 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/pyproject.toml +2 -1
- aepp-0.5.1.post1/aepp/__version__.py +0 -1
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/LICENSE +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/MANIFEST.in +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/README.md +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/__init__.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/accesscontrol.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/catalog.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/cli/__init__.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/config.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/configs.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/connector.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/customerprofile.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/dataaccess.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/dataprep.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/datasets.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/deletion.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/destination.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/destinationinstanceservice.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/edge.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/exportDatasetToDataLandingZone.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/flowservice.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/hygiene.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/identity.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/ingestion.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/observability.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/policy.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/privacyservice.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/queryservice.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/sandboxes.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/schema.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/segmentation.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/sensei.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/som.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/tags.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp/utils.py +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp.egg-info/SOURCES.txt +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp.egg-info/dependency_links.txt +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp.egg-info/entry_points.txt +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/aepp.egg-info/top_level.txt +0 -0
- {aepp-0.5.1.post1 → aepp-0.5.2.post1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aepp
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2.post1
|
|
4
4
|
Summary: Package to manage AEP API endpoint and some helper functions
|
|
5
5
|
Author-email: Julien Piccini <piccini.julien@gmail.com>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -22,6 +22,7 @@ Requires-Dist: tenacity
|
|
|
22
22
|
Requires-Dist: deprecation
|
|
23
23
|
Requires-Dist: datamodel-code-generator
|
|
24
24
|
Requires-Dist: rich
|
|
25
|
+
Requires-Dist: matplotlib
|
|
25
26
|
Dynamic: license-file
|
|
26
27
|
|
|
27
28
|
# Adobe Experience Platform API made for humans
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.5.2-1"
|
|
@@ -23,7 +23,7 @@ class ClassManager:
|
|
|
23
23
|
schemaAPI:'Schema'=None,
|
|
24
24
|
config: Union[dict,ConnectObject] = aepp.config.config_object,
|
|
25
25
|
description: str = 'power by aepp',
|
|
26
|
-
localFolder:str=None,
|
|
26
|
+
localFolder:str | list | None = None,
|
|
27
27
|
sandbox:str=None,
|
|
28
28
|
**kwargs
|
|
29
29
|
)->None:
|
|
@@ -48,13 +48,16 @@ class ClassManager:
|
|
|
48
48
|
elif config is not None and localFolder is None:
|
|
49
49
|
self.schemaAPI = Schema(config=config)
|
|
50
50
|
elif localFolder is not None:
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
if isinstance(localFolder, str):
|
|
52
|
+
self.localfolder = [Path(localFolder)]
|
|
53
|
+
elif isinstance(localFolder, list):
|
|
54
|
+
self.localfolder = [Path(lf) for lf in localFolder]
|
|
55
|
+
if any([folder.exists() is False for folder in self.localfolder]):
|
|
53
56
|
raise Exception(f"The local folder {self.localfolder} does not exist. Please create it and extract your sandbox before using it.")
|
|
54
57
|
self.schemaAPI = None
|
|
55
|
-
self.classFolder =
|
|
56
|
-
self.behavFolder =
|
|
57
|
-
if
|
|
58
|
+
self.classFolder = [folder / 'class' for folder in self.localfolder]
|
|
59
|
+
self.behavFolder = [folder / 'behaviour' for folder in self.localfolder]
|
|
60
|
+
if any([folder.exists() is False for folder in self.classFolder]):
|
|
58
61
|
raise Exception(f"The local folder {self.classFolder} does not exist. Please create it and extract your sandbox before using it.")
|
|
59
62
|
else:
|
|
60
63
|
raise Exception("You need to provide a schemaAPI instance or a config object to connect to the API or a local folder to use the local storage")
|
|
@@ -71,9 +74,11 @@ class ClassManager:
|
|
|
71
74
|
elif type(aepclass) == dict:
|
|
72
75
|
self.tenantId = aepclass.get('meta:tenantNamespace'," ")
|
|
73
76
|
elif self.localfolder is not None:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
+
for folder in self.localfolder:
|
|
78
|
+
config_json = json.load(FileIO(folder / 'config.json'))
|
|
79
|
+
if config_json.get('tenantId',None) is not None:
|
|
80
|
+
self.tenantId = config_json.get('tenantId')
|
|
81
|
+
break
|
|
77
82
|
else:
|
|
78
83
|
self.tenantId = " "
|
|
79
84
|
if type(aepclass) == dict:
|
|
@@ -82,10 +87,15 @@ class ClassManager:
|
|
|
82
87
|
self.aepclass = self.schemaAPI.getClass(aepclass['$id'],full=False,xtype='xed')
|
|
83
88
|
self.EDITABLE = True
|
|
84
89
|
elif self.localfolder is not None:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
found = False
|
|
91
|
+
for folder in self.classFolder:
|
|
92
|
+
for json_file in folder.glob('*.json'):
|
|
93
|
+
tmp_def = json.load(FileIO(json_file))
|
|
94
|
+
if tmp_def.get('$id') == aepclass['$id']:
|
|
95
|
+
self.aepclass = tmp_def
|
|
96
|
+
found = True
|
|
97
|
+
break
|
|
98
|
+
if found:
|
|
89
99
|
break
|
|
90
100
|
self.EDITABLE = False
|
|
91
101
|
elif self.tenantId[1:] not in aepclass['$id']:
|
|
@@ -93,10 +103,15 @@ class ClassManager:
|
|
|
93
103
|
self.aepclass = self.schemaAPI.getClass(aepclass['$id'],full=True,xtype='xed')
|
|
94
104
|
self.EDITABLE = False
|
|
95
105
|
elif self.localfolder is not None:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
found = False
|
|
107
|
+
for folder in self.classFolder:
|
|
108
|
+
for json_file in folder.glob('*.json'):
|
|
109
|
+
tmp_def = json.load(FileIO(json_file))
|
|
110
|
+
if tmp_def.get('$id') == aepclass['$id']:
|
|
111
|
+
self.aepclass = tmp_def
|
|
112
|
+
found = True
|
|
113
|
+
break
|
|
114
|
+
if found:
|
|
100
115
|
break
|
|
101
116
|
self.EDITABLE = False
|
|
102
117
|
self.__setAttributes__(self.aepclass)
|
|
@@ -106,20 +121,30 @@ class ClassManager:
|
|
|
106
121
|
self.aepclass = self.schemaAPI.getClass(aepclass,full=False,xtype='xed')
|
|
107
122
|
self.EDITABLE = True
|
|
108
123
|
elif self.localfolder is not None:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
124
|
+
found = False
|
|
125
|
+
for folder in self.classFolder:
|
|
126
|
+
for json_file in folder.glob('*.json'):
|
|
127
|
+
tmp_def = json.load(FileIO(json_file))
|
|
128
|
+
if tmp_def.get('$id') == aepclass or tmp_def.get('meta:altId') == aepclass:
|
|
129
|
+
self.aepclass = tmp_def
|
|
130
|
+
found = True
|
|
131
|
+
break
|
|
132
|
+
if found:
|
|
113
133
|
break
|
|
114
134
|
self.EDITABLE = False
|
|
115
135
|
else:
|
|
116
136
|
if self.schemaAPI is not None:
|
|
117
137
|
self.aepclass = self.schemaAPI.getClass(aepclass,full=True,xtype='xed')
|
|
118
138
|
elif self.localfolder is not None:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
found = False
|
|
140
|
+
for folder in self.classFolder:
|
|
141
|
+
for json_file in folder.glob('*.json'):
|
|
142
|
+
tmp_def = json.load(FileIO(json_file))
|
|
143
|
+
if tmp_def.get('$id') == aepclass or tmp_def.get('meta:altId') == aepclass:
|
|
144
|
+
self.aepclass = tmp_def
|
|
145
|
+
found = True
|
|
146
|
+
break
|
|
147
|
+
if found:
|
|
123
148
|
break
|
|
124
149
|
self.EDITABLE = False
|
|
125
150
|
self.__setAttributes__(self.aepclass)
|
|
@@ -168,10 +193,15 @@ class ClassManager:
|
|
|
168
193
|
if self.schemaAPI is not None:
|
|
169
194
|
self.behaviorDefinition = self.schemaAPI.getBehavior(behavId,full=True,xtype='xed')
|
|
170
195
|
elif self.localfolder is not None:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
196
|
+
found = False
|
|
197
|
+
for folder in self.behavFolder:
|
|
198
|
+
for json_file in folder.glob('*.json'):
|
|
199
|
+
tmp_def = json.load(FileIO(json_file))
|
|
200
|
+
if tmp_def.get('$id') == behavId:
|
|
201
|
+
self.behaviorDefinition = tmp_def
|
|
202
|
+
found = True
|
|
203
|
+
break
|
|
204
|
+
if found:
|
|
175
205
|
break
|
|
176
206
|
self.requiredFields = set()
|
|
177
207
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from ast import arg
|
|
2
2
|
from matplotlib.pyplot import table
|
|
3
3
|
import aepp
|
|
4
|
-
from aepp import synchronizer, schema, schemamanager, fieldgroupmanager, datatypemanager, identity, queryservice,catalog,flowservice
|
|
4
|
+
from aepp import synchronizer, schema, schemamanager, fieldgroupmanager, datatypemanager, identity, queryservice,catalog,flowservice,sandboxes, segmentation
|
|
5
5
|
import argparse, cmd, shlex, json
|
|
6
6
|
from functools import wraps
|
|
7
7
|
from rich.console import Console
|
|
@@ -47,26 +47,25 @@ class ServiceShell(cmd.Cmd):
|
|
|
47
47
|
self.sandbox = str(dict_config.get("sandbox-name","prod"))
|
|
48
48
|
else:
|
|
49
49
|
self.sandbox = str(kwargs.get("sandbox","prod"))
|
|
50
|
-
self.secret =
|
|
51
|
-
self.org_id =
|
|
52
|
-
self.client_id =
|
|
53
|
-
self.scopes =
|
|
50
|
+
self.secret = dict_config.get("secret",kwargs.get("secret"))
|
|
51
|
+
self.org_id = dict_config.get("org_id",kwargs.get("org_id"))
|
|
52
|
+
self.client_id = dict_config.get("client_id",kwargs.get("client_id"))
|
|
53
|
+
self.scopes = dict_config.get("scopes",kwargs.get("scopes"))
|
|
54
54
|
else:
|
|
55
|
-
self.sandbox =
|
|
56
|
-
self.secret =
|
|
57
|
-
self.org_id =
|
|
58
|
-
self.client_id =
|
|
59
|
-
self.scopes =
|
|
60
|
-
self.connectInstance = True
|
|
55
|
+
self.sandbox:str|None = kwargs.get("sandbox","prod")
|
|
56
|
+
self.secret:str|None = kwargs.get("secret")
|
|
57
|
+
self.org_id:str|None = kwargs.get("org_id")
|
|
58
|
+
self.client_id:str|None = kwargs.get("client_id")
|
|
59
|
+
self.scopes:str|None = kwargs.get("scopes")
|
|
61
60
|
if self.sandbox is not None and self.secret is not None and self.org_id is not None and self.client_id is not None and self.scopes is not None:
|
|
62
61
|
print("Configuring connection...")
|
|
63
62
|
self.config = aepp.configure(
|
|
64
|
-
connectInstance=self.connectInstance,
|
|
65
63
|
sandbox=self.sandbox,
|
|
66
64
|
secret=self.secret,
|
|
67
65
|
org_id=self.org_id,
|
|
68
66
|
client_id=self.client_id,
|
|
69
|
-
scopes=self.scopes
|
|
67
|
+
scopes=self.scopes,
|
|
68
|
+
connectInstance=self.connectInstance
|
|
70
69
|
)
|
|
71
70
|
self.prompt = f"{self.config.sandbox}> "
|
|
72
71
|
console.print(Panel(f"Connected to [bold green]{self.sandbox}[/bold green]", style="blue"))
|
|
@@ -82,7 +81,6 @@ class ServiceShell(cmd.Cmd):
|
|
|
82
81
|
console.print(f"Configuration file created at {Path.cwd() / Path(filename_json)}", style="green")
|
|
83
82
|
return
|
|
84
83
|
|
|
85
|
-
|
|
86
84
|
# # --- Commands ---
|
|
87
85
|
def do_config(self, arg:Any) -> None:
|
|
88
86
|
"""connect to an AEP instance"""
|
|
@@ -94,14 +92,14 @@ class ServiceShell(cmd.Cmd):
|
|
|
94
92
|
parser.add_argument("-cid", "--client_id", help="Auto-login client ID")
|
|
95
93
|
parser.add_argument("-cf", "--config_file", help="Path to config file", default=None)
|
|
96
94
|
args = parser.parse_args(shlex.split(arg))
|
|
97
|
-
if args.config_file:
|
|
95
|
+
if args.config_file is not None:
|
|
98
96
|
mypath = Path.cwd()
|
|
99
97
|
dict_config = json.load(FileIO(mypath / Path(args.config_file)))
|
|
100
|
-
self.sandbox =
|
|
101
|
-
self.secret =
|
|
102
|
-
self.org_id =
|
|
103
|
-
self.client_id =
|
|
104
|
-
self.scopes =
|
|
98
|
+
self.sandbox:str|None = args.sandbox if args.sandbox else dict_config.get("sandbox-name",args.sandbox)
|
|
99
|
+
self.secret:str|None = dict_config.get("secret",args.secret)
|
|
100
|
+
self.org_id:str|None = dict_config.get("org_id",args.org_id)
|
|
101
|
+
self.client_id:str|None = dict_config.get("client_id",args.client_id)
|
|
102
|
+
self.scopes:str|None = dict_config.get("scopes",args.scopes)
|
|
105
103
|
self.connectInstance = True
|
|
106
104
|
else:
|
|
107
105
|
if args.sandbox: self.sandbox = str(args.sandbox)
|
|
@@ -136,6 +134,42 @@ class ServiceShell(cmd.Cmd):
|
|
|
136
134
|
else:
|
|
137
135
|
console.print(Panel("(!) You must configure the connection first using the 'config' command.", style="red"))
|
|
138
136
|
|
|
137
|
+
@login_required
|
|
138
|
+
def do_get_sandboxes(self, args:Any) -> None:
|
|
139
|
+
"""List all sandboxes for the current organization"""
|
|
140
|
+
parser = argparse.ArgumentParser(prog='get_sandboxes', add_help=True)
|
|
141
|
+
parser.add_argument("-sv", "--save",help="Save sandboxes to CSV file")
|
|
142
|
+
try:
|
|
143
|
+
args = parser.parse_args(shlex.split(args))
|
|
144
|
+
aepp_sandboxes = sandboxes.Sandboxes(config=self.config)
|
|
145
|
+
sandboxes_list = aepp_sandboxes.getSandboxes()
|
|
146
|
+
if sandboxes_list:
|
|
147
|
+
table = Table(title=f"Sandboxes in Org: {self.config.org_id}")
|
|
148
|
+
table.add_column("Name", style="cyan")
|
|
149
|
+
table.add_column("Title", style="magenta")
|
|
150
|
+
table.add_column("Type", style="green")
|
|
151
|
+
table.add_column("Region", style="yellow")
|
|
152
|
+
table.add_column("Created", style="medium_violet_red")
|
|
153
|
+
for sb in sandboxes_list:
|
|
154
|
+
table.add_row(
|
|
155
|
+
sb.get("name","N/A"),
|
|
156
|
+
sb.get("title","N/A"),
|
|
157
|
+
sb.get("type","N/A"),
|
|
158
|
+
sb.get("region","N/A"),
|
|
159
|
+
sb.get("createdDate","N/A"),
|
|
160
|
+
)
|
|
161
|
+
console.print(table)
|
|
162
|
+
if args.save:
|
|
163
|
+
df_sandboxes = pd.DataFrame(sandboxes_list)
|
|
164
|
+
df_sandboxes.to_csv(f"sandboxes_{self.config.org_id}.csv", index=False)
|
|
165
|
+
console.print(f"Sandboxes exported to sandboxes_{self.config.org_id}.csv", style="green")
|
|
166
|
+
else:
|
|
167
|
+
console.print("(!) No sandboxes found.", style="red")
|
|
168
|
+
except Exception as e:
|
|
169
|
+
console.print(f"(!) Error: {str(e)}", style="red")
|
|
170
|
+
except SystemExit:
|
|
171
|
+
return
|
|
172
|
+
|
|
139
173
|
|
|
140
174
|
@login_required
|
|
141
175
|
def do_get_schemas(self, args:Any) -> None:
|
|
@@ -740,7 +774,7 @@ class ServiceShell(cmd.Cmd):
|
|
|
740
774
|
ds.get("name","N/A"),
|
|
741
775
|
datetime.fromtimestamp(ds.get("created",1000)/1000).isoformat().split('T')[0],
|
|
742
776
|
str(ds.get("dataIngested",False)),
|
|
743
|
-
ds.get(
|
|
777
|
+
ds.get('classification').get('dataBehavior','N/A'),
|
|
744
778
|
)
|
|
745
779
|
console.print(table)
|
|
746
780
|
except Exception as e:
|
|
@@ -748,6 +782,74 @@ class ServiceShell(cmd.Cmd):
|
|
|
748
782
|
except SystemExit:
|
|
749
783
|
return
|
|
750
784
|
|
|
785
|
+
@login_required
|
|
786
|
+
def do_get_datasets_tableName(self, args:Any) -> None:
|
|
787
|
+
parser = argparse.ArgumentParser(prog='get_datasets', add_help=True)
|
|
788
|
+
try:
|
|
789
|
+
args = parser.parse_args(shlex.split(args))
|
|
790
|
+
aepp_cat = catalog.Catalog(config=self.config)
|
|
791
|
+
datasets = aepp_cat.getDataSets(output='list')
|
|
792
|
+
table = Table(title=f"Datasets in Sandbox: {self.config.sandbox}")
|
|
793
|
+
table.add_column("Name", style="white")
|
|
794
|
+
table.add_column("Table Name", style="cyan",no_wrap=True)
|
|
795
|
+
table.add_column("Data Type", style="red")
|
|
796
|
+
for ds in datasets:
|
|
797
|
+
table.add_row(
|
|
798
|
+
ds.get("name","N/A"),
|
|
799
|
+
ds.get('tags',{}).get('adobe/pqs/table',["N/A"])[0],
|
|
800
|
+
ds.get('classification').get('dataBehavior','N/A'),
|
|
801
|
+
)
|
|
802
|
+
console.print(table)
|
|
803
|
+
except Exception as e:
|
|
804
|
+
console.print(f"(!) Error: {str(e)}", style="red")
|
|
805
|
+
except SystemExit:
|
|
806
|
+
return
|
|
807
|
+
|
|
808
|
+
@login_required
|
|
809
|
+
def do_get_observable_schema_json(self,args:Any) -> None:
|
|
810
|
+
"""Get the observable schema for a dataset by name or ID"""
|
|
811
|
+
parser = argparse.ArgumentParser(prog='get_observable_schema', add_help=True)
|
|
812
|
+
parser.add_argument("dataset", help="Dataset ID or Dataset Name to retrieve observable schema for",type=str)
|
|
813
|
+
try:
|
|
814
|
+
args = parser.parse_args(shlex.split(args))
|
|
815
|
+
aepp_cat = catalog.Catalog(config=self.config)
|
|
816
|
+
datasets = aepp_cat.getDataSets(output='list')
|
|
817
|
+
for ds in datasets:
|
|
818
|
+
if ds.get("name","") == args.dataset or ds.get("id","") == args.dataset:
|
|
819
|
+
datasetId = ds.get("id")
|
|
820
|
+
schema_json = aepp_cat.getDataSetObservableSchema(datasetId=datasetId,appendDatasetInfo=True)
|
|
821
|
+
myObs = catalog.ObservableSchemaManager(schema_json,config=self.config)
|
|
822
|
+
data = myObs.to_dict()
|
|
823
|
+
with open(f"{args.dataset}_observable_schema.json", 'w') as f:
|
|
824
|
+
json.dump(data, f, indent=4)
|
|
825
|
+
console.print(f"Saved Observable schema to {args.dataset}_observable_schema.json.", style="green")
|
|
826
|
+
except Exception as e:
|
|
827
|
+
console.print(f"(!) Error: {str(e)}", style="red")
|
|
828
|
+
except SystemExit:
|
|
829
|
+
return
|
|
830
|
+
|
|
831
|
+
@login_required
|
|
832
|
+
def do_get_observable_schema_csv(self,args:Any) -> None:
|
|
833
|
+
"""Get the observable schema for a dataset by name or ID"""
|
|
834
|
+
parser = argparse.ArgumentParser(prog='get_observable_schema', add_help=True)
|
|
835
|
+
parser.add_argument("dataset", help="Dataset ID or Dataset Name to retrieve observable schema for",type=str)
|
|
836
|
+
try:
|
|
837
|
+
args = parser.parse_args(shlex.split(args))
|
|
838
|
+
aepp_cat = catalog.Catalog(config=self.config)
|
|
839
|
+
datasets = aepp_cat.getDataSets(output='list')
|
|
840
|
+
for ds in datasets:
|
|
841
|
+
if ds.get("name","") == args.dataset or ds.get("id","") == args.dataset:
|
|
842
|
+
datasetId = ds.get("id")
|
|
843
|
+
schema_json = aepp_cat.getDataSetObservableSchema(datasetId=datasetId,appendDatasetInfo=True)
|
|
844
|
+
myObs = catalog.ObservableSchemaManager(schema_json,config=self.config)
|
|
845
|
+
data = myObs.to_dataframe()
|
|
846
|
+
data.to_csv(f"{args.dataset}_observable_schema.csv", index=False)
|
|
847
|
+
console.print(f"Saved Observable schema to {args.dataset}_observable_schema.csv.", style="green")
|
|
848
|
+
except Exception as e:
|
|
849
|
+
console.print(f"(!) Error: {str(e)}", style="red")
|
|
850
|
+
except SystemExit:
|
|
851
|
+
return
|
|
852
|
+
|
|
751
853
|
@login_required
|
|
752
854
|
def do_get_datasets_infos(self, args:Any) -> None:
|
|
753
855
|
"""List all datasets in the current sandbox"""
|
|
@@ -756,23 +858,58 @@ class ServiceShell(cmd.Cmd):
|
|
|
756
858
|
args = parser.parse_args(shlex.split(args))
|
|
757
859
|
aepp_cat = catalog.Catalog(config=self.config)
|
|
758
860
|
datasets = aepp_cat.getDataSets()
|
|
861
|
+
aepp_cat.data.infos = aepp_cat.data.infos.sort_values(by=['ups_storageSize','datalake_storageSize'], ascending=False)
|
|
759
862
|
aepp_cat.data.infos.to_csv(f"{aepp_cat.sandbox}_datasets_infos.csv",index=False)
|
|
760
863
|
console.print(f"Datasets infos exported to {aepp_cat.sandbox}_datasets_infos.csv", style="green")
|
|
761
864
|
table = Table(title=f"Datasets in Sandbox: {self.config.sandbox}")
|
|
762
865
|
table.add_column("ID", style="white")
|
|
763
866
|
table.add_column("Name", style="white",no_wrap=True)
|
|
764
|
-
table.add_column("
|
|
765
|
-
table.add_column("
|
|
766
|
-
table.add_column("
|
|
767
|
-
table.add_column("
|
|
867
|
+
table.add_column("UPS Rows", style="cyan")
|
|
868
|
+
table.add_column("UPS Storage Size", style="green")
|
|
869
|
+
table.add_column("Datalake Rows", style="magenta")
|
|
870
|
+
table.add_column("Datalake Storage Size", style="yellow")
|
|
768
871
|
for _, ds in aepp_cat.data.infos.iterrows():
|
|
769
872
|
table.add_row(
|
|
770
873
|
ds.get("id","N/A"),
|
|
771
874
|
ds.get("name","N/A"),
|
|
875
|
+
str(ds.get("ups_rows","N/A")),
|
|
876
|
+
str(ds.get("ups_storageSize","N/A")),
|
|
772
877
|
str(ds.get("datalake_rows","N/A")),
|
|
773
878
|
str(ds.get("datalake_storageSize","N/A")),
|
|
774
|
-
|
|
775
|
-
|
|
879
|
+
)
|
|
880
|
+
console.print(table)
|
|
881
|
+
except Exception as e:
|
|
882
|
+
console.print(f"(!) Error: {str(e)}", style="red")
|
|
883
|
+
except SystemExit:
|
|
884
|
+
return
|
|
885
|
+
|
|
886
|
+
@login_required
|
|
887
|
+
def do_get_snapshot_datasets(self,args:Any) -> None:
|
|
888
|
+
"""List all snapshot datasets in the current sandbox"""
|
|
889
|
+
parser = argparse.ArgumentParser(prog='get_snapshot_datasets', add_help=True)
|
|
890
|
+
try:
|
|
891
|
+
args = parser.parse_args(shlex.split(args))
|
|
892
|
+
aepp_cat = catalog.Catalog(config=self.config)
|
|
893
|
+
datasets = aepp_cat.getProfileSnapshotDatasets(explicitMergePolicy=True)
|
|
894
|
+
list_ds = []
|
|
895
|
+
for key, ds in datasets.items():
|
|
896
|
+
obj = ds
|
|
897
|
+
obj['id'] = key
|
|
898
|
+
list_ds.append(obj)
|
|
899
|
+
df_datasets = pd.DataFrame(list_ds)
|
|
900
|
+
df_datasets.to_csv(f"{self.config.sandbox}_snapshot_datasets.csv",index=False)
|
|
901
|
+
console.print(f"Snapshot Datasets exported to {self.config.sandbox}_snapshot_datasets.csv", style="green")
|
|
902
|
+
table = Table(title=f"Snapshot Datasets in Sandbox: {self.config.sandbox}")
|
|
903
|
+
table.add_column("ID", style="white")
|
|
904
|
+
table.add_column("Table Name", style="white")
|
|
905
|
+
table.add_column("Merge Policy Name", style="yellow")
|
|
906
|
+
table.add_column("Merge Policy ID", style="green")
|
|
907
|
+
for ds in list_ds:
|
|
908
|
+
table.add_row(
|
|
909
|
+
ds.get("id","N/A"),
|
|
910
|
+
ds.get("tags",{}).get('adobe/pqs/table',["N/A"])[0],
|
|
911
|
+
ds.get('mergePolicyName','N/A'),
|
|
912
|
+
[el.split(':')[1] for el in ds.get('tags',{}).get('unifiedProfile',[]) if el.startswith('mergePolicyId')][0]
|
|
776
913
|
)
|
|
777
914
|
console.print(table)
|
|
778
915
|
except Exception as e:
|
|
@@ -846,6 +983,37 @@ class ServiceShell(cmd.Cmd):
|
|
|
846
983
|
except SystemExit:
|
|
847
984
|
return
|
|
848
985
|
|
|
986
|
+
@login_required
|
|
987
|
+
def do_get_audiences(self, args:Any) -> None:
|
|
988
|
+
"""List all audiences in the current sandbox"""
|
|
989
|
+
parser = argparse.ArgumentParser(prog='get_audiences', add_help=True)
|
|
990
|
+
try:
|
|
991
|
+
args = parser.parse_args(shlex.split(args))
|
|
992
|
+
aepp_audience = segmentation.Segmentation(config=self.config)
|
|
993
|
+
audiences = aepp_audience.getAudiences()
|
|
994
|
+
df_audiences = pd.DataFrame(audiences)
|
|
995
|
+
df_audiences.to_csv(f"{self.config.sandbox}_audiences.csv",index=False)
|
|
996
|
+
console.print(f"Audiences exported to {self.config.sandbox}_audiences.csv", style="green")
|
|
997
|
+
table = Table(title=f"Audiences in Sandbox: {self.config.sandbox}")
|
|
998
|
+
table.add_column("ID", style="cyan")
|
|
999
|
+
table.add_column("Name", style="magenta")
|
|
1000
|
+
table.add_column("Evaluation", style="yellow")
|
|
1001
|
+
table.add_column("Total Profiles", style="green")
|
|
1002
|
+
table.add_column("Evaluation Date", style="white")
|
|
1003
|
+
for aud in audiences:
|
|
1004
|
+
table.add_row(
|
|
1005
|
+
aud.get("id","N/A"),
|
|
1006
|
+
aud.get("name","N/A"),
|
|
1007
|
+
'[red3]Batch[/red3]' if aud.get("evaluationInfo",{}).get("batch",{}).get('enabled') else '[chartreuse1]Streaming[/chartreuse1]' if aud.get("evaluationInfo",{}).get("continuous",{}).get('enabled') else '[blue_violet]Edge[/blue_violet]' if aud.get("evaluationInfo",{}).get("synchronous",{}).get('enabled') else 'N/A',
|
|
1008
|
+
str(aud.get('metrics',{}).get('data',{}).get('totalProfiles','N/A')),
|
|
1009
|
+
datetime.fromtimestamp(aud.get('metrics',{}).get('updateEpoch',0)).isoformat(),
|
|
1010
|
+
)
|
|
1011
|
+
console.print(table)
|
|
1012
|
+
except Exception as e:
|
|
1013
|
+
console.print(f"(!) Error: {str(e)}", style="red")
|
|
1014
|
+
except SystemExit:
|
|
1015
|
+
return
|
|
1016
|
+
|
|
849
1017
|
@login_required
|
|
850
1018
|
def do_get_flows(self, args:Any) -> None:
|
|
851
1019
|
"""List flows in the current sandbox based on parameters provided. By default, list all sources and destinations."""
|
|
@@ -1203,18 +1371,14 @@ class ServiceShell(cmd.Cmd):
|
|
|
1203
1371
|
console.print("Syncing artifact...", style="blue")
|
|
1204
1372
|
parser = argparse.ArgumentParser(prog='extractArtifact', description='Extract artifacts from AEP')
|
|
1205
1373
|
parser.add_argument('artifact', help='artifact to extract (name or id): "schema","fieldgroup","datatype","descriptor","dataset","identity","mergepolicy","audience"')
|
|
1206
|
-
parser.add_argument('-at','--artifactType', help='artifact type ')
|
|
1207
|
-
parser.add_argument('-t','--targets', help='target sandboxes')
|
|
1208
|
-
parser.add_argument('-lf','--localfolder', help='Local folder to extract artifacts to',default='extractions')
|
|
1374
|
+
parser.add_argument('-at','--artifactType', help='artifact type ',type=str)
|
|
1375
|
+
parser.add_argument('-t','--targets', help='target sandboxes',nargs='+',type=str)
|
|
1376
|
+
parser.add_argument('-lf','--localfolder', help='Local folder to extract artifacts to',default='extractions',nargs='+',type=str)
|
|
1209
1377
|
parser.add_argument('-b','--baseSandbox', help='Base sandbox for synchronization')
|
|
1210
1378
|
parser.add_argument('-rg','--region', help='Region to extract artifacts from: "ndl2" (default), "va7", "aus5", "can2", "ind2"',default='ndl2')
|
|
1211
1379
|
parser.add_argument('-v','--verbose', help='Enable verbose output',default=True)
|
|
1212
1380
|
try:
|
|
1213
1381
|
args = parser.parse_args(shlex.split(args))
|
|
1214
|
-
if ',' in args.targets:
|
|
1215
|
-
args.targets = args.targets.split(',')
|
|
1216
|
-
else:
|
|
1217
|
-
args.targets = [args.targets]
|
|
1218
1382
|
console.print("Initializing Synchronizor...", style="blue")
|
|
1219
1383
|
if args.baseSandbox:
|
|
1220
1384
|
synchronizor = synchronizer.Synchronizer(
|
|
@@ -36,7 +36,7 @@ class DataTypeManager:
|
|
|
36
36
|
schemaAPI:'Schema'=None,
|
|
37
37
|
config: Union[dict,ConnectObject] = aepp.config.config_object,
|
|
38
38
|
description:str="",
|
|
39
|
-
localFolder:str=None,
|
|
39
|
+
localFolder:str|list|None=None,
|
|
40
40
|
sandbox:str=None,
|
|
41
41
|
**kwargs
|
|
42
42
|
)->None:
|
|
@@ -65,10 +65,13 @@ class DataTypeManager:
|
|
|
65
65
|
elif config is not None and localFolder is None:
|
|
66
66
|
self.schemaAPI = Schema(config=config)
|
|
67
67
|
elif localFolder is not None:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
68
|
+
if isinstance(localFolder, str):
|
|
69
|
+
self.localfolder = [Path(localFolder)]
|
|
70
|
+
elif isinstance(localFolder, list):
|
|
71
|
+
self.localfolder = [Path(folder) for folder in localFolder]
|
|
72
|
+
self.datatypeFolder = [folder / 'datatype' for folder in self.localfolder]
|
|
73
|
+
self.datatypeGlobalFolder = [folder / 'global' for folder in self.datatypeFolder]
|
|
74
|
+
if any([folder.exists() is False for folder in self.localfolder]):
|
|
72
75
|
raise Exception(f"The local folder {self.localfolder} does not exist. Please create it and extract your sandbox before using it.")
|
|
73
76
|
self.schemaAPI = None
|
|
74
77
|
if self.schemaAPI is not None:
|
|
@@ -99,20 +102,32 @@ class DataTypeManager:
|
|
|
99
102
|
self.dataType = self.schemaAPI.getDataType(dataType['$id'],full=False)
|
|
100
103
|
self.EDITABLE = True
|
|
101
104
|
elif self.localfolder is not None:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
found = False
|
|
106
|
+
for folder in self.datatypeFolder:
|
|
107
|
+
for dataTypeFile in folder.glob(f"*.json"):
|
|
108
|
+
tmp_def = json.load(FileIO(dataTypeFile))
|
|
109
|
+
if tmp_def.get('$id') == dataType.get('$id') or tmp_def.get('meta:altId') == dataType.get('meta:altId'):
|
|
110
|
+
self.dataType = tmp_def
|
|
111
|
+
found = True
|
|
112
|
+
break
|
|
113
|
+
if found:
|
|
114
|
+
break
|
|
106
115
|
self.EDITABLE = False
|
|
107
116
|
else:
|
|
108
117
|
if self.schemaAPI is not None:
|
|
109
118
|
self.dataType = self.schemaAPI.getDataType(dataType['$id'],full=True)
|
|
110
119
|
self.EDITABLE = True
|
|
111
120
|
elif self.localfolder is not None:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
121
|
+
found = False
|
|
122
|
+
for folder in self.datatypeGlobalFolder:
|
|
123
|
+
for dataTypeFile in folder.glob("*.json"):
|
|
124
|
+
tmp_def = json.load(FileIO(dataTypeFile))
|
|
125
|
+
if tmp_def.get('$id') == dataType.get('$id') or tmp_def.get('meta:altId') == dataType.get('meta:altId') or tmp_def.get('title') == dataType.get('title'):
|
|
126
|
+
self.dataType = tmp_def
|
|
127
|
+
found = True
|
|
128
|
+
break
|
|
129
|
+
if found:
|
|
130
|
+
break
|
|
116
131
|
self.EDITABLE = False
|
|
117
132
|
elif type(dataType) == str:
|
|
118
133
|
if self.tenantId[1:] in dataType:
|
|
@@ -122,10 +137,16 @@ class DataTypeManager:
|
|
|
122
137
|
raise ValueError(f"Cannot find the data type with id {dataType} in the schema API.")
|
|
123
138
|
self.EDITABLE = True
|
|
124
139
|
elif self.localfolder is not None:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
140
|
+
found = False
|
|
141
|
+
for folder in self.datatypeFolder:
|
|
142
|
+
for dataTypeFile in folder.glob("*.json"):
|
|
143
|
+
tmp_def = json.load(FileIO(dataTypeFile))
|
|
144
|
+
if tmp_def.get('$id') == dataType or tmp_def.get('meta:altId') == dataType or tmp_def.get('title') == dataType:
|
|
145
|
+
self.dataType = tmp_def
|
|
146
|
+
found = True
|
|
147
|
+
break
|
|
148
|
+
if found:
|
|
149
|
+
break
|
|
129
150
|
self.EDITABLE = False
|
|
130
151
|
else:
|
|
131
152
|
raise Exception("You try to retrieve the datatype definition from the id, but no API or localFolder has been passed as a parameter.")
|
|
@@ -136,10 +157,16 @@ class DataTypeManager:
|
|
|
136
157
|
raise ValueError(f"Cannot find the data type with id {dataType} in the schema API.")
|
|
137
158
|
self.EDITABLE = True
|
|
138
159
|
elif self.localfolder is not None:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
160
|
+
found = False
|
|
161
|
+
for folder in self.datatypeGlobalFolder:
|
|
162
|
+
for dataTypeFile in folder.glob("*.json"):
|
|
163
|
+
tmp_def = json.load(FileIO(dataTypeFile))
|
|
164
|
+
if tmp_def.get('$id') == dataType or tmp_def.get('meta:altId') == dataType or tmp_def.get('title') == dataType:
|
|
165
|
+
self.dataType = tmp_def
|
|
166
|
+
found = True
|
|
167
|
+
break
|
|
168
|
+
if found:
|
|
169
|
+
break
|
|
143
170
|
self.EDITABLE = False
|
|
144
171
|
else:
|
|
145
172
|
raise Exception("You try to retrieve the datatype definition from the id, but no API or localFolder has been passed as a parameter.")
|
|
@@ -181,12 +208,18 @@ class DataTypeManager:
|
|
|
181
208
|
self.dataTypeManagers[dt_manager.title] = dt_manager
|
|
182
209
|
elif self.localfolder is not None:
|
|
183
210
|
for dt in dataTypes: ## today only searching custom data types in local folder
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
211
|
+
found = False
|
|
212
|
+
for folder in self.datatypeFolder:
|
|
213
|
+
for dataTypeFile in folder.glob("*.json"):
|
|
214
|
+
tmp_def = json.load(FileIO(dataTypeFile))
|
|
215
|
+
if tmp_def.get('$id') == dt or tmp_def.get('meta:altId') == dt or tmp_def.get('title') == dt:
|
|
216
|
+
dt_manager = DataTypeManager(dataType=tmp_def,localFolder=self.localfolder,tenantId=self.tenantId,sandbox=self.sandbox)
|
|
217
|
+
self.dataTypes[dt_manager.id] = dt_manager.title
|
|
218
|
+
self.dataTypeManagers[dt_manager.title] = dt_manager
|
|
219
|
+
found = True
|
|
220
|
+
break
|
|
221
|
+
if found:
|
|
222
|
+
break
|
|
190
223
|
if title is not None:
|
|
191
224
|
self.dataType['title'] = title
|
|
192
225
|
self.title = title
|