dataquery-sdk 0.1.0__tar.gz → 0.1.2__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 (29) hide show
  1. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/CHANGELOG.md +6 -2
  2. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/PKG-INFO +4 -8
  3. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/README.md +1 -1
  4. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/__init__.py +5 -4
  5. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/auth.py +7 -26
  6. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/auto_download.py +50 -55
  7. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/cli.py +11 -33
  8. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/client.py +223 -351
  9. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/config.py +14 -24
  10. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/connection_pool.py +17 -61
  11. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/dataquery.py +315 -394
  12. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/exceptions.py +12 -20
  13. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/logging_config.py +6 -18
  14. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/models.py +121 -455
  15. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/rate_limiter.py +7 -56
  16. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/retry.py +8 -25
  17. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/utils.py +15 -30
  18. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/PKG-INFO +4 -8
  19. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/requires.txt +2 -6
  20. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/pyproject.toml +23 -35
  21. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/CONTRIBUTING.md +0 -0
  22. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/LICENSE +0 -0
  23. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/MANIFEST.in +0 -0
  24. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery/py.typed +0 -0
  25. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/SOURCES.txt +0 -0
  26. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/dependency_links.txt +0 -0
  27. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/entry_points.txt +0 -0
  28. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/dataquery_sdk.egg-info/top_level.txt +0 -0
  29. {dataquery_sdk-0.1.0 → dataquery_sdk-0.1.2}/setup.cfg +0 -0
@@ -14,6 +14,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
14
14
  ## [0.0.8] - 2025-10-21
15
15
  - Made label and expression parameters optional in attribute api response
16
16
  ## [0.0.9] - 2025-12-09
17
- - Perfomance optimizations
17
+ - Performance optimizations
18
18
  ## [0.1.0] - 2025-12-10
19
- Time serires data made optional
19
+ - Time series data made optional
20
+ ## [0.1.1] - 2026-02-18
21
+ - Non blocking IO
22
+ ## [0.1.2] - 2026-02-18
23
+ - File already exists status added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataquery-sdk
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Python SDK for DATAQUERY Data API - Query, download, and check availability of economic data files
5
5
  Author-email: DATAQUERY SDK Team <support@dataquery.com>
6
6
  Project-URL: Homepage, https://github.com/dataquery/dataquery-sdk
@@ -34,10 +34,8 @@ Provides-Extra: dev
34
34
  Requires-Dist: pytest>=7.0.0; extra == "dev"
35
35
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
36
36
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
37
- Requires-Dist: black>=23.0.0; extra == "dev"
38
- Requires-Dist: flake8>=6.0.0; extra == "dev"
37
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
39
38
  Requires-Dist: mypy>=1.5.0; extra == "dev"
40
- Requires-Dist: isort>=5.12.0; extra == "dev"
41
39
  Requires-Dist: pre-commit>=3.0.0; extra == "dev"
42
40
  Requires-Dist: pip-audit>=2.7.0; extra == "dev"
43
41
  Requires-Dist: build>=1.3.0; extra == "dev"
@@ -53,10 +51,8 @@ Provides-Extra: all
53
51
  Requires-Dist: pytest>=7.0.0; extra == "all"
54
52
  Requires-Dist: pytest-asyncio>=0.21.0; extra == "all"
55
53
  Requires-Dist: pytest-cov>=4.0.0; extra == "all"
56
- Requires-Dist: black>=23.0.0; extra == "all"
57
- Requires-Dist: flake8>=6.0.0; extra == "all"
54
+ Requires-Dist: ruff>=0.4.0; extra == "all"
58
55
  Requires-Dist: mypy>=1.5.0; extra == "all"
59
- Requires-Dist: isort>=5.12.0; extra == "all"
60
56
  Requires-Dist: pre-commit>=3.0.0; extra == "all"
61
57
  Requires-Dist: mkdocs>=1.5.0; extra == "all"
