metahq-cli 0.1.0__tar.gz → 1.0.0rc1__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.
Files changed (30) hide show
  1. metahq_cli-1.0.0rc1/LICENSE +28 -0
  2. metahq_cli-1.0.0rc1/PKG-INFO +104 -0
  3. metahq_cli-1.0.0rc1/README.md +66 -0
  4. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/pyproject.toml +6 -0
  5. metahq_cli-1.0.0rc1/src/metahq_cli/__init__.py +1 -0
  6. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/commands/__init__.py +3 -1
  7. metahq_cli-1.0.0rc1/src/metahq_cli/commands/delete.py +63 -0
  8. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/commands/retrieve.py +41 -33
  9. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/commands/search.py +2 -1
  10. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/commands/setup.py +25 -13
  11. metahq_cli-1.0.0rc1/src/metahq_cli/commands/validate.py +51 -0
  12. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/logger.py +14 -18
  13. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/main.py +3 -1
  14. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/retrieval_builder.py +162 -68
  15. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/retriever.py +83 -28
  16. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/setup/config.py +33 -8
  17. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/setup/downloader.py +76 -73
  18. metahq_cli-1.0.0rc1/src/metahq_cli/util/__init__.py +0 -0
  19. metahq_cli-1.0.0rc1/src/metahq_cli/util/_validate.py +66 -0
  20. metahq_cli-1.0.0rc1/src/metahq_cli/util/helpers.py +37 -0
  21. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/util/supported.py +11 -10
  22. metahq_cli-0.1.0/PKG-INFO +0 -29
  23. metahq_cli-0.1.0/src/metahq_cli/__init__.py +0 -1
  24. metahq_cli-0.1.0/src/metahq_cli/util/helpers.py +0 -15
  25. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/.gitignore +0 -0
  26. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/commands/supported.py +0 -0
  27. /metahq_cli-0.1.0/README.md → /metahq_cli-1.0.0rc1/src/metahq_cli/setup/__init__.py +0 -0
  28. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/util/checkers.py +0 -0
  29. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/util/common_args.py +0 -0
  30. {metahq_cli-0.1.0 → metahq_cli-1.0.0rc1}/src/metahq_cli/util/messages.py +0 -0
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2025, Krishnan Lab
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,104 @@
1
+ Metadata-Version: 2.4
2
+ Name: metahq-cli
3
+ Version: 1.0.0rc1
4
+ Summary: The meta-hq CLI.
5
+ Author-email: Parker Hicks <parker.hicks@cuanschutz.edu>, Faisal Alquaddoomi <faisal.alquaddoomi@cuanschutz.edu>
6
+ License-File: LICENSE
7
+ Keywords: CLI,Data Curation,Database,Public Biomedical Data
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Science/Research
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Requires-Python: >=3.11
16
+ Requires-Dist: click>=8.3.0
17
+ Requires-Dist: metahq-core>=0.1.0
18
+ Requires-Dist: requests>=2.30.0
19
+ Requires-Dist: rich>=13.0.0
20
+ Provides-Extra: dev
21
+ Requires-Dist: black; extra == 'dev'
22
+ Requires-Dist: flake8; extra == 'dev'
23
+ Requires-Dist: isort; extra == 'dev'
24
+ Requires-Dist: mkdocs-click; extra == 'dev'
25
+ Requires-Dist: mkdocs-material; extra == 'dev'
26
+ Requires-Dist: mkdocs>=1.6.1; extra == 'dev'
27
+ Requires-Dist: mkdocstrings[python]; extra == 'dev'
28
+ Requires-Dist: mypy; extra == 'dev'
29
+ Requires-Dist: pymdown-extensions; extra == 'dev'
30
+ Requires-Dist: pytest-cov; extra == 'dev'
31
+ Requires-Dist: pytest>=8.0; extra == 'dev'
32
+ Requires-Dist: vulture; extra == 'dev'
33
+ Provides-Extra: test
34
+ Requires-Dist: pytest-benchmark; extra == 'test'
35
+ Requires-Dist: pytest-cov; extra == 'test'
36
+ Requires-Dist: pytest>=8.0; extra == 'test'
37
+ Description-Content-Type: text/markdown
38
+
39
+ <div align="left">
40
+ <img src="https://raw.githubusercontent.com/krishnanlab/meta-hq/main/media/metahq_cli_logo.png" alt="CLI Logo" width="400" height="200" />
41
+ </div>
42
+
43
+ ![CLI Tests](https://github.com/krishnanlab/meta-hq/workflows/CLI%20Tests/badge.svg)
44
+ ![Python](https://img.shields.io/badge/python-3.12+-blue.svg)
45
+ ![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)
46
+ ![pypi](https://img.shields.io/pypi/v/metahq-cli.svg)
47
+ [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
48
+
49
+ A command line interface to query the MetaHQ database.
50
+
51
+ ## Key features
52
+
53
+ - Query standardized and harmonized biomedical annotations
54
+ - Access metadata for samples and studies in GEO and SRA
55
+ - Convert annotations to labels
56
+ - Fast execution time
57
+ - Search for relevant ontology terms with free-text queries
58
+ - Contribute new annotation sets to the database
59
+
60
+ ## Docs
61
+
62
+ Source Code: [https://github.com/krishnanlab/meta-hq/tree/main/packages/cli](https://github.com/krishnanlab/meta-hq/tree/main/packages/cli)
63
+
64
+ ## Installation and setup
65
+
66
+ **To install**:
67
+
68
+ ```bash
69
+ pip install metahq-cli
70
+ ```
71
+
72
+ **Setup**: This command must be run with every metahq-cli installation. The `setup` command downloads the
73
+ MetaHQ database from Zenodo.
74
+
75
+ ```bash
76
+ metahq setup --doi latest
77
+ ```
78
+
79
+ ## Retrieve
80
+
81
+ Query the MetaHQ database to retrieve sample or study annotations for tissues, diseases, sex, and age.
82
+
83
+ ```bash
84
+ metahq retrieve diseases --terms "MONDO:0004994,MONDO:0008903" \
85
+ --level sample --filters "species=human,tech=rnaseq,ecode=expert" \
86
+ --metadata "sample,series,platform,srp" --fmt parquet
87
+ ```
88
+
89
+ ## Search
90
+
91
+ If you do not have ontology terms memorized off-hand, shame on you. However, you can cheat and run the following
92
+ command to find the ontology term IDs most similar to a free-text query of a disease, tissue, or cell line.
93
+
94
+ ```bash
95
+ metahq search --query "heart attack" --type disease --ontology MONDO
96
+ ```
97
+
98
+ ## Supported
99
+
100
+ Run the following to view supported attributes, filters, formats, etc.
101
+
102
+ ```bash
103
+ metahq supported
104
+ ```
@@ -0,0 +1,66 @@
1
+ <div align="left">
2
+ <img src="https://raw.githubusercontent.com/krishnanlab/meta-hq/main/media/metahq_cli_logo.png" alt="CLI Logo" width="400" height="200" />
3
+ </div>
4
+
5
+ ![CLI Tests](https://github.com/krishnanlab/meta-hq/workflows/CLI%20Tests/badge.svg)
6
+ ![Python](https://img.shields.io/badge/python-3.12+-blue.svg)
7
+ ![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)
8
+ ![pypi](https://img.shields.io/pypi/v/metahq-cli.svg)
9
+ [![License](https://img.shields.io/badge/License-BSD_3--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)
10
+
11
+ A command line interface to query the MetaHQ database.
12
+
13
+ ## Key features
14
+
15
+ - Query standardized and harmonized biomedical annotations
16
+ - Access metadata for samples and studies in GEO and SRA
17
+ - Convert annotations to labels
18
+ - Fast execution time
19
+ - Search for relevant ontology terms with free-text queries
20
+ - Contribute new annotation sets to the database
21
+
22
+ ## Docs
23
+
24
+ Source Code: [https://github.com/krishnanlab/meta-hq/tree/main/packages/cli](https://github.com/krishnanlab/meta-hq/tree/main/packages/cli)
25
+
26
+ ## Installation and setup
27
+
28
+ **To install**:
29
+
30
+ ```bash
31
+ pip install metahq-cli
32
+ ```
33
+
34
+ **Setup**: This command must be run with every metahq-cli installation. The `setup` command downloads the
35
+ MetaHQ database from Zenodo.
36
+
37
+ ```bash
38
+ metahq setup --doi latest
39
+ ```
40
+
41
+ ## Retrieve
42
+
43
+ Query the MetaHQ database to retrieve sample or study annotations for tissues, diseases, sex, and age.
44
+
45
+ ```bash
46
+ metahq retrieve diseases --terms "MONDO:0004994,MONDO:0008903" \
47
+ --level sample --filters "species=human,tech=rnaseq,ecode=expert" \
48
+ --metadata "sample,series,platform,srp" --fmt parquet
49
+ ```
50
+
51
+ ## Search
52
+
53
+ If you do not have ontology terms memorized off-hand, shame on you. However, you can cheat and run the following
54
+ command to find the ontology term IDs most similar to a free-text query of a disease, tissue, or cell line.
55
+
56
+ ```bash
57
+ metahq search --query "heart attack" --type disease --ontology MONDO
58
+ ```
59
+
60
+ ## Supported
61
+
62
+ Run the following to view supported attributes, filters, formats, etc.
63
+
64
+ ```bash
65
+ metahq supported
66
+ ```
@@ -34,6 +34,12 @@ dev = [
34
34
  "isort",
35
35
  "flake8",
36
36
  "mypy",
37
+ "mkdocs>=1.6.1",
38
+ "mkdocstrings[python]",
39
+ "mkdocs-material",
40
+ "mkdocs-click",
41
+ "pymdown-extensions",
42
+ "vulture",
37
43
  ]
38
44
 
39
45
  test = [
@@ -0,0 +1 @@
1
+ __version__ = "1.0.0-rc.1"
@@ -1,6 +1,8 @@
1
+ from metahq_cli.commands.delete import delete
1
2
  from metahq_cli.commands.retrieve import retrieve_commands
2
3
  from metahq_cli.commands.search import search
3
4
  from metahq_cli.commands.setup import setup
4
5
  from metahq_cli.commands.supported import supported
6
+ from metahq_cli.commands.validate import validate
5
7
 
6
- __all__ = ["retrieve_commands", "setup", "search", "supported"]
8
+ __all__ = ["retrieve_commands", "setup", "search", "supported", "validate", "delete"]
@@ -0,0 +1,63 @@
1
+ """
2
+ CLI command to delete old version of MetaHQ
3
+ """
4
+
5
+ import click
6
+ import os
7
+ import shutil
8
+ import sys
9
+ from metahq_core.util.progress import console
10
+ from pathlib import Path
11
+
12
+ from metahq_cli.logger import setup_logger
13
+ from metahq_cli.util.common_args import logging_args
14
+ from metahq_core.util.supported import get_default_log_dir
15
+ from metahq_cli.util.helpers import set_verbosity
16
+
17
+ from metahq_core.util.supported import get_config, get_config_file
18
+
19
+ @click.command(name="delete", context_settings={"help_option_names": ["-h", "--help"]})
20
+ @click.option(
21
+ "-l",
22
+ "--log-dir",
23
+ type=click.Path(),
24
+ default="default",
25
+ help="Path to directory storing logs. Default is `/home/path/MetaHQ`.",
26
+ )
27
+ @click.option(
28
+ "--all",
29
+ "-a",
30
+ is_flag=True,
31
+ default=False,
32
+ help="Will delete everything in MetaHQ directory",
33
+ )
34
+ @logging_args
35
+ def delete(log_level: str, log_dir: str, quiet: bool, all: bool):
36
+ """Delete old version of MetaHQ"""
37
+ if log_dir == "default":
38
+ log_dir = str(get_default_log_dir())
39
+
40
+ logger = setup_logger(__name__, level=log_level, log_dir=log_dir, console=console)
41
+ verbose = set_verbosity(quiet)
42
+
43
+ # read in config
44
+ config = get_config()
45
+ config_path = get_config_file()
46
+ logger.info(f"The path to config file is found at: {config_path}")
47
+ logger.info(f"Information in the config file is: {config}")
48
+
49
+ if click.confirm(
50
+ f"Delete MetaHQ database and config file or config directory?",
51
+ default=False,
52
+ ):
53
+ logger.info("Deleting existing data directory...")
54
+ shutil.rmtree(config["data_dir"])
55
+ if all:
56
+ logger.info("Deleting existing config directory...")
57
+ shutil.rmtree(config_path.parent)
58
+ else:
59
+ logger.info("Deleting existing config file...")
60
+ os.remove(config_path)
61
+ else:
62
+ logger.info("Keeping existing data directory and config file.")
63
+ sys.exit("Terminating...")
@@ -4,7 +4,7 @@ CLI command to retrieve annotations and labels from meta-hq.
4
4
  Author: Parker Hicks
5
5
  Date: 2025-09-05
6
6
 
7
- Last updated: 2025-11-24 by Parker Hicks
7
+ Last updated: 2025-12-01 by Parker Hicks
8
8
  """
9
9
 
10
10
  import click
@@ -61,15 +61,17 @@ def retrieve_commands():
61
61
  """Retrieval commands for tissue, disease, sex, and age annotations."""
62
62
 
63
63
 
64
- @retrieve_commands.command("tissues")
65
- @logging_args
64
+ @retrieve_commands.command("age")
65
+ @click.option(
66
+ "--terms",
67
+ type=AGE_GROUP_OPT,
68
+ default="all",
69
+ help="Age groups to choose. Can combine like 'fetus,adult'.",
70
+ )
66
71
  @retrieval_args
67
- @ontology_retrieval_args
68
- @click.option("--terms", type=str, default="UBERON:0000948,UBERON:0000955")
69
- def retrieve_tissues(
70
- terms, level, mode, fmt, metadata, filters, output, log_level, quiet, direct
71
- ):
72
- """Retrieval command for tissue ontology terms."""
72
+ @logging_args
73
+ def retrieve_age(terms, level, fmt, metadata, filters, output, log_level, quiet):
74
+ """Retrieval command for age group annotations."""
73
75
  if metadata == "default":
74
76
  metadata = level
75
77
 
@@ -78,17 +80,17 @@ def retrieve_tissues(
78
80
  __name__, console=get_console(), level=log_level, log_dir=get_log_dir()
79
81
  )
80
82
 
81
- # hidden from user. Used to test annotation quality.
82
- mode = check_direct(mode, direct, verbose, log)
83
83
  builder = Builder(logger=log, verbose=verbose)
84
84
 
85
85
  # parse and check filters
86
86
  filters = builder.get_filters(filters)
87
87
 
88
88
  # make configs
89
- query_config = builder.query_config("geo", "tissue", level, filters)
90
- curation_config = builder.curation_config(terms, mode, "uberon")
91
- output_config = builder.output_config(output, fmt, metadata, level=level)
89
+ query_config = builder.query_config("geo", "age", level, filters)
90
+ curation_config = builder.curation_config(terms, "direct", "age")
91
+ output_config = builder.output_config(
92
+ output, fmt, metadata, level=level, attribute="age"
93
+ )
92
94
 
93
95
  # retrieve
94
96
  retriever = Retriever(
@@ -98,10 +100,10 @@ def retrieve_tissues(
98
100
 
99
101
 
100
102
  @retrieve_commands.command("diseases")
101
- @logging_args
103
+ @click.option("--terms", type=str, default="MONDO:0004994,MONDO:0018177")
102
104
  @retrieval_args
103
105
  @ontology_retrieval_args
104
- @click.option("--terms", type=str, default="MONDO:0004994,MONDO:0018177")
106
+ @logging_args
105
107
  def retrieve_diseases(
106
108
  terms, level, mode, fmt, metadata, filters, output, log_level, quiet, direct
107
109
  ):
@@ -124,7 +126,9 @@ def retrieve_diseases(
124
126
  # make configs
125
127
  query_config = builder.query_config("geo", "disease", level, filters)
126
128
  curation_config = builder.curation_config(terms, mode, "mondo")
127
- output_config = builder.output_config(output, fmt, metadata, level=level)
129
+ output_config = builder.output_config(
130
+ output, fmt, metadata, level=level, attribute="disease"
131
+ )
128
132
 
129
133
  # retrieve
130
134
  retriever = Retriever(
@@ -134,9 +138,9 @@ def retrieve_diseases(
134
138
 
135
139
 
136
140
  @retrieve_commands.command("sex")
137
- @logging_args
138
- @retrieval_args
139
141
  @click.option("--terms", type=str, default="male,female")
142
+ @retrieval_args
143
+ @logging_args
140
144
  def retrieve_sex(terms, level, fmt, metadata, filters, output, log_level, quiet):
141
145
  """Retrieval command for sex annotations."""
142
146
  if metadata == "default":
@@ -155,7 +159,9 @@ def retrieve_sex(terms, level, fmt, metadata, filters, output, log_level, quiet)
155
159
  # make configs
156
160
  query_config = builder.query_config("geo", "sex", level, filters)
157
161
  curation_config = builder.curation_config(terms, "direct", "sex")
158
- output_config = builder.output_config(output, fmt, metadata, level=level)
162
+ output_config = builder.output_config(
163
+ output, fmt, metadata, level=level, attribute="sex"
164
+ )
159
165
 
160
166
  # retrieve
161
167
  retriever = Retriever(
@@ -164,17 +170,15 @@ def retrieve_sex(terms, level, fmt, metadata, filters, output, log_level, quiet)
164
170
  retriever.retrieve()
165
171
 
166
172
 
167
- @retrieve_commands.command("age")
168
- @logging_args
173
+ @retrieve_commands.command("tissues")
174
+ @click.option("--terms", type=str, default="UBERON:0000948,UBERON:0000955")
169
175
  @retrieval_args
170
- @click.option(
171
- "--terms",
172
- type=AGE_GROUP_OPT,
173
- default="all",
174
- help="Age groups to choose. Can combine like 'fetus,adult'.",
175
- )
176
- def retrieve_age(terms, level, fmt, metadata, filters, output, log_level, quiet):
177
- """Retrieval command for age group annotations."""
176
+ @ontology_retrieval_args
177
+ @logging_args
178
+ def retrieve_tissues(
179
+ terms, level, mode, fmt, metadata, filters, output, log_level, quiet, direct
180
+ ):
181
+ """Retrieval command for tissue ontology terms."""
178
182
  if metadata == "default":
179
183
  metadata = level
180
184
 
@@ -183,15 +187,19 @@ def retrieve_age(terms, level, fmt, metadata, filters, output, log_level, quiet)
183
187
  __name__, console=get_console(), level=log_level, log_dir=get_log_dir()
184
188
  )
185
189
 
190
+ # hidden from user. Used to test annotation quality.
191
+ mode = check_direct(mode, direct, verbose, log)
186
192
  builder = Builder(logger=log, verbose=verbose)
187
193
 
188
194
  # parse and check filters
189
195
  filters = builder.get_filters(filters)
190
196
 
191
197
  # make configs
192
- query_config = builder.query_config("geo", "age", level, filters)
193
- curation_config = builder.curation_config(terms, "direct", "age")
194
- output_config = builder.output_config(output, fmt, metadata, level=level)
198
+ query_config = builder.query_config("geo", "tissue", level, filters)
199
+ curation_config = builder.curation_config(terms, mode, "uberon")
200
+ output_config = builder.output_config(
201
+ output, fmt, metadata, level=level, attribute="tissue"
202
+ )
195
203
 
196
204
  # retrieve
197
205
  retriever = Retriever(
@@ -22,7 +22,6 @@ DEFAULT_TOP_HITS = 3
22
22
 
23
23
 
24
24
  @click.command(name="search", context_settings={"help_option_names": ["-h", "--help"]})
25
- @logging_args
26
25
  @click.option("--query", type=str, required=True, help="Search query")
27
26
  @click.option(
28
27
  "--db",
@@ -30,6 +29,7 @@ DEFAULT_TOP_HITS = 3
30
29
  type=str,
31
30
  default="default",
32
31
  help="DuckDB file",
32
+ hidden=True,
33
33
  )
34
34
  @click.option(
35
35
  "--type",
@@ -65,6 +65,7 @@ DEFAULT_TOP_HITS = 3
65
65
  @click.option(
66
66
  "--scopes", "-x", is_flag=True, default=False, help="Include scopes in synonym list"
67
67
  )
68
+ @logging_args
68
69
  def search(
69
70
  query,
70
71
  db,
@@ -4,15 +4,14 @@ Command to set up the meta-hq CLI.
4
4
  Author: Parker Hicks
5
5
  Date: 2025-09-05
6
6
 
7
- Last updated: 2025-11-24 by Parker Hicks
7
+ Last updated: 2025-11-26 by Parker Hicks
8
8
  """
9
9
 
10
+ from pathlib import Path
11
+
10
12
  import click
11
13
  from metahq_core.util.progress import console
12
- from metahq_core.util.supported import (
13
- get_default_data_dir,
14
- get_default_log_dir,
15
- )
14
+ from metahq_core.util.supported import get_default_data_dir, get_default_log_dir
16
15
 
17
16
  from metahq_cli.logger import setup_logger
18
17
  from metahq_cli.setup.config import Config
@@ -22,18 +21,31 @@ from metahq_cli.util.helpers import set_verbosity
22
21
  from metahq_cli.util.supported import LATEST_DATABASE
23
22
 
24
23
 
25
- @click.command
26
- @logging_args
27
- @click.option("--doi", type=str, default="latest")
28
- @click.option("--data-dir", type=click.Path(), default="default")
24
+ @click.command(name="setup", context_settings={"help_option_names": ["-h", "--help"]})
29
25
  @click.option(
30
- "--log-dir",
26
+ "-d",
27
+ "--doi",
31
28
  type=str,
29
+ default="latest",
30
+ help="Zenodo DOI of the MetaHQ database. Default is `latest`.",
31
+ )
32
+ @click.option(
33
+ "-o",
34
+ "--data-dir",
35
+ type=click.Path(),
32
36
  default="default",
33
- help="Path to directory storing logs.",
37
+ help="Path to directory to store the database. Default is `/home/path/.metahq_data`.",
34
38
  )
39
+ @click.option(
40
+ "-l",
41
+ "--log-dir",
42
+ type=click.Path(),
43
+ default="default",
44
+ help="Path to directory storing logs. Default is `/home/path/MetaHQ`.",
45
+ )
46
+ @logging_args
35
47
  def setup(doi: str, data_dir: str, log_level: str, log_dir: str, quiet: bool):
36
- """Creates the meta-hq package configuration file."""
48
+ """Download the MetaHQ database and configure the CLI."""
37
49
  if log_dir == "default":
38
50
  log_dir = str(get_default_log_dir())
39
51
 
@@ -48,7 +60,7 @@ def setup(doi: str, data_dir: str, log_level: str, log_dir: str, quiet: bool):
48
60
 
49
61
  logger.info("Downloading MetaHQ database...")
50
62
  downloader = Downloader(
51
- doi, data_dir, logger=logger, logdir=log_dir, verbose=verbose
63
+ doi, data_dir, logger=logger, logdir=str(log_dir), verbose=verbose
52
64
  )
53
65
  downloader.get()
54
66
  downloader.extract()
@@ -0,0 +1,51 @@
1
+ """
2
+ CLI command to validate data hasen't changed.
3
+ """
4
+
5
+ import click
6
+ from metahq_core.util.progress import console
7
+ from pathlib import Path
8
+ import hashlib
9
+
10
+ from metahq_cli.logger import setup_logger
11
+ from metahq_cli.util.common_args import logging_args
12
+ from metahq_core.util.supported import get_default_log_dir
13
+ from metahq_cli.util.helpers import set_verbosity
14
+
15
+ from metahq_core.util.supported import get_config, get_config_file
16
+ from metahq_cli.util._validate import check_md5_match
17
+
18
+ @click.command(name="validate", context_settings={"help_option_names": ["-h", "--help"]})
19
+ @click.option(
20
+ "-l",
21
+ "--log-dir",
22
+ type=click.Path(),
23
+ default="default",
24
+ help="Path to directory storing logs. Default is `/home/path/MetaHQ`.",
25
+ )
26
+ @logging_args
27
+ def validate(log_level: str, log_dir: str, quiet: bool):
28
+ """Validate files are unchanged"""
29
+ if log_dir == "default":
30
+ log_dir = str(get_default_log_dir())
31
+
32
+ logger = setup_logger(__name__, level=log_level, log_dir=log_dir, console=console)
33
+ verbose = set_verbosity(quiet)
34
+
35
+ # read in config
36
+ config = get_config()
37
+ config_path = get_config_file()
38
+ logger.info(f"The path to config file is found at: {config_path}")
39
+ logger.info(f"Validating the files for the config options: {config}")
40
+
41
+ changed_files = check_md5_match(config["zenodo_doi"], config["data_dir"])
42
+
43
+ if len(changed_files) == 0:
44
+ logger.info("Validation Passed: All files validated to be correct")
45
+ elif len(changed_files) > 0:
46
+ logger.warning(
47
+ f"Validation Failed: The following files are not the same as when downloaded. "
48
+ "Run `metahq setup` again."
49
+ )
50
+ for afile in changed_files:
51
+ logger.warning(afile)
@@ -4,13 +4,12 @@ Logger setup.
4
4
  Author: Parker Hicks
5
5
  Date: 2025-10-16
6
6
 
7
- Last updated: 2025-11-21 by Parker Hicks
7
+ Last updated: 2026-02-04 by Parker Hicks
8
8
  """
9
9
 
10
10
  from __future__ import annotations
11
11
 
12
12
  import logging
13
- from logging.handlers import TimedRotatingFileHandler
14
13
  from pathlib import Path
15
14
  from typing import TYPE_CHECKING
16
15
 
@@ -46,22 +45,22 @@ def setup_logger(
46
45
  log_dir: str | Path,
47
46
  level: int | str = logging.INFO,
48
47
  ) -> logging.Logger:
49
- """
50
- Sets up a logger.
48
+ """Sets up a logger.
49
+
50
+ Arguments:
51
+ name (str):
52
+ Logger name.
51
53
 
52
- Parameters
53
- ----------
54
- name: str
55
- Logger name.
54
+ console (rich.Console):
55
+ Console object to send logging information to.
56
56
 
57
- log_dir: str | Path
58
- Path to logging directory. Default is ~/metahq/logs
57
+ log_dir (str | Path):
58
+ Path to logging directory.
59
59
 
60
- level: int
61
- Logging level.
60
+ level (int):
61
+ Logging level.
62
62
 
63
- Returns
64
- -------
63
+ Returns:
65
64
  Configured logger.
66
65
 
67
66
  """
@@ -94,11 +93,8 @@ def setup_logger(
94
93
  logger.addHandler(console_handler)
95
94
 
96
95
  # file handler
97
- file_handler = TimedRotatingFileHandler(
96
+ file_handler = logging.FileHandler(
98
97
  Path(log_dir) / "log.log",
99
- when="midnight",
100
- interval=1,
101
- backupCount=30,
102
98
  encoding="utf-8",
103
99
  )
104
100
  # file formatter
@@ -9,7 +9,7 @@ Last updated: 2025-09-05
9
9
 
10
10
  import click
11
11
 
12
- from metahq_cli.commands import retrieve_commands, search, setup, supported
12
+ from metahq_cli.commands import retrieve_commands, search, setup, supported, validate, delete
13
13
 
14
14
 
15
15
  @click.group()
@@ -21,6 +21,8 @@ main.add_command(setup)
21
21
  main.add_command(search)
22
22
  main.add_command(supported)
23
23
  main.add_command(retrieve_commands, name="retrieve")
24
+ main.add_command(validate)
25
+ main.add_command(delete)
24
26
 
25
27
  if __name__ == "__main__":
26
28
  main()