databar 2.0.1__tar.gz → 2.0.3__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 (25) hide show
  1. {databar-2.0.1/src/databar.egg-info → databar-2.0.3}/PKG-INFO +1 -1
  2. {databar-2.0.1 → databar-2.0.3}/pyproject.toml +1 -1
  3. {databar-2.0.1 → databar-2.0.3}/src/databar/__init__.py +1 -1
  4. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/_auth.py +15 -1
  5. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/enrichments.py +14 -5
  6. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/tables.py +9 -9
  7. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/tasks.py +1 -1
  8. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/waterfalls.py +13 -4
  9. {databar-2.0.1 → databar-2.0.3}/src/databar/client.py +60 -4
  10. {databar-2.0.1 → databar-2.0.3/src/databar.egg-info}/PKG-INFO +1 -1
  11. {databar-2.0.1 → databar-2.0.3}/tests/test_cli.py +1 -1
  12. {databar-2.0.1 → databar-2.0.3}/LICENSE +0 -0
  13. {databar-2.0.1 → databar-2.0.3}/README.md +0 -0
  14. {databar-2.0.1 → databar-2.0.3}/setup.cfg +0 -0
  15. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/__init__.py +0 -0
  16. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/_output.py +0 -0
  17. {databar-2.0.1 → databar-2.0.3}/src/databar/cli/app.py +0 -0
  18. {databar-2.0.1 → databar-2.0.3}/src/databar/exceptions.py +0 -0
  19. {databar-2.0.1 → databar-2.0.3}/src/databar/models.py +0 -0
  20. {databar-2.0.1 → databar-2.0.3}/src/databar.egg-info/SOURCES.txt +0 -0
  21. {databar-2.0.1 → databar-2.0.3}/src/databar.egg-info/dependency_links.txt +0 -0
  22. {databar-2.0.1 → databar-2.0.3}/src/databar.egg-info/entry_points.txt +0 -0
  23. {databar-2.0.1 → databar-2.0.3}/src/databar.egg-info/requires.txt +0 -0
  24. {databar-2.0.1 → databar-2.0.3}/src/databar.egg-info/top_level.txt +0 -0
  25. {databar-2.0.1 → databar-2.0.3}/tests/test_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databar
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: Official Databar.ai Python SDK and CLI — connect to enrichments, waterfalls, and tables via api.databar.ai
5
5
  Author-email: "Databar.ai Team" <info@databar.ai>
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "databar"
7
- version = "2.0.1"
7
+ version = "2.0.3"
8
8
  description = "Official Databar.ai Python SDK and CLI — connect to enrichments, waterfalls, and tables via api.databar.ai"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -57,7 +57,7 @@ from .models import (
57
57
  WaterfallEnrichment,
58
58
  )
59
59
 