62
58
  Requires-Dist: mkdocs-material>=9.0.0; extra == "all"
@@ -279,7 +275,7 @@ DATAQUERY_CLIENT_SECRET=your_client_secret
279
275
 
280
276
  # Optional - API Endpoints
281
277
  DATAQUERY_BASE_URL=https://api-developer.jpmorgan.com
282
- DATAQUERY_FILES_BASE_URL=https://api-strm-gw01.jpmchase.com
278
+ DATAQUERY_FILES_BASE_URL=https://api-dataquery.jpmchase.com
283
279
 
284
280
  # Optional - Performance
285
281
  DATAQUERY_MAX_RETRIES=3
@@ -210,7 +210,7 @@ DATAQUERY_CLIENT_SECRET=your_client_secret
210
210
 
211
211
  # Optional - API Endpoints
212
212
  DATAQUERY_BASE_URL=https://api-developer.jpmorgan.com
213
- DATAQUERY_FILES_BASE_URL=https://api-strm-gw01.jpmchase.com
213
+ DATAQUERY_FILES_BASE_URL=https://api-dataquery.jpmchase.com
214
214
 
215
215
  # Optional - Performance
216
216
  DATAQUERY_MAX_RETRIES=3
@@ -14,9 +14,9 @@ Quick Start:
14
14
  For more information, visit: https://github.com/dataquery/dataquery-sdk
