quillsql 2.0__py3-none-any.whl → 2.0.1__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.
quillsql/core.py CHANGED
@@ -1,14 +1,18 @@
1
+ import os
2
+ import json
3
+
1
4
  import requests
2
5
  import psycopg2
3
- import psycopg2.extras
4
- import json
5
6
  import redis
6
7
 
7
8
  from psycopg2.extensions import make_dsn
8
9
 
10
+
9
11
  ## The host url of the Quill metadata server
10
- HOST = "https://quill-344421.uc.r.appspot.com" # or "http://localhost:8080"
11
- # HOST = "http://localhost:8080"
12
+ ENV = os.environ.get("PYTHON_ENV")
13
+ DEV_HOST = "http://localhost:8080"
14
+ PROD_HOST = "https://quill-344421.uc.r.appspot.com"
15
+ HOST = DEV_HOST if ENV == "development" else PROD_HOST
12
16
 
13
17
 
14
18
  ## The TTL for new cache entries (default: 1h)
@@ -24,7 +28,7 @@ class CachedPool:
24
28
  self.pool = psycopg2.connect(config)
25
29
  self.cache = self.get_cache(cache_config)
26
30
  self.ttl = cache_config and cache_config.get("ttl") or DEFAULT_CACHE_TTL
27
- self.cur = self.pool.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
31
+ self.cur = self.pool.cursor()
28
32
  self.orgId = None
29
33
 
30
34
  def get_cache(self, cache_config):
@@ -44,7 +48,7 @@ class CachedPool:
44
48
  def exec(self, sql):
45
49
  self.cur.execute(sql)
46
50
  rows = self.cur.fetchall()
47
- return [json.loads(json.dumps(row, default=str)) for row in rows]
51
+ return [self._build_dict(self.cur, row) for row in rows]
48
52
 
49
53
  def query(self, sql):
50
54
  if not self.cache:
@@ -59,6 +63,13 @@ class CachedPool:
59
63
  new_result_string = json.dumps(new_result)
60
64
  self.cache.set(key, new_result_string, "EX", DEFAULT_CACHE_TTL)
61
65
  return new_result
66
+
67
+ # Parses the row from a tuple to a dict using the cursor description.
68
+ def _build_dict(self, cursor, row):
69
+ dict_row = {}
70
+ for key, col in enumerate(cursor.description):
71
+ dict_row[col[0]] = row[key]
72
+ return json.loads(json.dumps(dict_row, default=str))
62
73
 
63
74
 
64
75
  ## handles a query task
@@ -0,0 +1,54 @@
1
+ Metadata-Version: 2.1
2
+ Name: quillsql
3
+ Version: 2.0.1
4
+ Summary: Quill SDK for Python.
5
+ Home-page: https://github.com/quill-sql/quill-python
6
+ Author: Quill
7
+ Author-email: shawn@quillsql.com
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: psycopg2-binary
11
+ Requires-Dist: requests
12
+ Requires-Dist: redis
13
+ Requires-Dist: python-dotenv
14
+ Requires-Dist: pytest
15
+
16
+ # Quill Python SDK
17
+ [![Quill SDK](https://github.com/quill-sql/quill-python/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/quill-sql/quill-python/actions/workflows/ci.yaml)
18
+
19
+ ## Quickstart
20
+
21
+ First, install the quillsql package by running:
22
+
23
+ ```bash
24
+ $ pip install quillsql
25
+ ```
26
+
27
+ Then, add a `/quill` endpoint to your existing python server. For example, if
28
+ you were running a FASTAPI app, you would just add the endpoint like this:
29
+
30
+ ```python
31
+ from quillsql import Quill
32
+ from fastapi import FastAPI, Request
33
+
34
+ app = FastAPI()
35
+
36
+ quill = Quill(
37
+ private_key=<YOUR_PRIVATE_KEY_HERE>,
38
+ database_connection_string=<YOUR_DB_CONNECTION_STRING_HERE>,
39
+ )
40
+
41
+ # ... your existing endpoints here ...
42
+
43
+ @app.post("/quill")
44
+ async def quill_post(data: Request):
45
+ body = await data.json()
46
+ return quill.query(org_id="2", data=body)
47
+ ```
48
+
49
+ Then you can run your app like normally. Pass in this route to our react library
50
+ on the frontend and you all set!
51
+
52
+
53
+ ## Questions
54
+ If you have any questions, please reach out to us!
@@ -0,0 +1,11 @@
1
+ quillsql/__init__.py,sha256=IgJnPRLx5GcenVt82mMQo0ydvXYB_-39Ft4Of5sEEGA,39
2
+ quillsql/core.py,sha256=WkVwPfbhH_b3mdWGP3rYRNk1wFAVOo8DFlJwnrlLozA,12428
3
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ tests/cache_test.py,sha256=5MvAAyGF37wiRejOwG8m4uG9jxnEOseV5GtXTtjAk-8,11092
5
+ tests/external_test.py,sha256=pTHIiWMA0JFTapdHzoNR7ud1uCXw1r8P0kBbVLWxaXk,7466
6
+ tests/simple_test.py,sha256=DTGOFHvljEJs6akVIrDwLr9shYIP68t7-JrF_d17O2Y,7378
7
+ quillsql-2.0.1.dist-info/LICENSE,sha256=f2-T2fNvArbqfNW8RwsOu2IWEwpsaRX6G-AJvLJKuzg,1068
8
+ quillsql-2.0.1.dist-info/METADATA,sha256=IeQgdsc33ygynQnGt75syOXbxIAtkSjtmmbMAtctYzA,1395
9
+ quillsql-2.0.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
10
+ quillsql-2.0.1.dist-info/top_level.txt,sha256=DCsUgdO5twxI1s5sw-euqMt1NDDtIGMR-t9A68P3gZs,15
11
+ quillsql-2.0.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: bdist_wheel (0.42.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,35 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: quillsql
3
- Version: 2.0
4
- Summary: Quill SDK for Python.
5
- Home-page: https://github.com/quill-sql/quill-python
6
- Author: Quill
7
- Author-email: shawn@quillsql.com
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- Requires-Dist: psycopg2-binary
11
- Requires-Dist: requests
12
- Requires-Dist: redis
13
- Requires-Dist: python-dotenv
14
- Requires-Dist: pytest
15
-
16
- Quill SDK for Python
17
-
18
-
19
- ## Quickstart
20
-
21
- Install dependencies
22
- ```bash
23
- $ pip install .
24
- ```
25
-
26
- Run unit tests
27
- ```bash
28
- $ pytest
29
- ```
30
-
31
- ## Troubleshooting
32
-
33
- ```
34
- $ python3 -m pip install --upgrade setuptools
35
- ```
@@ -1,12 +0,0 @@
1
- quillsql/__init__.py,sha256=IgJnPRLx5GcenVt82mMQo0ydvXYB_-39Ft4Of5sEEGA,39
2
- quillsql/core.py,sha256=dl6ry6_C6NUZKlU4gVaneD8zwEvdndcYLTEn_XgaDQk,12129
3
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- tests/cache_test.py,sha256=5MvAAyGF37wiRejOwG8m4uG9jxnEOseV5GtXTtjAk-8,11092
5
- tests/external_test.py,sha256=pTHIiWMA0JFTapdHzoNR7ud1uCXw1r8P0kBbVLWxaXk,7466
6
- tests/simple_test.py,sha256=DTGOFHvljEJs6akVIrDwLr9shYIP68t7-JrF_d17O2Y,7378
7
- tests/staging_test.py,sha256=zdDV6BvJRyNKCy80bxDNfK1F1oqM6oIv7ul7JPKi51Q,4962
8
- quillsql-2.0.dist-info/LICENSE,sha256=f2-T2fNvArbqfNW8RwsOu2IWEwpsaRX6G-AJvLJKuzg,1068
9
- quillsql-2.0.dist-info/METADATA,sha256=Atl3xFsavnxO6DWo4BaeJTLo37rtHffvC4csuVldGFA,570
10
- quillsql-2.0.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
11
- quillsql-2.0.dist-info/top_level.txt,sha256=DCsUgdO5twxI1s5sw-euqMt1NDDtIGMR-t9A68P3gZs,15
12
- quillsql-2.0.dist-info/RECORD,,
tests/staging_test.py DELETED
@@ -1,168 +0,0 @@
1
- import pytest
2
- from quillsql import Quill
3
- import os
4
- from dotenv import load_dotenv
5
-
6
- load_dotenv()
7
-
8
-
9
- quill = Quill(
10
- private_key=os.environ.get("QUILL_PRIVATE_KEY"),
11
- database_connection_string=os.environ.get("POSTGRES_STAGING_READ"),
12
- )
13
-
14
-
15
- def test_config_gets_metadata():
16
- res = quill.query(
17
- org_id="2", data={"metadata": {"task": "config", "name": "spend"}}
18
- )
19
-
20
- assert res is not None
21
- assert "sections" in res
22
- assert "newQueries" in res
23
- assert "filters" in res
24
- assert res["filters"] == []
25
- assert "fieldToRemove" in res
26
- assert res["fieldToRemove"] == "customer_id"
27
-
28
- for query in res["newQueries"]:
29
- assert "columns" in query
30
- assert any(
31
- column["field"] == "spend"
32
- and column["label"] == "spend"
33
- and column["format"] == "dollar_amount"
34
- for column in query["columns"]
35
- )
36
- assert any(
37
- column["field"] == "month"
38
- and column["label"] == "month"
39
- and column["format"] == "MMM_yyyy"
40
- for column in query["columns"]
41
- )
42
-
43
-
44
- def test_handles_empty_client_id():
45
- res = quill.query(
46
- org_id="2",
47
- data={
48
- "metadata": {
49
- "task": "item",
50
- "id": "6580d3aea2caa9000b1c1b06",
51
- "client_id": None,
52
- "filters": [],
53
- "query": "select * from transactions",
54
- }
55
- },
56
- )
57
-
58
- assert res["chartType"] == "line"
59
- assert res["clientId"] == "62cda15d7c9fcca7bc0a3689"
60
- assert res["name"] == "Total Spend Test"
61
- assert res["dashboardName"] == "spend"
62
-
63
-
64
- def test_gets_item():
65
- res = quill.query(
66
- org_id="2",
67
- data={
68
- "metadata": {
69
- "task": "item",
70
- "id": "6580d3aea2caa9000b1c1b06",
71
- "client_id": "62cda15d7c9fcca7bc0a3689",
72
- "filters": [],
73
- "query": "select * from transactions",
74
- }
75
- },
76
- )
77
-
78
- assert res["chartType"] == "line"
79
- assert res["clientId"] == "62cda15d7c9fcca7bc0a3689"
80
- assert res["name"] == "Total Spend Test"
81
- assert res["dashboardName"] == "spend"
82
-
83
-
84
- def test_query_for_data():
85
- res = quill.query(
86
- org_id="2",
87
- data={
88
- "metadata": {
89
- "task": "query",
90
- "id": "6580d48f457d7b000b7bee2c",
91
- "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
92
- }
93
- },
94
- )
95
-
96
- assert "rows" in res
97
- assert "fields" in res
98
-
99
-
100
- def test_creates_a_chart():
101
- res = quill.query(
102
- org_id="2",
103
- data={
104
- "metadata": {
105
- "task": "create",
106
- "dateField": {"table": "transactions", "field": "created_at"},
107
- "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
108
- "name": "Total Spend Test",
109
- "clientId": "62cda15d7c9fcca7bc0a3689",
110
- "customerId": "2",
111
- "xAxisField": "month",
112
- "xAxisLabel": "month",
113
- "yAxisFields": [
114
- {"field": "spend", "label": "spend", "format": "dollar_amount"}
115
- ],
116
- "yAxisLabel": "spend",
117
- "chartType": "line",
118
- "dashboardName": "spend",
119
- "xAxisFormat": "MMM_yyyy",
120
- "columns": [
121
- {"field": "spend", "label": "spend", "format": "dollar_amount"},
122
- {"field": "month", "label": "month", "format": "MMM_yyyy"},
123
- ],
124
- }
125
- },
126
- )
127
-
128
- assert res is not None
129
-
130
-
131
- def test_returns_different_configs_for_different_org_ids():
132
- data = {
133
- "metadata": {
134
- "task": "config",
135
- "name": "spend",
136
- }
137
- }
138
- res1 = quill.query(org_id="1", data=data)
139
- res2 = quill.query(org_id="2", data=data)
140
- assert res1 != res2
141
-
142
-
143
- def test_returns_different_items_for_different_org_ids():
144
- data = {
145
- "metadata": {
146
- "task": "item",
147
- "id": "6580d3aea2caa9000b1c1b06",
148
- "filters": [],
149
- "client_id": "62cda15d7c9fcca7bc0a3689",
150
- "query": "select * from transactions",
151
- }
152
- }
153
- res1 = quill.query(org_id="1", data=data)
154
- res2 = quill.query(org_id="2", data=data)
155
- assert res1 != res2
156
-
157
-
158
- def test_returns_different_query_data_for_different_org_ids():
159
- data = {
160
- "metadata": {
161
- "task": "query",
162
- "id": "6580d48f457d7b000b7bee2c",
163
- "query": "select sum(amount) as spend, date_trunc('month', created_at) as month from transactions group by month order by max(created_at);",
164
- }
165
- }
166
- res1 = quill.query(org_id="1", data=data)
167
- res2 = quill.query(org_id="2", data=data)
168
- assert res1 != res2