Skip to content
This repository was archived by the owner on Apr 28, 2023. It is now read-only.

Commit dd11aaf

Browse files
authored
[MERGE] pull request #11 from mathix420/multicharts
multicharts
2 parents 9500c41 + 5456960 commit dd11aaf

File tree

6 files changed

+491
-165
lines changed

6 files changed

+491
-165
lines changed

README.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515

1616
## Usage
1717

18-
### For public notions
18+
### For public Notion pages
1919

2020
Simply use https://charts.mathix.ninja url, and follow the documentation below.
2121

22-
### For private notions
22+
### For private Notion pages
2323

2424
You'll need to host your own version of this repository.
2525
The best way to do that is by clicking this button below, it will automatically host this API on [vercel.com](https://vercel.com/) which is **100% free**.
@@ -29,7 +29,7 @@ The best way to do that is by clicking this button below, it will automatically
2929

3030
1. Click the blue **Deploy** button on this page
3131
2. Log in or sign up to continue.
32-
3. Choose a name for your project, keep in mind that this name will goes in your url `https://YOUR-PROJECT-NAME.now.sh`
32+
3. Choose a name for your project, keep in mind that this name will goes in your url `https://YOUR-PROJECT-NAME.vercel.app`
3333
4. Follow instructions and don't forget to put your notion `TOKEN_V2` before clicking **Continue** [If you don't know how to get it, click here](/docs/notion-token.md)
3434
5. Click **Continue** one more time, and you're done!
3535

@@ -42,6 +42,39 @@ Don't forget to use your URL instead of `charts.mathix.ninja`.
4242