15
15
  """
16
16
 
17
- __version__ = "0.1.0"
17
+ __version__ = "0.1.2"
18
18
  __author__ = "DATAQUERY SDK Team"
19
- __email__ = "dataquery@jpmorgan.com"
19
+ __email__ = "dataquery_support@jpmorgan.com"
20
20
  __license__ = "MIT"
21
21
  __url__ = "https://github.com/jpmorganchase/dataquery-sdk"
22
22
 
@@ -43,7 +43,7 @@ from .connection_pool import (
43
43
  )
44
44
 
45
45
  # Core imports
46
- from .dataquery import DataQuery, setup_logging
46
+ from .dataquery import DataQuery
47
47
  from .exceptions import (
48
48
  AuthenticationError,
49
49
  AvailabilityError,
@@ -52,6 +52,7 @@ from .exceptions import (
52
52
  DateRangeError,
53
53
  DownloadError,
54
54
  FileNotFoundError,
55
+ FileNotFoundInGroupError,
55
56
  FileTypeError,
56
57
  GroupNotFoundError,
57
58
  NetworkError,
@@ -124,7 +125,6 @@ from .utils import (
124
125
  __all__ = [
125
126
  # Core
126
127
  "DataQuery",
127
- "setup_logging",
128
128
  # Models
129
129
  "ClientConfig",
130
130
  "Group",
@@ -153,6 +153,7 @@ __all__ = [
153
153
  "AvailabilityError",
154
154
  "GroupNotFoundError",
155
155
  "FileNotFoundError",
156
+ "FileNotFoundInGroupError",
156
157
  "DateRangeError",
157
158
  "FileTypeError",
158
159
  "WorkflowError",
@@ -32,9 +32,7 @@ class TokenManager:
32
32
  # Prefer explicit token_storage_dir only when token storage is enabled,
33
33
  # else fallback to download_dir/.tokens if download_dir is provided.
34
34
  base_dir: Optional[Path] = None
35
- token_storage_enabled = bool(
36
- getattr(self.config, "token_storage_enabled", False)
37
- )
35
+ token_storage_enabled = bool(getattr(self.config, "token_storage_enabled", False))
38
36
  token_storage_dir = getattr(self.config, "token_storage_dir", None)
39
37
  if token_storage_enabled and token_storage_dir:
40
38
  base_dir = Path(token_storage_dir)
@@ -97,16 +95,13 @@ class TokenManager:
97
95
 
98
96
  # Validate required fields
99
97
  if not self.config.client_id or not self.config.client_secret:
100
- raise ConfigurationError(
101
- "client_id and client_secret are required for OAuth"
102
- )
98
+ raise ConfigurationError("client_id and client_secret are required for OAuth")
103
99
 
104
100
  # Create token request
105
101
  token_request = TokenRequest(
106
102
  grant_type=self.config.grant_type,
107
103
  client_id=self.config.client_id,
108
104
  client_secret=self.config.client_secret,
109
- # scope removed
110
105
  aud=getattr(self.config, "aud", None),
111
106
  )
112
107
 
@@ -143,9 +138,7 @@ class TokenManager:
143
138
  status=response.status,
144
139
  error=error_data,
145
140
  )
146
- raise AuthenticationError(
147
- f"OAuth token request failed: {response.status}"
148
- )
141
+ raise AuthenticationError(f"OAuth token request failed: {response.status}")
149
142
 
150
143
  except Exception as e:
151
144
  logger.error("Error getting OAuth token", error=str(e))
@@ -221,9 +214,7 @@ class TokenManager:
221
214
 
222
215
  # Convert timestamp back to datetime
223
216
  if "issued_at" in token_data:
224
- token_data["issued_at"] = datetime.fromisoformat(
225
- token_data["issued_at"]
226
- )
217
+ token_data["issued_at"] = datetime.fromisoformat(token_data["issued_at"])
227
218
 
228
219
  self.current_token = OAuthToken(**token_data)
229
220
 
@@ -233,9 +224,7 @@ class TokenManager:
233
224
  self.current_token = None
234
225
  return None
235
226
 
236
- logger.info(
237
- "Token loaded from storage", expires_at=self.current_token.expires_at
238
- )
227
+ logger.info("Token loaded from storage", expires_at=self.current_token.expires_at)
239
228
  return self.current_token
240
229
 
241
230
  except Exception as e:
@@ -298,16 +287,8 @@ class TokenManager:
298
287
  return {
299
288
  "status": self.current_token.status.value,
300
289
  "token_type": self.current_token.token_type,
301
- "issued_at": (
302
- self.current_token.issued_at.isoformat()
303
- if self.current_token.issued_at
304
- else None
305
- ),
306
- "expires_at": (
307
- self.current_token.expires_at.isoformat()
308
- if self.current_token.expires_at
309
- else None
310
- ),
290
+ "issued_at": (self.current_token.issued_at.isoformat() if self.current_token.issued_at else None),
291
+ "expires_at": (self.current_token.expires_at.isoformat() if self.current_token.expires_at else None),
311
292
  "is_expired": self.current_token.is_expired,
312
293
  "has_refresh_token": self.current_token.refresh_token is not None,
313
294
  }
@@ -121,8 +121,7 @@ class AutoDownloadManager:
121
121
  self._stop_event.clear()
122
122
 
123
123
  self.logger.info(
124
- f"Starting auto-download for group '{self.group_id}' "
125
- f"(interval: {self.interval_minutes} minutes)"
124
+ f"Starting auto-download for group '{self.group_id}' (interval: {self.interval_minutes} minutes)"
126
125
  )
127
126
 
128
127
  # Start the monitoring task
@@ -145,9 +144,7 @@ class AutoDownloadManager:
145
144
  try:
146
145
  await asyncio.wait_for(self._task, timeout=5.0)
147
146
  except asyncio.TimeoutError:
148
- self.logger.warning(
149
- "Monitoring task did not stop gracefully, cancelling..."
150
- )
147
+ self.logger.warning("Monitoring task did not stop gracefully, cancelling...")
151
148
  self._task.cancel()
152
149
  try:
153
150
  await self._task
@@ -166,7 +163,7 @@ class AutoDownloadManager:
166
163
  self.stats["last_check_time"] = datetime.now()
167
164
 
168
165
  except Exception as e:
169
- self.logger.error(f"Error in monitoring loop: {e}")
166
+ self.logger.error("Error in monitoring loop: %s", e)
170
167
  self.stats["errors"].append(
171
168
  {
172
169
  "timestamp": datetime.now().isoformat(),
@@ -180,13 +177,11 @@ class AutoDownloadManager:
180
177
  try:
181
178
  await self._safe_call_callback(self.error_callback, e)
182
179
  except Exception as cb_error:
183
- self.logger.error(f"Error in error callback: {cb_error}")
180
+ self.logger.error("Error in error callback: %s", cb_error)
184
181
 
185
182
  # Wait for the specified interval or until stopped
186
183
  try:
187
- await asyncio.wait_for(
188
- self._stop_event.wait(), timeout=self.interval_minutes * 60
189
- )
184
+ await asyncio.wait_for(self._stop_event.wait(), timeout=self.interval_minutes * 60)
190
185
  # If we reach here, stop was requested
191
186
  break
192
187
  except asyncio.TimeoutError:
@@ -196,7 +191,7 @@ class AutoDownloadManager:
196
191
  except asyncio.CancelledError:
197
192
  self.logger.info("Monitoring loop cancelled")
198
193
  except Exception as e:
199
- self.logger.error(f"Fatal error in monitoring loop: {e}")
194
+ self.logger.error("Fatal error in monitoring loop: %s", e)
200
195
  finally:
201
196
  self._running = False
202
197
 
@@ -220,38 +215,34 @@ class AutoDownloadManager:
220
215
  )
221
216
 
222
217
  if not isinstance(available_files, list) or not available_files:
223
- self.logger.debug(
224
- f"No available files for group '{self.group_id}' between {start_date} and {end_date}"
225
- )
218
+ self.logger.debug(f"No available files for group '{self.group_id}' between {start_date} and {end_date}")
226
219
  return
227
220
 
228
- self.logger.debug(
229
- f"Found {len(available_files)} available file entries for group '{self.group_id}'"
230
- )
221
+ self.logger.debug(f"Found {len(available_files)} available file entries for group '{self.group_id}'")
231
222
 
232
223
  # Build eligible downloads
233
- eligible: List[tuple[str, str, str]] = [] # (file_id, date_str, file_key)
224
+ eligible: List[tuple[str, str, str]] = [] # (file_group_id, date_str, file_key)
234
225
  for item in available_files:
235
226
  if not self._running:
236
227
  break
237
- file_id = item.get("file-group-id")
228
+ file_group_id = item.get("file-group-id")
238
229
  date_str = item.get("file-datetime")
239
230
  avail_flag = item.get("is-available")
240
231
  is_available = bool(avail_flag)
241
- if not file_id or not date_str or not is_available:
232
+ if not file_group_id or not date_str or not is_available:
242
233
  continue
243
234
  if self.file_filter and not self.file_filter(item):
244
235
  continue
245
- file_key = f"{file_id}_{date_str}"
236
+ file_key = f"{file_group_id}_{date_str}"
246
237
  if file_key in self._downloaded_files:
247
238
  continue
248
239
  if self._failed_files.get(file_key, 0) >= self.max_retries:
249
240
  continue
250
- if self._file_exists_locally(file_id, date_str):
241
+ if self._file_exists_locally(file_group_id, date_str):
251
242
  self.stats["files_skipped"] += 1
252
243
  self._downloaded_files.add(file_key)
253
244
  continue
254
- eligible.append((file_id, date_str, file_key))
245
+ eligible.append((file_group_id, date_str, file_key))
255
246
 
256
247
  if not eligible:
257
248
  return
@@ -265,20 +256,13 @@ class AutoDownloadManager:
265
256
  async with semaphore:
266
257
  await self._download_file(fid, dstr, fkey)
267
258
 
268
- tasks = [
269
- asyncio.create_task(worker(fid, dstr, fkey))
270
- for fid, dstr, fkey in eligible
271
- ]
259
+ tasks = [asyncio.create_task(worker(fid, dstr, fkey)) for fid, dstr, fkey in eligible]
272
260
  await asyncio.gather(*tasks, return_exceptions=True)
273
261
 
274
262
  except Exception as e:
275
- self.logger.error(f"Error checking available files: {e}")
263
+ self.logger.error("Error checking available files: %s", e)
276
264
  raise
277
265
 
278
- async def _process_file(self, file_info):
279
- """Deprecated path; available-files flow supersedes this."""
280
- return
281
-
282
266
  def _get_dates_to_check(self) -> List[str]:
283
267
  """Get list of dates to check for files."""
284
268
  if self.check_current_date_only:
@@ -293,11 +277,7 @@ class AutoDownloadManager:
293
277
  dates.append(date.strftime("%Y%m%d"))
294
278
  return dates
295
279
 
296
- async def _check_and_download_file_for_date(self, file_id: str, date_str: str):
297
- """Deprecated path; available-files flow supersedes this."""
298
- return
299
-
300
- def _file_exists_locally(self, file_id: str, date_str: str) -> bool:
280
+ def _file_exists_locally(self, file_group_id: str, date_str: str) -> bool:
301
281
  """Check if file already exists in the destination directory."""
302
282
  # Look for files that might match this file_group_id and date
303
283
  # This is a heuristic approach since we don't know the exact filename
@@ -306,14 +286,14 @@ class AutoDownloadManager:
306
286
  if file_path.is_file():
307
287
  filename = file_path.name
308
288
  # Check if filename contains the file_group_id and date
309
- if file_id in filename and date_str in filename:
289
+ if file_group_id in filename and date_str in filename:
310
290
  return True
311
291
 
312
292
  return False
313
293
 
314
- async def _download_file(self, file_id: str, date_str: str, file_key: str):
294
+ async def _download_file(self, file_group_id: str, date_str: str, file_key: str):
315
295
  """Download a file."""
316
- self.logger.info(f"Downloading '{file_id}' for {date_str}...")
296
+ self.logger.info("Downloading '%s' for %s", file_group_id, date_str)
317
297
 
318
298
  # Create progress callback that updates statistics
319
299
  def progress_wrapper(progress: DownloadProgress):
@@ -331,17 +311,18 @@ class AutoDownloadManager:
331
311
  else:
332
312
  self.progress_callback(progress)
333
313
  except Exception as e:
334
- self.logger.error(f"Error in progress callback: {e}")
314
+ self.logger.error("Error in progress callback: %s", e)
335
315
 
336
316
  try:
337
317
  result = await self.client.download_file_async(
338
- file_group_id=file_id,
318
+ file_group_id=file_group_id,
339
319
  file_datetime=date_str,
340
320
  options=self.download_options,
341
321
  progress_callback=progress_wrapper,
342
322
  )
343
323
 
344
324
  succeeded = False
325
+ already_exists = False
345
326
  if result is not None and getattr(result, "status", None) is not None:
346
327
  try:
347
328
  from .models import ( # import inside to avoid cycles in some tools
@@ -349,19 +330,29 @@ class AutoDownloadManager:
349
330
  )
350
331
 
351
332
  succeeded = result.status == DownloadStatus.COMPLETED
333
+ already_exists = result.status == DownloadStatus.ALREADY_EXISTS
352
334
  except Exception:
353
335
  succeeded = False
354
336
 
355
- if succeeded:
356
- self.logger.info(f"Successfully downloaded '{file_id}' for {date_str}")
337
+ if already_exists:
338
+ self.logger.info(
339
+ "File already exists for '%s' for %s, skipping",
340
+ file_group_id,
341
+ date_str,
342
+ )
343
+ self.stats["files_skipped"] += 1
344
+ self._downloaded_files.add(file_key)
345
+ if file_key in self._failed_files:
346
+ del self._failed_files[file_key]
347
+
348
+ elif succeeded:
349
+ self.logger.info("Successfully downloaded '%s' for %s", file_group_id, date_str)
357
350
  self.stats["files_downloaded"] += 1
358
351
  self._downloaded_files.add(file_key)
359
352
  # Accumulate total bytes if provided
360
353
  try:
361
354
  if getattr(result, "file_size", None):
362
- self.stats["total_bytes_downloaded"] += (
363
- int(result.file_size) or 0
364
- )
355
+ self.stats["total_bytes_downloaded"] += int(result.file_size) or 0
365
356
  except Exception:
366
357
  pass
367
358
 
@@ -370,19 +361,23 @@ class AutoDownloadManager:
370
361
  del self._failed_files[file_key]
371
362
 
372
363
  else:
373
- error_msg = (
374
- getattr(result, "error_message", "Unknown error")
375
- if result
376
- else "No result"
377
- )
364
+ error_msg = getattr(result, "error_message", "Unknown error") if result else "No result"
378
365
  self.logger.error(
379
- f"Download failed for '{file_id}' for {date_str}: {error_msg}"
366
+ "Download failed for '%s' for %s: %s",
367
+ file_group_id,
368
+ date_str,
369
+ error_msg,
380
370
  )
381
371
  self._failed_files[file_key] = self._failed_files.get(file_key, 0) + 1
382
372
  self.stats["download_failures"] += 1
383
373
 
384
374
  except Exception as e:
385
- self.logger.error(f"Exception downloading '{file_id}' for {date_str}: {e}")
375
+ self.logger.error(
376
+ "Exception downloading '%s' for %s: %s",
377
+ file_group_id,
378
+ date_str,
379
+ e,
380
+ )
386
381
  self._failed_files[file_key] = self._failed_files.get(file_key, 0) + 1
387
382
  self.stats["download_failures"] += 1
388
383
  raise
@@ -395,7 +390,7 @@ class AutoDownloadManager:
395
390
  else:
396
391
  callback(*args, **kwargs)
397
392
  except Exception as e:
398
- self.logger.error(f"Error in callback: {e}")
393
+ self.logger.error("Error in callback: %s", e)
399
394
 
400
395
  def get_stats(self) -> Dict[str, Any]:
401
396
  """Get auto-download statistics."""
@@ -11,18 +11,14 @@ from dataquery import DataQuery
11
11
 
12
12
  def create_parser() -> argparse.ArgumentParser:
13
13
  """Create the top-level CLI parser with subcommands."""
14
- parser = argparse.ArgumentParser(
15
- description="Command Line Interface for the DataQuery SDK"
16
- )
14
+ parser = argparse.ArgumentParser(description="Command Line Interface for the DataQuery SDK")
17
15
  parser.add_argument("--env-file", type=str, default=None, help="Path to .env file")
18
16
  subparsers = parser.add_subparsers(dest="command")
19
17
 
20
18
  # groups
21
19
  p_groups = subparsers.add_parser("groups", help="List or search groups")
22
20
  p_groups.add_argument("--json", action="store_true", help="Output JSON")
23
- p_groups.add_argument(
24
- "--limit", type=int, default=None, help="Limit number of results"
25
- )
21
+ p_groups.add_argument("--limit", type=int, default=None, help="Limit number of results")
26
22
  p_groups.add_argument("--search", type=str, default=None, help="Search keywords")
27
23
 
28
24
  # files
@@ -43,22 +39,14 @@ def create_parser() -> argparse.ArgumentParser:
43
39
  p_dl.add_argument("--file-group-id", default=None)
44
40
  p_dl.add_argument("--file-datetime", default=None)
45
41
  p_dl.add_argument("--destination", type=str, default=None)
46
- p_dl.add_argument(
47
- "--watch", action="store_true", help="Watch a group for new files"
48
- )
42
+ p_dl.add_argument("--watch", action="store_true", help="Watch a group for new files")
49
43
  p_dl.add_argument("--group-id", default=None, help="Required with --watch")
50
44
  p_dl.add_argument("--json", action="store_true")
51
- p_dl.add_argument(
52
- "--num-parts", type=int, default=5, help="Number of parallel parts"
53
- )
54
- p_dl.add_argument(
55
- "--chunk-size", type=int, default=None, help="Chunk size in bytes"
56
- )
45
+ p_dl.add_argument("--num-parts", type=int, default=5, help="Number of parallel parts")
46
+ p_dl.add_argument("--chunk-size", type=int, default=None, help="Chunk size in bytes")
57
47
 
58
48
  # download-group
59
- p_dlg = subparsers.add_parser(
60
- "download-group", help="Download all files in a group"
61
- )
49
+ p_dlg = subparsers.add_parser("download-group", help="Download all files in a group")
62
50
  p_dlg.add_argument("--group-id", required=True)
63
51
  p_dlg.add_argument("--start-date", required=True)
64
52
  p_dlg.add_argument("--end-date", required=True)
@@ -101,9 +89,7 @@ async def cmd_groups(args: argparse.Namespace) -> int:
101
89
  for g in items:
102
90
  try:
103
91
  d = g.model_dump()
104
- print(
105
- f"{d.get('group_id') or d.get('group-id')}\t{d.get('group_name') or d.get('group-name')}"
106
- )
92
+ print(f"{d.get('group_id') or d.get('group-id')}\t{d.get('group_name') or d.get('group-name')}")
107
93
  except Exception:
108
94
  print(str(g))
109
95
  return 0
@@ -125,9 +111,7 @@ async def cmd_files(args: argparse.Namespace) -> int:
125
111
  for f in files:
126
112
  try:
127
113
  d = f.model_dump()
128
- print(
129
- f"{d.get('file_group_id') or d.get('file-group-id')}\t{d.get('file_type')}"
130
- )
114
+ print(f"{d.get('file_group_id') or d.get('file-group-id')}\t{d.get('file_type')}")
131
115
  except Exception:
132
116
  print(str(f))
133
117
  return 0
@@ -135,9 +119,7 @@ async def cmd_files(args: argparse.Namespace) -> int:
135
119
 
136
120
  async def cmd_availability(args: argparse.Namespace) -> int:
137
121
  async with DataQuery(args.env_file) as dq:
138
- avail = await dq.check_availability_async(
139
- args.file_group_id, args.file_datetime
140
- )
122
+ avail = await dq.check_availability_async(args.file_group_id, args.file_datetime)
141
123
  if args.json:
142
124
  try:
143
125
  print(json.dumps(getattr(avail, "model_dump")(), indent=2))
@@ -210,9 +192,7 @@ async def cmd_download_group(args: argparse.Namespace) -> int:
210
192
  if args.json:
211
193
  print(json.dumps(results, indent=2))
212
194
  else:
213
- print(
214
- f"Downloaded {results.get('successful', 0)} files to {args.destination}"
215
- )
195
+ print(f"Downloaded {results.get('successful', 0)} files to {args.destination}")
216
196
  if results.get("failed", 0) > 0:
217
197
  print(f"Failed: {results.get('failed', 0)}")
218
198
  return 0
@@ -221,9 +201,7 @@ async def cmd_download_group(args: argparse.Namespace) -> int:
221
201
  def cmd_config_show(args: argparse.Namespace) -> int:
222
202
  from dataquery.config import EnvConfig
223
203
 
224
- EnvConfig.create_client_config(
225
- env_file=Path(args.env_file) if getattr(args, "env_file", None) else None
226
- )
204
+ EnvConfig.create_client_config(env_file=Path(args.env_file) if getattr(args, "env_file", None) else None)
227
205
  print("Configuration loaded")
228
206
  return 0
229
207