60
- __version__ = "2.0.1"
60
+ __version__ = "2.0.3"
61
61
  __all__ = [
62
62
  "DatabarClient",
63
63
  # exceptions
@@ -10,6 +10,7 @@ Key resolution order (same as MCP server):
10
10
  from __future__ import annotations
11
11
 
12
12
  import os
13
+ import shutil
13
14
  from pathlib import Path
14
15
 
15
16
  import typer
@@ -87,10 +88,23 @@ def login(
87
88
  success(f"API key saved to {CONFIG_FILE}")
88
89
  console.print("[dim]Tip: You can also set DATABAR_API_KEY as an environment variable.[/dim]")
89
90
 
91
+ # Check if the databar binary is on PATH and print a hint if not
92
+ if shutil.which("databar") is None:
93
+ import sys
94
+ bin_dir = Path(sys.executable).parent
95
+ console.print(
96
+ f"\n[yellow]Note:[/yellow] The [bold]databar[/bold] command is not on your PATH.\n"
97
+ f"Add this to your shell profile ([dim]~/.zshrc[/dim] or [dim]~/.bashrc[/dim]):\n\n"
98
+ f" [bold]export PATH=\"{bin_dir}:$PATH\"[/bold]\n\n"
99
+ f"Then restart your terminal, or run:\n\n"
100
+ f" [bold]source ~/.zshrc[/bold]\n\n"
101
+ f"Until then, use the full path: [bold]{bin_dir}/databar[/bold]"
102
+ )
103
+
90
104
 
91
105
  @app.command("whoami")
92
106
  def whoami(
93
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f", help="Output format.")
107
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f", help="Output format.")
94
108
  ) -> None:
95
109
  """Show current user info and credit balance."""
96
110
  client = get_client()
@@ -28,7 +28,7 @@ app = typer.Typer(help="Search and run data enrichments.")
28
28
  @app.command("list")
29
29
  def list_enrichments(
30
30
  query: Optional[str] = typer.Option(None, "--query", "-q", help="Search query."),
31
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
31
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
32
32
  ) -> None:
33
33
  """List available enrichments."""
34
34
  client = get_client()
@@ -59,7 +59,7 @@ def list_enrichments(
59
59
  @app.command("get")
60
60
  def get_enrichment(
61
61
  enrichment_id: int = typer.Argument(..., help="Enrichment ID."),
62
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
62
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
63
63
  ) -> None:
64
64
  """Get full details for an enrichment including parameters."""
65
65
  client = get_client()
@@ -99,11 +99,20 @@ def get_enrichment(
99
99
  output(rows, OutputFormat.TABLE, table_columns=["name", "type"])
100
100
 
101
101
 
102
+ @app.command("info", hidden=True)
103
+ def info_enrichment(
104
+ enrichment_id: int = typer.Argument(..., help="Enrichment ID."),
105
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
106
+ ) -> None:
107
+ """Alias for 'get'. Get full details for an enrichment including parameters."""
108
+ get_enrichment(enrichment_id, fmt)
109
+
110
+
102
111
  @app.command("run")
103
112
  def run_enrichment(
104
113
  enrichment_id: int = typer.Argument(..., help="Enrichment ID."),
105
114
  params_json: str = typer.Option(..., "--params", "-p", help='JSON params, e.g. \'{"email":"alice@example.com"}\''),
106
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
115
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
107
116
  raw: bool = typer.Option(False, "--raw", help="Print raw result without formatting."),
108
117
  ) -> None:
109
118
  """Run a single enrichment and wait for results."""
@@ -132,7 +141,7 @@ def run_enrichment(
132
141
  def bulk_enrichment(
133
142
  enrichment_id: int = typer.Argument(..., help="Enrichment ID."),
134
143
  input_file: Path = typer.Option(..., "--input", "-i", help="CSV file with one row per input.", exists=True),
135
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
144
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
136
145
  out: Optional[Path] = typer.Option(None, "--out", "-o", help="Output file (for CSV format)."),
137
146
  ) -> None:
138
147
  """Run a bulk enrichment from a CSV input file."""
@@ -159,7 +168,7 @@ def param_choices(
159
168
  query: Optional[str] = typer.Option(None, "--query", "-q", help="Filter choices."),
160
169
  page: int = typer.Option(1, "--page", help="Page number."),
161
170
  limit: int = typer.Option(50, "--limit", help="Results per page."),
162
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
171
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
163
172
  ) -> None:
164
173
  """List available choices for a select/mselect enrichment parameter."""
165
174
  client = get_client()
@@ -33,7 +33,7 @@ app = typer.Typer(help="Manage tables and rows.")
33
33
 
34
34
  @app.command("list")
35
35
  def list_tables(
36
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
36
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
37
37
  ) -> None:
38
38
  """List all tables in your workspace."""
39
39
  client = get_client()
@@ -59,7 +59,7 @@ def list_tables(
59
59
  def create_table(
60
60
  name: Optional[str] = typer.Option(None, "--name", "-n", help="Table name."),
61
61
  columns: Optional[str] = typer.Option(None, "--columns", "-c", help="Comma-separated column names."),
62
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
62
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
63
63
  ) -> None:
64
64
  """Create a new empty table."""
65
65
  col_list = [c.strip() for c in columns.split(",")] if columns else None
@@ -79,7 +79,7 @@ def create_table(
79
79
  @app.command("columns")
80
80
  def get_columns(
81
81
  table_uuid: str = typer.Argument(..., help="Table UUID."),
82
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
82
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
83
83
  ) -> None:
84
84
  """List columns defined on a table."""
85
85
  client = get_client()
@@ -106,7 +106,7 @@ def get_rows(
106
106
  table_uuid: str = typer.Argument(..., help="Table UUID."),
107
107
  page: int = typer.Option(1, "--page", help="Page number."),
108
108
  per_page: int = typer.Option(1000, "--per-page", help="Rows per page (max 1000)."),
109
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
109
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
110
110
  out: Optional[Path] = typer.Option(None, "--out", "-o", help="Output file (for CSV format)."),
111
111
  ) -> None:
112
112
  """Get rows from a table."""
@@ -143,7 +143,7 @@ def insert_rows(
143
143
  input_file: Optional[Path] = typer.Option(None, "--input", "-i", help="CSV file.", exists=True),
144
144
  allow_new_columns: bool = typer.Option(False, "--allow-new-columns", help="Auto-create unknown columns."),
145
145
  dedupe_keys: Optional[str] = typer.Option(None, "--dedupe-keys", help="Comma-separated column names for deduplication."),
146
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
146
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
147
147
  ) -> None:
148
148
  """Insert rows into a table."""
149
149
  raw_rows = _load_rows(data_json, input_file)
@@ -183,7 +183,7 @@ def patch_rows(
183
183
  data_json: Optional[str] = typer.Option(None, "--data", "-d", help='JSON array: [{"id":"<uuid>","fields":{...}}]'),
184
184
  input_file: Optional[Path] = typer.Option(None, "--input", "-i", help="CSV file (must have an 'id' column).", exists=True),
185
185
  no_overwrite: bool = typer.Option(False, "--no-overwrite", help="Only fill empty cells; keep existing values."),
186
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
186
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
187
187
  ) -> None:
188
188
  """Update existing rows by row UUID."""
189
189
  raw_rows = _load_rows(data_json, input_file)
@@ -219,7 +219,7 @@ def upsert_rows(
219
219
  key_col: str = typer.Option(..., "--key-col", "-k", help="Column name to match on (e.g. 'email')."),
220
220
  data_json: Optional[str] = typer.Option(None, "--data", "-d", help="JSON array of row objects."),
221
221
  input_file: Optional[Path] = typer.Option(None, "--input", "-i", help="CSV file.", exists=True),
222
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
222
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
223
223
  ) -> None:
224
224
  """Insert or update rows matched by a key column."""
225
225
  raw_rows = _load_rows(data_json, input_file)
@@ -253,7 +253,7 @@ def upsert_rows(
253
253
  @app.command("enrichments")
254
254
  def table_enrichments(
255
255
  table_uuid: str = typer.Argument(..., help="Table UUID."),
256
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
256
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
257
257
  ) -> None:
258
258
  """List enrichments configured on a table."""
259
259
  client = get_client()
@@ -277,7 +277,7 @@ def add_enrichment(
277
277
  table_uuid: str = typer.Argument(..., help="Table UUID."),
278
278
  enrichment_id: int = typer.Option(..., "--enrichment-id", "-e", help="Enrichment ID to add."),
279
279
  mapping_json: str = typer.Option(..., "--mapping", "-m", help='JSON mapping of param → column, e.g. \'{"email": "email_col"}\''),
280
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
280
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
281
281
  ) -> None:
282
282
  """Add an enrichment to a table with a column mapping."""
283
283
  try:
@@ -19,7 +19,7 @@ app = typer.Typer(help="Check the status of async tasks.")
19
19
  @app.command("get")
20
20
  def get_task(
21
21
  task_id: str = typer.Argument(..., help="Task ID returned by a run or bulk-run call."),
22
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
22
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
23
23
  poll: bool = typer.Option(
24
24
  False,
25
25
  "--poll",
@@ -27,7 +27,7 @@ app = typer.Typer(help="Search and run waterfall enrichments.")
27
27
  @app.command("list")
28
28
  def list_waterfalls(
29
29
  query: Optional[str] = typer.Option(None, "--query", "-q", help="Filter by name/description."),
30
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
30
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
31
31
  ) -> None:
32
32
  """List available waterfall enrichments."""
33
33
  client = get_client()
@@ -64,7 +64,7 @@ def list_waterfalls(
64
64
  @app.command("get")
65
65
  def get_waterfall(
66
66
  identifier: str = typer.Argument(..., help="Waterfall identifier."),
67
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
67
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
68
68
  ) -> None:
69
69
  """Get details for a specific waterfall."""
70
70
  client = get_client()
@@ -103,13 +103,22 @@ def get_waterfall(
103
103
  output(rows, OutputFormat.TABLE, table_columns=["id", "name", "price"])
104
104
 
105
105
 
106
+ @app.command("info", hidden=True)
107
+ def info_waterfall(
108
+ identifier: str = typer.Argument(..., help="Waterfall identifier."),
109
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
110
+ ) -> None:
111
+ """Alias for 'get'. Get details for a specific waterfall."""
112
+ get_waterfall(identifier, fmt)
113
+
114
+
106
115
  @app.command("run")
107
116
  def run_waterfall(
108
117
  identifier: str = typer.Argument(..., help="Waterfall identifier."),
109
118
  params_json: str = typer.Option(..., "--params", "-p", help='JSON params, e.g. \'{"linkedin_url":"https://..."}\''),
110
119
  providers: Optional[str] = typer.Option(None, "--providers", help="Comma-separated provider IDs (default: all)."),
111
120
  email_verifier: Optional[int] = typer.Option(None, "--email-verifier", help="Email verifier enrichment ID."),
112
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
121
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
113
122
  raw: bool = typer.Option(False, "--raw", help="Print raw result without formatting."),
114
123
  ) -> None:
115
124
  """Run a waterfall enrichment and wait for results."""
@@ -151,7 +160,7 @@ def bulk_waterfall(
151
160
  input_file: Path = typer.Option(..., "--input", "-i", help="CSV file with one row per input.", exists=True),
152
161
  providers: Optional[str] = typer.Option(None, "--providers", help="Comma-separated provider IDs (default: all)."),
153
162
  email_verifier: Optional[int] = typer.Option(None, "--email-verifier", help="Email verifier enrichment ID."),
154
- fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "-f"),
163
+ fmt: OutputFormat = typer.Option(OutputFormat.TABLE, "--format", "--output", "-f"),
155
164
  out: Optional[Path] = typer.Option(None, "--out", "-o", help="Output file (for CSV format)."),
156
165
  ) -> None:
157
166
  """Run a waterfall enrichment in bulk from a CSV input file."""
@@ -16,6 +16,7 @@ in databar_mcp/src/databar-client.ts.
16
16
  from __future__ import annotations
17
17
 
18
18
  import os
19
+ import re
19
20
  import time
20
21
  from typing import Any, Dict, List, Optional
21
22
 
@@ -438,10 +439,65 @@ class DatabarClient:
438
439
  table_uuid: str,
439
440
  enrichment_id: int,
440
441
  mapping: Dict[str, Any],
441
- ) -> Any:
442
- """Add an enrichment to a table with a parameter-to-column mapping."""
443
- payload = {"enrichment": enrichment_id, "mapping": mapping}
444
- return self._request("POST", f"/table/{table_uuid}/add-enrichment", json=payload)
442
+ ) -> TableEnrichment:
443
+ """
444
+ Add an enrichment to a table with a parameter-to-column mapping.
445
+
446
+ ``mapping`` keys are enrichment parameter names. Values are dicts with:
447
+ - ``{"type": "mapping", "value": "<column-name-or-uuid>"}``
448
+ — reads the value from a table column per row.
449
+ You may pass a human-readable column name; the SDK will automatically
450
+ resolve it to the required column UUID via GET /table/{uuid}/columns.
451
+ - ``{"type": "simple", "value": "<static-value>"}``
452
+ — uses the same hardcoded value for every row.
453
+
454
+ Returns the newly added :class:`TableEnrichment` (with ``id`` and ``name``),
455
+ resolved by diffing enrichments before and after the add.
456
+ """
457
+ # Auto-resolve column names → UUIDs for mapping-type entries
458
+ resolved_mapping: Dict[str, Any] = {}
459
+ column_map: Optional[Dict[str, str]] = None # name → uuid, built lazily
460
+
461
+ for param, entry in mapping.items():
462
+ if not isinstance(entry, dict) or entry.get("type") != "mapping":
463
+ resolved_mapping[param] = entry
464
+ continue
465
+
466
+ value = entry.get("value", "")
467
+ # Looks like a UUID already — 8-4-4-4-12 hex pattern
468
+ if re.fullmatch(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}", str(value), re.IGNORECASE):
469
+ resolved_mapping[param] = entry
470
+ continue
471
+
472
+ # Build the column name→uuid map once
473
+ if column_map is None:
474
+ columns = self.get_columns(table_uuid)
475
+ column_map = {c.name: c.identifier for c in columns}
476
+
477
+ uuid = column_map.get(value)
478
+ if uuid is None:
479
+ # Not a known column name — pass through and let the API surface the error
480
+ resolved_mapping[param] = entry
481
+ else:
482
+ resolved_mapping[param] = {**entry, "value": uuid}
483
+
484
+ # Snapshot existing enrichment IDs so we can detect the new one
485
+ before_ids = {e.id for e in self.get_table_enrichments(table_uuid)}
486
+
487
+ payload = {"enrichment": enrichment_id, "mapping": resolved_mapping}
488
+ self._request("POST", f"/table/{table_uuid}/add-enrichment", json=payload)
489
+
490
+ # Fetch updated list and return the newly created TableEnrichment
491
+ after = self.get_table_enrichments(table_uuid)
492
+ new_enrichments = [e for e in after if e.id not in before_ids]
493
+ if new_enrichments:
494
+ return new_enrichments[-1]
495
+
496
+ # Fallback: return the last enrichment in the list if we can't detect which is new
497
+ if after:
498
+ return after[-1]
499
+
500
+ raise DatabarError("Enrichment was added but could not be retrieved. Use get_table_enrichments() to fetch it manually.")
445
501
 
446
502
  def run_table_enrichment(
447
503
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: databar
3
- Version: 2.0.1
3
+ Version: 2.0.3
4
4
  Summary: Official Databar.ai Python SDK and CLI — connect to enrichments, waterfalls, and tables via api.databar.ai
5
5
  Author-email: "Databar.ai Team" <info@databar.ai>
6
6
  License: MIT License
@@ -329,4 +329,4 @@ def test_task_get_poll(monkeypatch):
329
329
  def test_version_flag():
330
330
  result = runner.invoke(app, ["--version"])
331
331
  assert result.exit_code == 0
332
- assert "2.0.1" in result.output
332
+ assert "2.0.2" in result.output
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes