divbase-cli 0.1.0.dev2__py3-none-any.whl → 0.1.0.dev4__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.
@@ -17,13 +17,11 @@ TODO:
17
17
  """
18
18
 
19
19
  import logging
20
- from pathlib import Path
21
20
 
22
21
  import typer
23
22
  from rich import print
24
23
 
25
- from divbase_cli.cli_commands.user_config_cli import CONFIG_FILE_OPTION
26
- from divbase_cli.cli_commands.version_cli import PROJECT_NAME_OPTION
24
+ from divbase_cli.cli_commands.shared_args_options import PROJECT_NAME_OPTION
27
25
  from divbase_cli.cli_config import cli_settings
28
26
  from divbase_cli.config_resolver import resolve_project
29
27
  from divbase_cli.user_auth import make_authenticated_request
@@ -75,7 +73,6 @@ def sample_metadata_query(
75
73
  ),
76
74
  metadata_tsv_name: str = METADATA_TSV_ARGUMENT,
77
75
  project: str | None = PROJECT_NAME_OPTION,
78
- config_file: Path = CONFIG_FILE_OPTION,
79
76
  ) -> None:
80
77
  """
81
78
  Query the tsv sidecar metadata file for the VCF files in the project's data store on DivBase.
@@ -86,7 +83,7 @@ def sample_metadata_query(
86
83
  TODO: handle when the name of the sample column is something other than Sample_ID
87
84
  """
88
85
 
89
- project_config = resolve_project(project_name=project, config_path=config_file)
86
+ project_config = resolve_project(project_name=project)
90
87
 
91
88
  request_data = SampleMetadataQueryRequest(tsv_filter=filter, metadata_tsv_name=metadata_tsv_name)
92
89
 
@@ -100,14 +97,23 @@ def sample_metadata_query(
100
97
 
101
98
  results = SampleMetadataQueryTaskResult(**response.json())
102
99
 
100
+ if results.warnings:
101
+ print("[yellow]Warnings:[/yellow]")
102
+ for warning in results.warnings:
103
+ print(f" • {warning}")
104
+ print()
105
+
103
106
  if show_sample_results:
104
107
  print("[bright_blue]Name and file for each sample in query results:[/bright_blue]")
105
108
  for sample in results.sample_and_filename_subset:
106
109
  print(f"Sample ID: '{sample['Sample_ID']}', Filename: '{sample['Filename']}'")
107
110
 
108
111
  print(f"The results for the query ([bright_blue]{results.query_message}[/bright_blue]):")
109
- print(f"Unique Sample IDs: {results.unique_sample_ids}")
110
- print(f"Unique filenames: {results.unique_filenames}\n")
112
+
113
+ unique_sample_ids = results.unique_sample_ids or []
114
+ unique_filenames = results.unique_filenames or []
115
+ print(f"Unique Sample IDs: {unique_sample_ids}")
116
+ print(f"Unique filenames: {unique_filenames}\n")
111
117
 
112
118
 
113
119
  @query_app.command("bcftools-pipe")
@@ -116,7 +122,6 @@ def pipe_query(
116
122
  command: str = BCFTOOLS_ARGUMENT,
117
123
  metadata_tsv_name: str = METADATA_TSV_ARGUMENT,
118
124
  project: str | None = PROJECT_NAME_OPTION,
119
- config_file: Path = CONFIG_FILE_OPTION,
120
125
  ) -> None:
121
126
  """
122
127
  Submit a query to run on the DivBase API. A single, merged VCF file will be added to the project on success.
@@ -129,7 +134,7 @@ def pipe_query(
129
134
  TODO consider handling the bcftools command whitelist checks also on the CLI level since the error messages are nicer looking?
130
135
  TODO consider moving downloading of missing files elsewhere, since this is now done before the celery task
131
136
  """
132
- project_config = resolve_project(project_name=project, config_path=config_file)
137
+ project_config = resolve_project(project_name=project)
133
138
 
134
139
  request_data = BcftoolsQueryRequest(tsv_filter=tsv_filter, command=command, metadata_tsv_name=metadata_tsv_name)
135
140
 
@@ -0,0 +1,23 @@
1
+ """
2
+ To avoid potential problems with circular imports, we can put shared typer args + options (etc...) here
3
+
4
+ If you have something that is only used in one of the cli subcommands, don't move it here.
5
+ """
6
+
7
+ import typer
8
+
9
+ PROJECT_NAME_OPTION = typer.Option(
10
+ None,
11
+ "--project",
12
+ "-p",
13
+ help="Name of the DivBase project, if not provided uses the default in your DivBase config file",
14
+ show_default=False,
15
+ )
16
+
17
+
18
+ FORMAT_AS_TSV_OPTION = typer.Option(
19
+ False,
20
+ "--tsv",
21
+ "-t",
22
+ help="If set, will print the output in .TSV format for easier programmatic parsing.",
23
+ )
@@ -6,11 +6,9 @@ Submits a query for fetching the Celery task history for the user to the DivBase
6
6
  """
7
7
 
8
8
  import logging
9
- from pathlib import Path
10
9
 
11
10
  import typer
12
11
 
13
- from divbase_cli.cli_commands.user_config_cli import CONFIG_FILE_OPTION
14
12
  from divbase_cli.cli_exceptions import AuthenticationError
15
13
  from divbase_cli.display_task_history import TaskHistoryDisplayManager
16
14
  from divbase_cli.user_auth import make_authenticated_request
@@ -28,7 +26,6 @@ task_history_app = typer.Typer(
28
26
 
29
27
  @task_history_app.command("user")
30
28
  def list_task_history_for_user(
31
- config_file: Path = CONFIG_FILE_OPTION,
32
29
  limit: int = typer.Option(10, help="Maximum number of tasks to display in the terminal. Sorted by recency."),
33
30
  project: str | None = typer.Option(
34
31
  None, help="Optional project name to filter the user's task history by project."
@@ -40,7 +37,7 @@ def list_task_history_for_user(
40
37
 
41
38
  # TODO add option to sort ASC/DESC by task timestamp
42
39
 
43
- config = load_user_config(config_file)
40
+ config = load_user_config()
44
41
  logged_in_url = config.logged_in_url
45
42
 
46
43
  if not logged_in_url:
@@ -73,13 +70,12 @@ def list_task_history_for_user(
73
70
  @task_history_app.command("id")
74
71
  def task_history_by_id(
75
72
  task_id: int | None = typer.Argument(..., help="Task ID to check the status of a specific query job."),
76
- config_file: Path = CONFIG_FILE_OPTION,
77
73
  ):
78
74
  """
79
75
  Check status of a specific task submitted by the user by its task ID.
80
76
  """
81
77
 
82
- config = load_user_config(config_file)
78
+ config = load_user_config()
83
79
  logged_in_url = config.logged_in_url
84
80
 
85
81
  if not logged_in_url:
@@ -104,7 +100,6 @@ def task_history_by_id(
104
100
 
105
101
  @task_history_app.command("project")
106
102
  def list_task_history_for_project(
107
- config_file: Path = CONFIG_FILE_OPTION,
108
103
  limit: int = typer.Option(10, help="Maximum number of tasks to display in the terminal. Sorted by recency."),
109
104
  project: str = typer.Argument(..., help="Project name to check the task history for."),
110
105
  ):
@@ -115,7 +110,7 @@ def list_task_history_for_project(
115
110
  # TODO add option to sort ASC/DESC by task timestamp
116
111
  # TODO use default project from config if not --project specified
117
112
 
118
- config = load_user_config(config_file)
113
+ config = load_user_config()
119
114
  logged_in_url = config.logged_in_url
120
115
 
121
116
  if not logged_in_url:
@@ -4,8 +4,6 @@ config subcommand for the divbase-cli package.
4
4
  Controls the user config file stored at "~/.config/.divbase_tools.yaml" (unless specified otherwise).
5
5
  """
6
6
 
7
- from pathlib import Path
8
-
9
7
  import typer
10
8
  from rich import print
11
9
  from rich.console import Console
@@ -13,34 +11,12 @@ from rich.table import Table
13
11
 
14
12
  from divbase_cli.cli_config import cli_settings
15
13
  from divbase_cli.user_config import (
16
- create_user_config,
17
14
  load_user_config,
18
15
  )
19
16
 
20
- CONFIG_FILE_OPTION = typer.Option(
21
- cli_settings.CONFIG_PATH,
22
- "--config",
23
- "-c",
24
- help="Path to your user configuration file. If you didn't specify a custom path when you created it, you don't need to set this.",
25
- )
26
-
27
17
  config_app = typer.Typer(help="Manage your user configuration file for the DivBase CLI.", no_args_is_help=True)
28
18
 
29
19
 
30
- @config_app.command("create")
31
- def create_user_config_command(
32
- config_file: Path = typer.Option(
33
- cli_settings.CONFIG_PATH,
34
- "--config",
35
- "-c",
36
- help="Where to store your config file locally on your pc.",
37
- ),
38
- ):
39
- """Create a user configuration file for divbase-cli."""
40
- create_user_config(config_path=config_file)
41
- print(f"User configuration file created at {config_file.resolve()}.")
42
-
43
-
44
20
  @config_app.command("add")
45
21
  def add_project_command(
46
22
  name: str = typer.Argument(..., help="Name of the project to add to your config file."),
@@ -56,17 +32,16 @@ def add_project_command(
56
32
  "-d",
57
33
  help="Set this project as the default project in your config file.",
58
34
  ),
59
- config_file: Path = CONFIG_FILE_OPTION,
60
35
  ):
61
36
  """Add a new project to your user configuration file."""
62
- config = load_user_config(config_file)
37
+ config = load_user_config()
63
38
  project = config.add_project(
64
39
  name=name,
65
40
  divbase_url=divbase_url,
66
41
  is_default=make_default,
67
42
  )
68
43
 
69
- print(f"Project: '{project.name}' added to your config file located at {config_file.resolve()}.")
44
+ print(f"Project: '{project.name}' added to your config.")
70
45
  print(f"The URL: {project.divbase_url} was set as the DivBase API URL for this project.")
71
46
 
72
47
  if make_default:
@@ -75,17 +50,16 @@ def add_project_command(
75
50
  print(f"To make '{project.name}' your default project you can run: 'divbase config set-default {project.name}'")
76
51
 
77
52
 
78
- @config_app.command("remove")
53
+ @config_app.command("rm")
79
54
  def remove_project_command(
80
55
  name: str = typer.Argument(..., help="Name of the project to remove from your user configuration file."),
81
- config_file: Path = CONFIG_FILE_OPTION,
82
56
  ):
83
57
  """Remove a project from your user configuration file."""
84
- config = load_user_config(config_file)
58
+ config = load_user_config()
85
59
  removed_project = config.remove_project(name)
86
60
 
87
61
  if not removed_project:
88
- print(f"The project '{name}' was not found in your config file located at {config_file.resolve()}.")
62
+ print(f"Nothing to do, the project '{name}' was not found in your user config")
89
63
  else:
90
64
  print(f"The project '{removed_project}' was removed from your config.")
91
65
 
@@ -93,20 +67,17 @@ def remove_project_command(
93
67
  @config_app.command("set-default")
94
68
  def set_default_project_command(
95
69
  name: str = typer.Argument(..., help="Name of the project to add to the user configuration file."),
96
- config_file: Path = CONFIG_FILE_OPTION,
97
70
  ):
98
71
  """Set your default project to use in all divbase-cli commands."""
99
- config = load_user_config(config_file)
72
+ config = load_user_config()
100
73
  default_project_name = config.set_default_project(name=name)
101
74
  print(f"Default project is now set to '{default_project_name}'.")
102
75
 
103
76
 
104
77
  @config_app.command("show-default")
105
- def show_default_project_command(
106
- config_file: Path = CONFIG_FILE_OPTION,
107
- ) -> None:
78
+ def show_default_project_command() -> None:
108
79
  """Print the currently set default project to the console."""
109
- config = load_user_config(config_file)
80
+ config = load_user_config()
110
81
 
111
82
  if config.default_project:
112
83
  print(config.default_project)
@@ -123,10 +94,9 @@ def set_default_dload_dir_command(
123
94
  You can specify an absolute path.
124
95
  You can use '.' to refer to the directory you run the command from.""",
125
96
  ),
126
- config_file: Path = CONFIG_FILE_OPTION,
127
97
  ):
128
98
  """Set the default download dir"""
129
- config = load_user_config(config_file)
99
+ config = load_user_config()
130
100
  dload_dir = config.set_default_download_dir(download_dir=download_dir)
131
101
  if dload_dir == ".":
132
102
  print("The default download directory will be whereever you run the command from.")
@@ -135,14 +105,14 @@ def set_default_dload_dir_command(
135
105
 
136
106
 
137
107
  @config_app.command("show")
138
- def show_user_config(
139
- config_file: Path = CONFIG_FILE_OPTION,
140
- ):
108
+ def show_user_config():
141
109
  """Pretty print the contents of your current config file."""
142
- config = load_user_config(config_file)
110
+ config = load_user_config()
143
111
  console = Console()
144
112
 
145
- console.print(f"[bold]Your DivBase user configuration file's contents located at:[/bold] '{config_file}'")
113
+ console.print(
114
+ f"[bold]Your DivBase user configuration file's contents located at:[/bold] '{cli_settings.CONFIG_PATH.resolve()}'\n"
115
+ )
146
116
 
147
117
  if not config.default_download_dir:
148
118
  dload_dir_info = "Not specified, meaning the working directory of wherever you run the download command from."
@@ -1,28 +1,23 @@
1
1
  """CLI commands for managing project versions in DivBase."""
2
2
 
3
3
  from datetime import datetime
4
- from pathlib import Path
5
4
  from zoneinfo import ZoneInfo
6
5
 
7
6
  import typer
8
7
  from rich import print
9
- from rich.console import Console
10
8
  from rich.table import Table
11
9
 
12
- from divbase_cli.cli_commands.user_config_cli import CONFIG_FILE_OPTION
10
+ from divbase_cli.cli_commands.shared_args_options import FORMAT_AS_TSV_OPTION, PROJECT_NAME_OPTION
13
11
  from divbase_cli.config_resolver import ensure_logged_in, resolve_project
14
- from divbase_cli.services import (
12
+ from divbase_cli.services.project_versions import (
15
13
  add_version_command,
16
14
  delete_version_command,
17
15
  get_version_details_command,
18
16
  list_versions_command,
17
+ update_version_command,
19
18
  )
20
-
21
- PROJECT_NAME_OPTION = typer.Option(
22
- None,
23
- help="Name of the DivBase project, if not provided uses the default in your DivBase config file",
24
- show_default=False,
25
- )
19
+ from divbase_cli.utils import print_rich_table_as_tsv
20
+ from divbase_lib.utils import format_file_size
26
21
 
27
22
  version_app = typer.Typer(
28
23
  no_args_is_help=True,
@@ -42,11 +37,10 @@ def add_version(
42
37
  name: str = typer.Argument(help="Name of the version (e.g., semantic version).", show_default=False),
43
38
  description: str = typer.Option("", help="Optional description of the version."),
44
39
  project: str | None = PROJECT_NAME_OPTION,
45
- config_file: Path = CONFIG_FILE_OPTION,
46
40
  ):
47
41
  """Add a new project version entry which specifies the current state of all files in the project at the current timestamp."""
48
- project_config = resolve_project(project_name=project, config_path=config_file)
49
- logged_in_url = ensure_logged_in(config_path=config_file, desired_url=project_config.divbase_url)
42
+ project_config = resolve_project(project_name=project)
43
+ logged_in_url = ensure_logged_in(desired_url=project_config.divbase_url)
50
44
 
51
45
  add_version_response = add_version_command(
52
46
  name=name,
@@ -57,11 +51,58 @@ def add_version(
57
51
  print(f"New version: '{add_version_response.name}' added to the project: '{project_config.name}'")
58
52
 
59
53
 
60
- @version_app.command("list")
54
+ @version_app.command("update")
55
+ def update_version(
56
+ version_name: str = typer.Argument(help="Name of the existing version you want to modify", show_default=False),
57
+ new_name: str | None = typer.Option(
58
+ None, "--new-name", "-n", help="New name for the version. If not specified, the name will remain unchanged."
59
+ ),
60
+ new_description: str | None = typer.Option(
61
+ None,
62
+ "--new-description",
63
+ "-d",
64
+ help="Optional new description of the version. If not specified, the description will remain unchanged.",
65
+ ),
66
+ project: str | None = PROJECT_NAME_OPTION,
67
+ ):
68
+ """
69
+ Update an existing project version entry's name and/or description.
70
+
71
+ The files and timestamp associated with a version entry are immutable and cannot be changed.
72
+ This is by design to ensure version entries are representations of the project's state at the timepoint they were created at.
73
+ You can create a new version entry to capture the current state of the project instead.
74
+
75
+ To see your current version entries run: `divbase-cli version ls`
76
+ """
77
+ if not new_name and not new_description:
78
+ print(
79
+ "No updates specified. Please provide a new name (--new-name) and/or description (--new-description) to update for this entry."
80
+ )
81
+ return
82
+
83
+ project_config = resolve_project(project_name=project)
84
+ logged_in_url = ensure_logged_in(desired_url=project_config.divbase_url)
85
+
86
+ updated_version = update_version_command(
87
+ project_name=project_config.name,
88
+ divbase_base_url=logged_in_url,
89
+ version_name=version_name,
90
+ new_name=new_name,
91
+ new_description=new_description,
92
+ )
93
+ if new_name and new_name != version_name:
94
+ print(f"Version '{version_name}' renamed to '{updated_version.name}' in project: '{project_config.name}'")
95
+ else:
96
+ print(f"Version '{updated_version.name}' updated in project: '{project_config.name}'")
97
+ if new_description:
98
+ print(f"New description: '{updated_version.description}'")
99
+
100
+
101
+ @version_app.command("ls")
61
102
  def list_versions(
62
103
  project: str | None = PROJECT_NAME_OPTION,
63
- config_file: Path = CONFIG_FILE_OPTION,
64
104
  include_deleted: bool = typer.Option(False, help="Include soft-deleted versions in the listing."),
105
+ format_output_as_tsv: bool = FORMAT_AS_TSV_OPTION,
65
106
  ):
66
107
  """
67
108
  List all entries in the project versioning file.
@@ -70,8 +111,8 @@ def list_versions(
70
111
  If you specify --include-deleted, soft-deleted versions will also be shown.
71
112
  Soft-deleted versions can be restored by a DivBase admin within 30 days of deletion.
72
113
  """
73
- project_config = resolve_project(project_name=project, config_path=config_file)
74
- logged_in_url = ensure_logged_in(config_path=config_file, desired_url=project_config.divbase_url)
114
+ project_config = resolve_project(project_name=project)
115
+ logged_in_url = ensure_logged_in(desired_url=project_config.divbase_url)
75
116
 
76
117
  versions_info = list_versions_command(
77
118
  project_name=project_config.name, include_deleted=include_deleted, divbase_base_url=logged_in_url
@@ -81,7 +122,6 @@ def list_versions(
81
122
  print(f"No versions found for project: {project_config.name}.")
82
123
  return
83
124
 
84
- console = Console()
85
125
  table = Table(title=f"Versions for {project_config.name}")
86
126
  table.add_column("Version", style="cyan", no_wrap=True)
87
127
  table.add_column("Created ", style="magenta")
@@ -99,49 +139,70 @@ def list_versions(
99
139
  else:
100
140
  table.add_row(name, created_at, desc)
101
141
 
102
- console.print(table)
142
+ if not format_output_as_tsv:
143
+ print(table)
144
+ else:
145
+ print_rich_table_as_tsv(table=table)
103
146
 
104
147
 
105
148
  @version_app.command("info")
106
149
  def get_version_info(
107
150
  version: str = typer.Argument(help="Specific version to retrieve information for"),
108
151
  project: str | None = PROJECT_NAME_OPTION,
109
- config_file: Path = CONFIG_FILE_OPTION,
152
+ format_output_as_tsv: bool = FORMAT_AS_TSV_OPTION,
110
153
  ):
111
- """Provide detailed information about a user specified project version, including all files present and their unique hashes."""
112
- project_config = resolve_project(project_name=project, config_path=config_file)
113
- logged_in_url = ensure_logged_in(config_path=config_file, desired_url=project_config.divbase_url)
154
+ """
155
+ Provide detailed information about a user specified project version, including all files present and their unique hashes.
156
+ """
157
+ project_config = resolve_project(project_name=project)
158
+ logged_in_url = ensure_logged_in(desired_url=project_config.divbase_url)
114
159
 
115
160
  version_details = get_version_details_command(
116
161
  project_name=project_config.name, divbase_base_url=logged_in_url, version_name=version
117
162
  )
118
163
 
119
- print(f"Project version entry for project: '{project_config.name}' with name: '{version_details.name}'")
120
- print(f"Entry created at: {format_timestamp(version_details.created_at)}")
121
- if version_details.description:
122
- print(f"Description: {version_details.description}")
123
- if version_details.is_deleted:
124
- print(
125
- "[red]WARNING: This version has been soft-deleted and will soon be permanently deleted unless restored - Contact a DivBase admin to prevent this.[/red]"
164
+ table = Table(title=f"Project version files for {project_config.name}")
165
+ table.add_column("Name", style="cyan", no_wrap=True)
166
+ table.add_column("Version ID", style="magenta")
167
+ table.add_column("MD5 Checksum", style="green")
168
+ table.add_column("Size", style="yellow")
169
+ for object_name, file_details in version_details.files.items():
170
+ file_size = format_file_size(file_details["size"])
171
+
172
+ table.add_row(
173
+ object_name,
174
+ file_details["version_id"],
175
+ file_details["etag"],
176
+ file_size,
126
177
  )
127
- print("Files at this version:")
128
- for object_name, hash in version_details.files.items():
129
- print(f"- '{object_name}' : '{hash}'")
178
+
179
+ if not format_output_as_tsv:
180
+ print(f"Project version entry for project: '{project_config.name}' with name: '{version_details.name}'")
181
+ print(f"Entry created at: {format_timestamp(version_details.created_at)}")
182
+ if version_details.description:
183
+ print(f"Description: {version_details.description}")
184
+ if version_details.is_deleted:
185
+ print(
186
+ "[red]WARNING: This version has been soft-deleted and will soon be permanently deleted unless restored - Contact a DivBase admin to prevent this.[/red]"
187
+ )
188
+ print(table)
189
+ else:
190
+ print_rich_table_as_tsv(table=table)
130
191
 
131
192
 
132
- @version_app.command("delete")
193
+ @version_app.command("rm")
133
194
  def delete_version(
134
195
  name: str = typer.Argument(help="Name of the version (e.g., semantic version).", show_default=False),
135
196
  project: str | None = PROJECT_NAME_OPTION,
136
- config_file: Path = CONFIG_FILE_OPTION,
137
197
  ):
138
198
  """
139
199
  Delete a version entry in the project versioning table. This does not delete the files themselves.
200
+
140
201
  Deleted version entries older than 30 days will be permanently deleted.
141
202
  You can ask a DivBase admin to restore a deleted version within that time period.
142
203
  """
143
- project_config = resolve_project(project_name=project, config_path=config_file)
144
- logged_in_url = ensure_logged_in(config_path=config_file, desired_url=project_config.divbase_url)
204
+ project_config = resolve_project(project_name=project)
205
+ logged_in_url = ensure_logged_in(desired_url=project_config.divbase_url)
145
206
 
146
207
  deleted_version = delete_version_command(
147
208
  project_name=project_config.name, divbase_base_url=logged_in_url, version_name=name
divbase_cli/cli_config.py CHANGED
@@ -2,16 +2,31 @@
2
2
  Settings for DivBase CLI.
3
3
 
4
4
  This class creates a single 'settings' object at module load time that can be imported and used throughout the entire package.
5
+
6
+ The user config and tokens are stored in the users local app dir:
7
+ https://typer.tiangolo.com/tutorial/app-dir/
5
8
  """
6
9
 
7
10
  import os
8
11
  from dataclasses import dataclass
9
12
  from pathlib import Path
10
13
 
11
- DEFAULT_CONFIG_PATH = Path.home() / ".config" / "divbase" / "config.yaml"
12
- DEFAULT_TOKENS_PATH = Path.home() / ".config" / "divbase" / ".secrets"
14
+ import typer
15
+
16
+ APP_NAME = "divbase-cli"
17
+ APP_DIR = Path(typer.get_app_dir(APP_NAME))
13
18
  DEFAULT_METADATA_TSV_NAME = "sample_metadata.tsv"
14
- DEFAULT_DIVBASE_API_URL = "http://localhost:8000/api" # TODO - change to production URL when time comes
19
+ DEFAULT_LOG_LEVEL = "INFO"
20
+ DEV_MODE = os.getenv("DIVBASE_DEV", "0") == "1"
21
+ CONFIG_PATH = APP_DIR / "config.yaml"
22
+ TOKENS_PATH = APP_DIR / ".secrets"
23
+
24
+ if DEV_MODE:
25
+ DEFAULT_DIVBASE_API_URL = "http://localhost:8000/api"
26
+ DEFAULT_LOGGING_ON = "1"
27
+ else:
28
+ DEFAULT_DIVBASE_API_URL = "https://divbase-dev.scilifelab-2-dev.sys.kth.se/api" # TODO - prod url when time
29
+ DEFAULT_LOGGING_ON = "0"
15
30
 
16
31
 
17
32
  @dataclass
@@ -19,21 +34,24 @@ class DivBaseCLISettings:
19
34
  """
20
35
  Settings for DivBase CLI.
21
36
 
22
- NOTE: Do not create an instance of this class yourself,
23
- import the 'settings' instance created at this module's load time.
37
+ You do not need to create an instance of this class yourself,
38
+ instead, import the 'cli_settings' instance created at this module's load time.
24
39
  """
25
40
 
26
- CONFIG_PATH: Path = Path(os.getenv("DIVBASE_CONFIG_PATH", DEFAULT_CONFIG_PATH))
27
- TOKENS_PATH: Path = Path(os.getenv("DIVBASE_TOKENS_PATH", DEFAULT_TOKENS_PATH))
41
+ CONFIG_PATH: Path = Path(os.getenv("DIVBASE_CLI_CONFIG_PATH", CONFIG_PATH))
42
+ TOKENS_PATH: Path = Path(os.getenv("DIVBASE_CLI_TOKENS_PATH", TOKENS_PATH))
28
43
  DIVBASE_API_URL: str = os.getenv("DIVBASE_API_URL", DEFAULT_DIVBASE_API_URL)
29
44
  METADATA_TSV_NAME: str = os.getenv("DIVBASE_METADATA_TSV_NAME", DEFAULT_METADATA_TSV_NAME)
30
- LOGGING_ON: bool = bool(os.getenv("DIVBASE_LOGGING_ON", "True") == "True")
31
- LOG_LEVEL: str = os.getenv("DIVBASE_LOG_LEVEL", "INFO").upper()
45
+ LOGGING_ON: bool = os.getenv("DIVBASE_LOGGING_ON", DEFAULT_LOGGING_ON) == "1"
46
+ LOG_LEVEL: str = os.getenv("DIVBASE_LOG_LEVEL", DEFAULT_LOG_LEVEL).upper()
32
47
 
33
48
  def __post_init__(self):
34
49
  valid_levels = ["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG", "NOTSET"]
35
50
  if self.LOG_LEVEL not in valid_levels:
36
51
  raise ValueError(f"Invalid LOG_LEVEL: {self.LOG_LEVEL}. Must be one of {valid_levels}.")
37
52
 
53
+ if self.DIVBASE_API_URL.endswith("/"):
54
+ self.DIVBASE_API_URL = self.DIVBASE_API_URL[:-1]
55
+
38
56
 
39
57
  cli_settings = DivBaseCLISettings()