ialirt-data-access 0.1.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 IMAP Science Operations Center
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.3
2
+ Name: ialirt-data-access
3
+ Version: 0.1.0
4
+ Summary: I-ALiRT Data Access
5
+ License: MIT
6
+ Keywords: IMAP,SDC,SOC,SDS,Science Operations
7
+ Author: IMAP SDC Developers
8
+ Author-email: imap-sdc@lists.lasp.colorado.edu
9
+ Requires-Python: >=3.9,<4
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Natural Language :: English
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Operating System :: POSIX
16
+ Classifier: Operating System :: Unix
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering
24
+ Classifier: Topic :: Software Development
25
+ Provides-Extra: dev
26
+ Provides-Extra: test
27
+ Requires-Dist: pre-commit (>=3.3.3,<4.0.0) ; extra == "dev"
28
+ Requires-Dist: pytest (>=6.2.5) ; extra == "test"
29
+ Requires-Dist: pytest-cov (>=4.0.0,<5.0.0) ; extra == "test"
30
+ Requires-Dist: ruff (==0.2.1) ; extra == "dev"
31
+ Project-URL: homepage, https://github.com/IMAP-Science-Operations-Center
32
+ Description-Content-Type: text/markdown
33
+
34
+ # I-ALiRT Data Access Package
35
+
36
+ This lightweight Python package allows users to query and download log data.
37
+
38
+ ## Command Line Utility
39
+
40
+ ### To install
41
+
42
+ ```bash
43
+ pip install ialirt-data-access
44
+ ialirt-data-access -h
45
+ ```
46
+
47
+ ### Query / Search for logs
48
+
49
+ Find all files from a given year, day of year, and instance
50
+
51
+ ```bash
52
+ $ ialirt_data_access --url <url> ialirt-log-query --year <year> --doy <doy> --instance <instance>
53
+ ```
54
+
55
+ ### Download logs
56
+
57
+ Download a log and place in Downloads directory or optionally specify another local directory by appending --downloads_dir <directory> to the command
58
+
59
+ ```bash
60
+ $ ialirt_data_access --url <url> ialirt-log-download ----filename <filename>
61
+ ```
62
+
63
+ ## Importing as a package
64
+
65
+ ```python
66
+ import ialirt_data_access
67
+
68
+ # Search for files
69
+ results = ialirt_data_access.query(year="2024", doy="045", instance="1")
70
+ ```
71
+
72
+ ## Configuration
73
+
74
+ ### Data Access URL
75
+
76
+ To change the default URL that the package accesses, you can set
77
+ the environment variable ``IALIRT_DATA_ACCESS_URL`` or within the
78
+ package ``ialirt_data_access.config["DATA_ACCESS_URL"]``. The default
79
+ is the development server ``https://ialirt.dev.imap-mission.com``.
80
+
81
+ ## Troubleshooting
82
+
83
+ ### Network issues
84
+
85
+ #### SSL
86
+
87
+ If you encounter SSL errors similar to the following:
88
+
89
+ ```text
90
+ urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)>
91
+ ```
92
+
93
+ That generally means the Python environment you're using is not finding your system's root
94
+ certificates properly. This means you need to tell Python how to find those certificates
95
+ with the following potential solutions.
96
+
97
+ 1. **Upgrade the certifi package**
98
+
99
+ ```bash
100
+ pip install --upgrade certifi
101
+ ```
102
+
103
+ 2. **Install system certificates**
104
+ Depending on the Python version you installed the program with the command will look something like this:
105
+
106
+ ```bash
107
+ /Applications/Python\ 3.10/Install\ Certificates.command
108
+ ```
109
+
110
+ #### HTTP Error 502: Bad Gateway
111
+
112
+ This could mean that the service is temporarily down. If you
113
+ continue to encounter this, reach out to the IMAP SDC at
114
+ <imap-sdc@lasp.colorado.edu>.
115
+
@@ -0,0 +1,81 @@
1
+ # I-ALiRT Data Access Package
2
+
3
+ This lightweight Python package allows users to query and download log data.
4
+
5
+ ## Command Line Utility
6
+
7
+ ### To install
8
+
9
+ ```bash
10
+ pip install ialirt-data-access
11
+ ialirt-data-access -h
12
+ ```
13
+
14
+ ### Query / Search for logs
15
+
16
+ Find all files from a given year, day of year, and instance
17
+
18
+ ```bash
19
+ $ ialirt_data_access --url <url> ialirt-log-query --year <year> --doy <doy> --instance <instance>
20
+ ```
21
+
22
+ ### Download logs
23
+
24
+ Download a log and place in Downloads directory or optionally specify another local directory by appending --downloads_dir <directory> to the command
25
+
26
+ ```bash
27
+ $ ialirt_data_access --url <url> ialirt-log-download ----filename <filename>
28
+ ```
29
+
30
+ ## Importing as a package
31
+
32
+ ```python
33
+ import ialirt_data_access
34
+
35
+ # Search for files
36
+ results = ialirt_data_access.query(year="2024", doy="045", instance="1")
37
+ ```
38
+
39
+ ## Configuration
40
+
41
+ ### Data Access URL
42
+
43
+ To change the default URL that the package accesses, you can set
44
+ the environment variable ``IALIRT_DATA_ACCESS_URL`` or within the
45
+ package ``ialirt_data_access.config["DATA_ACCESS_URL"]``. The default
46
+ is the development server ``https://ialirt.dev.imap-mission.com``.
47
+
48
+ ## Troubleshooting
49
+
50
+ ### Network issues
51
+
52
+ #### SSL
53
+
54
+ If you encounter SSL errors similar to the following:
55
+
56
+ ```text
57
+ urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)>
58
+ ```
59
+
60
+ That generally means the Python environment you're using is not finding your system's root
61
+ certificates properly. This means you need to tell Python how to find those certificates
62
+ with the following potential solutions.
63
+
64
+ 1. **Upgrade the certifi package**
65
+
66
+ ```bash
67
+ pip install --upgrade certifi
68
+ ```
69
+
70
+ 2. **Install system certificates**
71
+ Depending on the Python version you installed the program with the command will look something like this:
72
+
73
+ ```bash
74
+ /Applications/Python\ 3.10/Install\ Certificates.command
75
+ ```
76
+
77
+ #### HTTP Error 502: Bad Gateway
78
+
79
+ This could mean that the service is temporarily down. If you
80
+ continue to encounter this, reach out to the IMAP SDC at
81
+ <imap-sdc@lasp.colorado.edu>.
@@ -0,0 +1,25 @@
1
+ """Data Access for I-ALiRT.
2
+
3
+ This package contains the data access tools for the I-ALiRT logs.
4
+ Provides a convenient way to query and download log files.
5
+ """
6
+
7
+ import os
8
+
9
+ from ialirt_data_access.io import download, query
10
+
11
+ __all__ = [
12
+ "query",
13
+ "download",
14
+ ]
15
+ __version__ = "0.1.0"
16
+
17
+
18
+ config = {
19
+ "DATA_ACCESS_URL": os.getenv("IALIRT_DATA_ACCESS_URL")
20
+ or "https://ialirt.dev.imap-mission.com",
21
+ }
22
+ """Settings configuration dictionary.
23
+
24
+ DATA_ACCESS_URL : This is the URL of the data access API.
25
+ """
@@ -0,0 +1,148 @@
1
+ #!/usr/bin/env python3
2
+
3
+ """Command line interface to query IALIRT logs in the s3 bucket.
4
+
5
+ Usage:
6
+ ialirt_data_access --debug --url <url> ialirt-log-query
7
+ --year <year> --doy <doy> --instance <instance>
8
+ """
9
+
10
+ import argparse
11
+ import logging
12
+ from pathlib import Path
13
+
14
+ import ialirt_data_access
15
+
16
+ logger = logging.getLogger(__name__)
17
+ logging.basicConfig(level=logging.INFO)
18
+
19
+
20
+ def _download_parser(args: argparse.Namespace):
21
+ """Download an I-ALiRT log.
22
+
23
+ Parameters
24
+ ----------
25
+ args : argparse.Namespace
26
+ An object containing the parsed arguments and their values
27
+ """
28
+ try:
29
+ ialirt_data_access.download(args.filename, args.downloads_dir)
30
+ except ialirt_data_access.io.IALIRTDataAccessError as e:
31
+ print(e)
32
+
33
+
34
+ def _query_parser(args: argparse.Namespace):
35
+ """Query the I-ALiRT log API.
36
+
37
+ Parameters
38
+ ----------
39
+ args : argparse.Namespace
40
+ Parsed arguments including year, doy, and instance.
41
+
42
+ Returns
43
+ -------
44
+ None
45
+ """
46
+ query_params = {
47
+ "year": args.year,
48
+ "doy": args.doy,
49
+ "instance": args.instance,
50
+ }
51
+ try:
52
+ query_results = ialirt_data_access.query(**query_params)
53
+ logger.info("Query results: %s", query_results)
54
+ print(query_results)
55
+ except ialirt_data_access.io.IALIRTDataAccessError as e:
56
+ logger.error("An error occurred: %s", e)
57
+ print(e)
58
+ return
59
+
60
+
61
+ def main():
62
+ """Parse the command line arguments.
63
+
64
+ Run the command line interface to the I-ALiRT Data Access API.
65
+ """
66
+ url_help = (
67
+ "URL of the IALIRT API. "
68
+ "The default is https://ialirt.dev.imap-mission.com. This can also be "
69
+ "set using the IALIRT_DATA_ACCESS_URL environment variable."
70
+ )
71
+
72
+ parser = argparse.ArgumentParser(prog="ialirt-data-access")
73
+ parser.add_argument(
74
+ "--version",
75
+ action="version",
76
+ version=f"%(prog)s {ialirt_data_access.__version__}",
77
+ )
78
+ parser.add_argument("--url", type=str, required=False, help=url_help)
79
+ # Logging level
80
+ parser.add_argument(
81
+ "--vv",
82
+ "--debug",
83
+ help="Print lots of debugging statements",
84
+ action="store_const",
85
+ dest="loglevel",
86
+ const=logging.DEBUG,
87
+ default=logging.WARNING,
88
+ )
89
+ parser.add_argument(
90
+ "-v",
91
+ "--verbose",
92
+ help="Add verbose output",
93
+ action="store_const",
94
+ dest="loglevel",
95
+ const=logging.INFO,
96
+ )
97
+
98
+ subparsers = parser.add_subparsers(required=True)
99
+
100
+ # Query command
101
+ query_parser = subparsers.add_parser("ialirt-log-query")
102
+ query_parser.add_argument(
103
+ "--year", type=str, required=True, help="Year of the logs (e.g., 2024)."
104
+ )
105
+ query_parser.add_argument(
106
+ "--doy", type=str, required=True, help="Day of year of the logs (e.g., 045)."
107
+ )
108
+ query_parser.add_argument(
109
+ "--instance",
110
+ type=str,
111
+ required=True,
112
+ help="Instance number (e.g., 1).",
113
+ choices=[
114
+ "1",
115
+ "2",
116
+ ],
117
+ )
118
+ query_parser.set_defaults(func=_query_parser)
119
+
120
+ # Download command
121
+ download_parser = subparsers.add_parser("ialirt-log-download")
122
+ download_parser.add_argument(
123
+ "--filename",
124
+ type=str,
125
+ required=True,
126
+ help="Example: flight_iois.log.YYYY-DOYTHH:MM:SS.ssssss",
127
+ )
128
+ download_parser.add_argument(
129
+ "--downloads_dir",
130
+ type=Path,
131
+ required=False,
132
+ help="Example: flight_iois.log.YYYY-DOYTHH:MM:SS.ssssss",
133
+ )
134
+ download_parser.set_defaults(func=_download_parser)
135
+
136
+ # Parse the arguments and set the values
137
+ args = parser.parse_args()
138
+ logging.basicConfig(level=args.loglevel)
139
+
140
+ if args.url:
141
+ # Explicit url from the command line
142
+ ialirt_data_access.config["DATA_ACCESS_URL"] = args.url
143
+
144
+ args.func(args)
145
+
146
+
147
+ if __name__ == "__main__":
148
+ main()
@@ -0,0 +1,147 @@
1
+ """The ``io`` module."""
2
+
3
+ import contextlib
4
+ import json
5
+ import logging
6
+ import urllib.request
7
+ from pathlib import Path
8
+ from typing import Optional
9
+ from urllib.error import HTTPError, URLError
10
+ from urllib.parse import urlencode
11
+
12
+ import ialirt_data_access
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ class IALIRTDataAccessError(Exception):
18
+ """Base class for exceptions in this module."""
19
+
20
+ pass
21
+
22
+
23
+ @contextlib.contextmanager
24
+ def _get_url_response(request: urllib.request.Request):
25
+ """Get the response from a URL request.
26
+
27
+ This is a helper function to make it easier to handle
28
+ the different types of errors that can occur when
29
+ opening a URL and write out the response body.
30
+ """
31
+ try:
32
+ # Open the URL and yield the response
33
+ with urllib.request.urlopen(request) as response:
34
+ yield response
35
+
36
+ except HTTPError as e:
37
+ message = (
38
+ f"HTTP Error: {e.code} - {e.reason}\n"
39
+ f"Server Message: {e.read().decode('utf-8')}"
40
+ )
41
+ raise IALIRTDataAccessError(message) from e
42
+ except URLError as e:
43
+ message = f"URL Error: {e.reason}"
44
+ raise IALIRTDataAccessError(message) from e
45
+
46
+
47
+ def _validate_query_params(year: str, doy: str, instance: str):
48
+ """Validate the query parameters for the IALIRT log API.
49
+
50
+ Parameters
51
+ ----------
52
+ year : str
53
+ Year, must be a 4-digit string (e.g., '2024').
54
+ doy : str
55
+ Day of year, must be a string between '001' and '365'.
56
+ instance : str
57
+ Instance number, must be either '1' or '2'.
58
+
59
+ Raises
60
+ ------
61
+ ValueError
62
+ If any parameter is invalid.
63
+ """
64
+ if not (year.isdigit() and len(year) == 4):
65
+ raise ValueError("Year must be a 4-digit string (e.g., '2024').")
66
+ if not (doy.isdigit() and 1 <= int(doy) <= 366):
67
+ raise ValueError("DOY must be a string between '001' and '365'.")
68
+ if instance not in {"1", "2"}:
69
+ raise ValueError("Instance must be '1' or '2'.")
70
+
71
+
72
+ def query(*, year: str, doy: str, instance: str) -> list[str]:
73
+ """Query the logs.
74
+
75
+ Parameters
76
+ ----------
77
+ year : str
78
+ Year
79
+ doy : str
80
+ Day of year
81
+ instance : str
82
+ Instance number
83
+
84
+ Returns
85
+ -------
86
+ list
87
+ List of files matching the query
88
+ """
89
+ query_params = {
90
+ "year": year,
91
+ "doy": doy,
92
+ "instance": instance,
93
+ }
94
+ _validate_query_params(**query_params)
95
+
96
+ url = f"{ialirt_data_access.config['DATA_ACCESS_URL']}"
97
+ url += f"/ialirt-log-query?{urlencode(query_params)}"
98
+
99
+ logger.info("Querying for %s with url %s", query_params, url)
100
+ request = urllib.request.Request(url, method="GET")
101
+ with _get_url_response(request) as response:
102
+ # Retrieve the response as a list of files
103
+ items = response.read().decode("utf-8")
104
+ logger.debug("Received response: %s", items)
105
+ # Decode the JSON string into a list
106
+ items = json.loads(items)
107
+ logger.debug("Decoded JSON: %s", items)
108
+ return items
109
+
110
+
111
+ def download(filename: str, downloads_dir: Optional[Path] = None) -> Path:
112
+ """Download the logs.
113
+
114
+ Parameters
115
+ ----------
116
+ filename : str
117
+ Filename
118
+ downloads_dir : Path
119
+ Directory to save the file
120
+
121
+ Returns
122
+ -------
123
+ destination: pathlib.Path
124
+ Path to the downloaded file
125
+ """
126
+ if downloads_dir is None:
127
+ downloads_dir = Path.home() / "Downloads"
128
+
129
+ url = f"{ialirt_data_access.config['DATA_ACCESS_URL']}"
130
+ url += f"/ialirt-log-download/logs/{filename}"
131
+
132
+ downloads_dir.mkdir(parents=True, exist_ok=True)
133
+ destination = downloads_dir / filename
134
+
135
+ if destination.exists():
136
+ logger.info("File already exists: %s", destination)
137
+ return destination
138
+
139
+ logger.info("Downloading %s with url %s", filename, url)
140
+ request = urllib.request.Request(url, method="GET")
141
+ with _get_url_response(request) as response:
142
+ logger.debug("Received response: %s", response)
143
+ with open(destination, "wb") as local_file:
144
+ local_file.write(response.read())
145
+ print(f"Successfully downloaded the file to: {destination}")
146
+
147
+ return destination
@@ -0,0 +1,68 @@
1
+ [build-system]
2
+ requires = ["poetry-core"]
3
+ build-backend = "poetry.core.masonry.api"
4
+
5
+ [tool.poetry]
6
+ name = "ialirt-data-access"
7
+ version = "0.1.0"
8
+ description = "I-ALiRT Data Access"
9
+ authors = ["IMAP SDC Developers <imap-sdc@lists.lasp.colorado.edu>"]
10
+ readme = "README.md"
11
+ license = "MIT"
12
+ keywords = ["IMAP", "SDC", "SOC", "SDS", "Science Operations"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Natural Language :: English",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Software Development",
23
+ "Topic :: Scientific/Engineering",
24
+ "Operating System :: Microsoft :: Windows",
25
+ "Operating System :: POSIX",
26
+ "Operating System :: Unix",
27
+ "Operating System :: MacOS",
28
+ ]
29
+
30
+ [tool.poetry.scripts]
31
+ ialirt-data-access = "ialirt_data_access.cli:main"
32
+
33
+ [tool.poetry.urls]
34
+ homepage = "https://github.com/IMAP-Science-Operations-Center"
35
+
36
+ [tool.poetry.dependencies]
37
+ python = ">=3.9,<4"
38
+ # Optional dependencies
39
+ pre-commit = {version="^3.3.3", optional=true}
40
+ pytest = {version=">=6.2.5", optional=true}
41
+ pytest-cov = {version="^4.0.0", optional=true}
42
+ ruff = {version="==0.2.1", optional=true}
43
+
44
+ [tool.poetry.extras]
45
+ dev = ["pre-commit", "ruff"]
46
+ test = ["pytest", "pytest-cov"]
47
+
48
+ [tool.pytest.ini_options]
49
+ testpaths = [
50
+ "tests",
51
+ ]
52
+ addopts = "-ra"
53
+
54
+ [tool.ruff]
55
+ target-version = "py39"
56
+ lint.select = ["B", "E", "D", "F", "I", "N", "S", "W", "PL", "PT", "UP", "RUF"]
57
+ # D104: Missing docstring in public package
58
+ # D203: 1 blank line required before class docstring
59
+ # D213: Multi-line docstring summary should start at the second line
60
+ # D413: Missing blank line after last section
61
+ # PLR2004: Magic value in comparison
62
+ lint.ignore = ["D104", "D203", "D213", "D413", "PLR2004"]
63
+
64
+ [tool.ruff.lint.per-file-ignores]
65
+ # S101: Use of assert detected
66
+ "tests/*" = ["S101"]
67
+ # S310: Audit URL open for permitted schemes.
68
+ "ialirt_data_access/*" = ["S310"]