Skip to content
This repository was archived by the owner on Apr 1, 2024. It is now read-only.

Commit 42e6c3e

Browse files
committed
Obtaining a first version of a working and simple uploader.
Signed-off-by: Cédric Foellmi <[email protected]>
1 parent b005cc3 commit 42e6c3e

File tree

7 files changed

+208
-305
lines changed

7 files changed

+208
-305
lines changed

oort/cli/cli.py

+19-31
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import click
2-
from arcsecond import ArcsecondAPI, Config
2+
from arcsecond import ArcsecondAPI, ArcsecondConfig, cli as ArcsecondCLI
33
from arcsecond.options import State
44

55
from oort import __version__
6+
from oort.common.context import Context
67
from oort.common.utils import build_endpoint_kwargs
8+
from oort.uploader.walker import walk
79
from .errors import OortCloudError, InvalidUploadOptionsOortCloudError
810
from .helpers import display_command_summary
911
from .options import basic_options
10-
from .validators import validate_upload_parameters
1112

1213
pass_state = click.make_pass_decorator(State, ensure=True)
1314

@@ -64,35 +65,21 @@ def login(state, username, password):
6465
This Upload key is not your full API key. When logging in with oort, no fetch
6566
nor storage of the API key occur (only the Upload one).
6667
"""
67-
_, error = ArcsecondAPI(Config(state)).login(username, password, upload_key=True)
68+
config = ArcsecondConfig(state)
69+
_, error = ArcsecondAPI(config).login(username, password, upload_key=True)
6870
if error:
6971
click.echo(error)
7072
else:
71-
username = ArcsecondAPI.username(api=state.api_name)
73+
username = config.username
7274
click.echo(f' • Successfully logged in as @{username} (API: {state.api_name}).')
7375

7476

75-
@main.command()
76-
@click.argument('name', required=True, nargs=1)
77-
@click.argument('address', required=False, nargs=1)
77+
@main.command(help='Get or set the API server address (fully qualified domain name).')
78+
@click.argument('name', required=False, nargs=1)
79+
@click.argument('fqdn', required=False, nargs=1)
7880
@pass_state
79-
def api(state, name=None, address=None):
80-
"""
81-
Configure the API server address.
82-
83-
For instance:
84-
85-
• "oort api main" to get the main API server address (default).\n
86-
• "oort api dev http://localhost:8000" to configure a dev server.
87-
88-
You can then use --api <api name> in every command to choose which API
89-
server you want to interact with. Hence, "--api dev" will choose the above
90-
dev server.
91-
"""
92-
if address is None:
93-
print(ArcsecondAPI.get_api_name(api_name=name))
94-
else:
95-
ArcsecondAPI.set_api_name(address, api_name=name)
81+
def api(state, name=None, fqdn=None):
82+
ArcsecondCLI.api(state, name, fqdn)
9683

9784

9885
@main.command(help="Display the list of (organisation) datasets.")
@@ -109,7 +96,7 @@ def datasets(state, organisation=None):
10996
click.echo(" • Fetching datasets...")
11097

11198
kwargs = build_endpoint_kwargs(state.api_name, subdomain=organisation)
112-
dataset_list, error = ArcsecondAPI.datasets(**kwargs).list()
99+
dataset_list, error = ArcsecondAPI(ArcsecondConfig(state)).datasets.list()
113100
if error is not None:
114101
raise OortCloudError(str(error))
115102

@@ -134,7 +121,7 @@ def datasets(state, organisation=None):
134121
help="The subdomain, if uploading for an Observatory Portal.")
135122
@basic_options
136123
@pass_state
137-
def upload(state, folder, organisation=None, dataset=None):
124+
def upload(state, folder, dataset=None, organisation=None):
138125
"""
139126
Upload the content of a folder.
140127
@@ -152,16 +139,17 @@ def upload(state, folder, organisation=None, dataset=None):
152139
Oort will then start walking through the folder tree and uploads regular files
153140
(hidden and empty files will be skipped).
154141
"""
142+
config = ArcsecondConfig(state)
143+
context = Context(config, dataset_uuid_or_name=dataset, subdomain=organisation)
144+
155145
try:
156-
values = validate_upload_parameters(organisation, dataset, state)
146+
context.validate()
157147
except InvalidUploadOptionsOortCloudError as e:
158148
click.echo(f"\n • ERROR {str(e)} \n")
159149
return
160150

161-
display_command_summary([folder, ], Config(state), values)
151+
display_command_summary(context, [folder, ])
162152
ok = input('\n ----> OK? (Press Enter) ')
163153

164154
if ok.strip() == '':
165-
from oort.uploader.walker import walk
166-
167-
walk(folder, context)
155+
walk(context, folder)

oort/cli/helpers.py

+11-13
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
import pathlib
33

44
import click
5-
from arcsecond import Config
65

6+
from oort.common.context import Context
77
from oort.common.utils import is_file_hidden
88

99

@@ -35,27 +35,25 @@ def __get_formatted_bytes_size(size):
3535
return f"{(size / math.pow(k, i)):.2f} {units[i]}"
3636

3737

38-
def display_command_summary(folders: list, config: Config, values: dict):
38+
def display_command_summary(context: Context, folders: list):
3939
click.echo("\n --- Upload summary --- ")
40-
click.echo(f" • Arcsecond username: @{config.username} (Upload key: {config.upload_key[:4]}••••)")
41-
if config.subdomain:
42-
msg = f" • Uploading to Observatory Portal '{config.subdomain}' (as {config.read_key(config.subdomain)})."
40+
click.echo(f" • Arcsecond username: @{context.config.username} (Upload key: {context.config.upload_key[:4]}••••)")
41+
if context.config.subdomain:
42+
role = context.config.read_key(context.config.subdomain)
43+
msg = f" • Uploading to Observatory Portal '{context.config.subdomain}' (as {role})."
4344
else:
4445
msg = " • Uploading to your *personal* account."
4546
click.echo(msg)
4647

47-
dataset_uuid = values.get('dataset').get('uuid', '')
48-
dataset_name = values.get('dataset').get('name', '')
49-
50-
if dataset_uuid and dataset_name:
51-
msg = f" • Data will be appended to existing dataset '{dataset_name}' ({dataset_uuid})."
52-
elif not dataset_uuid and dataset_name:
53-
msg = f" • Data will be inserted into a new dataset named '{dataset_name}'."
48+
if context.dataset_uuid and context.dataset_name:
49+
msg = f" • Data will be appended to existing dataset '{context.dataset_name}' ({context.dataset_uuid})."
50+
elif not context.dataset_uuid and context.dataset_name:
51+
msg = f" • Data will be inserted into a new dataset named '{context.dataset_name}'."
5452
else:
5553
msg = " • Using folder names for dataset names (one folder = one dataset)."
5654
click.echo(msg)
5755

58-
click.echo(f" • Using API server: {config.api_name}")
56+
click.echo(f" • Using API server: {context.config.api_name}")
5957
click.echo(f" • Folder{'s' if len(folders) > 1 else ''}:")
6058
for folder in folders:
6159
folder_path = pathlib.Path(folder).expanduser().resolve()

oort/cli/validators.py

-110
This file was deleted.

oort/common/context.py

+103
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import uuid
2+
3+
import click
4+
from arcsecond import ArcsecondAPI, ArcsecondConfig
5+
6+
from oort.cli.errors import (
7+
UnknownOrganisationOortCloudError,
8+
InvalidOrgMembershipOortCloudError,
9+
InvalidAstronomerOortCloudError,
10+
InvalidWatchOptionsOortCloudError,
11+
InvalidOrganisationDatasetOortCloudError,
12+
InvalidDatasetOortCloudError
13+
)
14+
15+
16+
class Context(object):
17+
def __init__(self, config: ArcsecondConfig, dataset_uuid_or_name: str, subdomain: str):
18+
self._config = config
19+
self._dataset_uuid_or_name = dataset_uuid_or_name
20+
self._subdomain = subdomain
21+
self._dataset = None
22+
self._organisation = None
23+
self._api = ArcsecondAPI(config, subdomain)
24+
25+
def validate(self):
26+
self._validate_local_astronomer_credentials()
27+
self._validate_dataset_uuid()
28+
if self._subdomain:
29+
self._validate_remote_organisation()
30+
self._validate_astronomer_role_in_remote_organisation()
31+
32+
def _validate_local_astronomer_credentials(self):
33+
username = self._config.username
34+
if username is None:
35+
raise InvalidAstronomerOortCloudError('Missing username')
36+
37+
upload_key = self._config.upload_key
38+
if not upload_key:
39+
raise InvalidWatchOptionsOortCloudError('Missing upload_key.')
40+
41+
def _validate_dataset_uuid(self):
42+
try:
43+
uuid.UUID(self._dataset_uuid_or_name)
44+
except ValueError:
45+
click.echo(f" • Looking for a dataset with name {self._dataset_uuid_or_name}...")
46+
datasets_list, error = self._api.datasets.list(**{'name': self._dataset_uuid_or_name})
47+
if len(datasets_list) == 0:
48+
click.echo(f" • No dataset with name {self._dataset_uuid_or_name} found. It will be created.")
49+
self._dataset = {'name': self._dataset_uuid_or_name}
50+
elif len(datasets_list) == 1:
51+
click.echo(f" • One dataset with name {self._dataset_uuid_or_name}. Data will be appended to it.")
52+
self._dataset = datasets_list[0]
53+
else:
54+
error = f"Multiple datasets with name {self._dataset_uuid_or_name} found. Be more specific."
55+
else:
56+
click.echo(f" • Fetching details of dataset {self._dataset_uuid_or_name}...")
57+
self._dataset, error = self._api.datasets.read(str(self._dataset_uuid_or_name))
58+
59+
if error is not None:
60+
if self._subdomain:
61+
raise InvalidOrganisationDatasetOortCloudError(str(self._dataset_uuid_or_name),
62+
self._subdomain,
63+
str(error))
64+
else:
65+
raise InvalidDatasetOortCloudError(str(self._dataset_uuid_or_name), str(error))
66+
67+
def _validate_remote_organisation(self):
68+
click.echo(f" • Fetching details of organisation {self._subdomain}...")
69+
self._organisation, error = self._api.organisations.read(self._subdomain)
70+
if error is not None:
71+
raise UnknownOrganisationOortCloudError(self._subdomain, str(error))
72+
73+
def _validate_astronomer_role_in_remote_organisation(self):
74+
role = self._config.read_key(self._subdomain)
75+
if not role or role not in ['member', 'admin', 'superadmin']:
76+
raise InvalidOrgMembershipOortCloudError(self._subdomain)
77+
78+
@property
79+
def config(self):
80+
return self._config
81+
82+
@property
83+
def dataset_uuid(self):
84+
return self._dataset.get('uuid', '')
85+
86+
@property
87+
def dataset_name(self):
88+
return self._dataset.get('name', '')
89+
90+
@property
91+
def organisation_subdomain(self):
92+
return self._organisation.get('subdomain', '')
93+
94+
# def __print_organisation_telescopes(org_subdomain: str, api: str = 'main'):
95+
# click.echo(f" • Here is a list of existing telescopes for organisation '{org_subdomain}':")
96+
# kwargs = build_endpoint_kwargs(api, org_subdomain)
97+
# telescope_list, error = ArcsecondAPI.telescopes(**kwargs).list()
98+
# for telescope in telescope_list:
99+
# s = f" 🔭 {telescope['name']}"
100+
# if telescope.get('alias', ''):
101+
# s += f" a.k.a. {telescope['alias']}"
102+
# s += f" ({telescope['uuid']})"
103+
# click.echo(s)

0 commit comments

Comments
 (0)