4343
If you want to stay up to date I will recommend you to use a [Deploy Hook](https://vercel.com/docs/v2/more/deploy-hooks).
4444

45+
46+
## Examples
47+
48+
### Pokedex average values for each type
49+
50+
**Original:** [Notion Pokédex](https://www.notion.so/9201f4ac42814afdbcdbee910c919e3f?v=2eb8d4fb18184bfb8cc7cd7b8c5ef002)
51+
52+
**Columns:** `Primary Type:value | HP:avg | Attack:avg | Defense:avg | Speed:avg`
53+
54+
**Line chart:** https://notion-charts-git-multicharts-mathix420.vercel.app/schema-chart/9201f4ac42814afdbcdbee910c919e3f/2eb8d4fb18184bfb8cc7cd7b8c5ef002?s=Type%253A%29P%257CF%253Avalue%252CHP%253AHkft%253Aavg%252CAttack%253AB%252F%29Q%253Aavg%252CDefense%253Aq%252B%252BI%253Aavg%252CSpeed%253A8CLR%253Aavg&t=LineChart&dark
55+
56+
<details>
57+
<summary>See picture</summary>
58+
59+
![Line chart pokemon](https://i.imgur.com/26QAU5m.png)
60+
61+
</details>
62+
63+
### Pokedex normal types Candle-Stick chart
64+
65+
**Original:** [Normal Type Pokédex](https://www.notion.so/9201f4ac42814afdbcdbee910c919e3f?v=2eb8d4fb18184bfb8cc7cd7b8c5ef002)
66+
67+
**Columns:** `Name:value | HP:value | Attack:value | Defense:value | Speed:value`
68+
69+
**Candle-Stick chart:** https://notion-charts-git-multicharts-mathix420.vercel.app/schema-chart/2b5e6a6389e64f3298ab97005f4e6a35/9ef790d411504c70b437361169034b42?s=Name%253Atitle%253Avalue%252CPoints%253AHkft%253Avalue%252C%253AB%252F%29Q%253Avalue%252C%253Aq%252B%252BI%253Avalue%252C%253A8CLR%253Avalue&t=CandlestickChart
70+
71+
<details>
72+
<summary>See picture</summary>
73+
74+
![Candle-Stick chart pokemon](https://i.imgur.com/BaNfhQ9.png)
75+
76+
</details>
77+
4578
## Documentation
4679

4780
For fast and easy previews you can now [go directly here](https://charts.mathix.ninja).

main.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from werkzeug.exceptions import HTTPException
33
from requests.exceptions import HTTPError
44
from notion.client import NotionClient
5+
from itertools import groupby
56
from os import getenv
67
import json
78

@@ -80,6 +81,70 @@ def get_labels(request):
8081
return default_labels
8182

8283

84+
def flatten_row(row):
85+
res = []
86+
87+
for field, value in row.items():
88+
if value and (isinstance(value, list) or (isinstance(value, str) and ',' in value)):
89+
if not isinstance(value, list):
90+
value = value.split(',')
91+
for v in value:
92+
res += flatten_row({**row, field: v or 'EMPTY'})
93+
elif value == []:
94+
return flatten_row({**row, field: 'EMPTY'})
95+
96+
return res or [row]
97+
98+
99+
def clean_data(rows, fields):
100+
res = []
101+
rows = [{field: row.get_property(field)
102+
for field in fields} for row in rows]
103+
for row in rows:
104+
res += flatten_row(row)
105+
return res
106+
107+
108+
def get_datas(collection, view, columns_schema):
109+
if not columns_schema:
110+
raise Exception('Bad schema')
111+
columns_schema = list(map(lambda x: x.split(':'), columns_schema))
112+
113+
try:
114+
cv = get_client().get_collection_view(URL_BASE.format(collection, view))
115+
except Exception as e:
116+
if str(e) == 'Invalid collection view URL':
117+
raise Exception('Bad view')
118+
raise
119+
120+
rows = cv.default_query().execute()
121+
datas = [list(map(lambda x: x[0], columns_schema))]
122+
123+
flatten = clean_data(rows, set(map(lambda x: x[1], columns_schema)))
124+
grouped = groupby(sorted(flatten, key=lambda x: x[columns_schema[0][1]]), lambda x: x[columns_schema[0][1]])
125+
126+
for key, group in grouped:
127+
datas.append([[]] * len(columns_schema))
128+
group = list(group)
129+
datas[-1][0] = key
130+
131+
for i, schema in enumerate(columns_schema):
132+
_, field, action = schema
133+
134+
if action == 'count':
135+
datas[-1][i] = len(list(filter(lambda x: x[field], group)))
136+
elif action == 'sum':
137+
datas[-1][i] = sum(map(lambda x: int(x[field]), group))
138+
elif action == 'avg':
139+
lst = list(map(lambda x: int(x[field]), group))
140+
datas[-1][i] = sum(lst) / len(lst)
141+
else:
142+
values = set(map(lambda x: x[field], group))
143+
datas[-1][i] = ','.join(map(str, values)) if len(values) > 1 else (list(values) or [''])[0]
144+
145+
return cv, datas
146+
147+
83148
@app.errorhandler(Exception)
84149
def handle_error(e):
85150
code = 500
@@ -88,6 +153,76 @@ def handle_error(e):
88153
return jsonify(error=str(e)), code
89154

90155

156+
TYPES_EXCLUDED = ['relation', 'person', 'date']
157+
158+
159+
@app.route('/notion-schema/<collection>/<view>')
160+
def get_schema(collection, view):
161+
try:
162+
cv = get_client().get_collection_view(URL_BASE.format(collection, view))
163+
except Exception as e:
164+
if str(e) == 'Invalid collection view URL':
165+
raise Exception('Bad view')
166+
raise
167+
168+
rows = cv.default_query().execute()
169+
170+
return jsonify({
171+
'columns': list(filter(lambda x: x['type'] not in TYPES_EXCLUDED, rows[0].schema))
172+
}), 200
173+
174+
175+
@app.route('/schema-chart/<collection>/<view>')
176+
def build_schema_chart(collection, view):
177+
dark_mode = 'dark' in request.args
178+
chart_type = request.args.get('t', 'PieChart')
179+
columns_schema = request.args.get('s', '').split(',')
180+
181+
cv, datas = get_datas(collection, view, columns_schema)
182+
183+
return render_template(
184+
'schema.html',
185+
dark_mode=dark_mode,
186+
chart_type=chart_type,
187+
datas=json.dumps(datas),
188+
title=request.args.get('title', cv.name),
189+
)
190+
191+
192+
@app.route('/image-chart/<collection>/<view>')
193+
def get_chart_image_v2(collection, view):
194+
chart_type = request.args.get('t', 'PieChart')
195+
columns_schema = request.args.get('s', '').split(',')
196+
197+
cv, datas = get_datas(collection, view, columns_schema)
198+
199+
labels = list(map(lambda x: remove_non_ascii(x[0]), datas[1:]))
200+
datasets = []
201+
202+
nb_datasets = len(datas[0])
203+
204+
for index in range(1, nb_datasets):
205+
datasets.append({
206+
'label': datas[0][index],
207+
'data': list(map(lambda x: x[index], datas[1:]))
208+
})
209+
210+
data = {
211+
'type': chart_type.lower().replace('chart', ''),
212+
'data': {
213+
'labels': labels,
214+
'borderWidth': 0,
215+
'datasets': datasets
216+
},
217+
'options': {
218+
'plugins': {'outlabels': {'text': ''}},
219+
'rotation': 0,
220+
}
221+
}
222+
223+
return redirect(CHART_URL + json.dumps(data))
224+
225+
91226
@app.route('/chart-image/<collection>/<view>')
92227
def get_chart_image(collection, view):
93228
labels = get_labels(request)

0 commit comments

Comments
 (0)