bittensor-cli 9.1.0__py3-none-any.whl → 9.1.2__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.
@@ -1,5 +1,8 @@
1
1
  import asyncio
2
2
  import json
3
+ import os
4
+ import tempfile
5
+ import webbrowser
3
6
  import netaddr
4
7
  from dataclasses import asdict, is_dataclass
5
8
  from typing import Any, Dict, List
@@ -7,8 +10,9 @@ from pywry import PyWry
7
10
 
8
11
  from bittensor_cli.src.bittensor.balances import Balance
9
12
  from bittensor_cli.src.bittensor.subtensor_interface import SubtensorInterface
10
- from bittensor_cli.src.bittensor.utils import console
13
+ from bittensor_cli.src.bittensor.utils import console, WalletLike
11
14
  from bittensor_wallet import Wallet
15
+ from bittensor_cli.src import defaults
12
16
 
13
17
  root_symbol_html = f"&#x{ord('τ'):X};"
14
18
 
@@ -29,42 +33,80 @@ class Encoder(json.JSONEncoder):
29
33
  async def display_network_dashboard(
30
34
  wallet: Wallet,
31
35
  subtensor: "SubtensorInterface",
32
- prompt: bool = True,
36
+ use_wry: bool = False,
37
+ save_file: bool = False,
38
+ dashboard_path: str = None,
39
+ coldkey_ss58: str = None,
33
40
  ) -> bool:
34
41
  """
35
42
  Generate and display the HTML interface.
36
43
  """
44
+ if coldkey_ss58:
45
+ wallet = WalletLike(coldkeypub_ss58=coldkey_ss58, name=coldkey_ss58[:7])
37
46
  try:
38
47
  with console.status("[dark_sea_green3]Fetching data...", spinner="earth"):
39
48
  _subnet_data = await fetch_subnet_data(wallet, subtensor)
40
49
  subnet_data = process_subnet_data(_subnet_data)
41
50
  html_content = generate_full_page(subnet_data)
42
51
 
43
- console.print(
44
- "[dark_sea_green3]Opening dashboard in a window. Press Ctrl+C to close.[/dark_sea_green3]"
45
- )
46
- window = PyWry()
47
- window.send_html(
48
- html=html_content,
49
- title="Bittensor View",
50
- width=1200,
51
- height=800,
52
- )
53
- window.start()
54
- await asyncio.sleep(10)
55
- try:
56
- while True:
57
- if _has_exited(window):
58
- break
59
- await asyncio.sleep(1)
60
- except KeyboardInterrupt:
61
- console.print("\n[yellow]Closing Bittensor View...[/yellow]")
62
- finally:
63
- if not _has_exited(window):
64
- try:
65
- window.close()
66
- except Exception:
67
- pass
52
+ if use_wry:
53
+ console.print(
54
+ "[dark_sea_green3]Opening dashboard in a window. Press Ctrl+C to close.[/dark_sea_green3]"
55
+ )
56
+ window = PyWry()
57
+ window.send_html(
58
+ html=html_content,
59
+ title="Bittensor View",
60
+ width=1200,
61
+ height=800,
62
+ )
63
+ window.start()
64
+ await asyncio.sleep(10)
65
+ try:
66
+ while True:
67
+ if _has_exited(window):
68
+ break
69
+ await asyncio.sleep(1)
70
+ except KeyboardInterrupt:
71
+ console.print("\n[yellow]Closing Bittensor View...[/yellow]")
72
+ finally:
73
+ if not _has_exited(window):
74
+ try:
75
+ window.close()
76
+ except Exception:
77
+ pass
78
+ else:
79
+ if save_file:
80
+ dir_path = os.path.expanduser(dashboard_path)
81
+ else:
82
+ dir_path = os.path.expanduser(defaults.dashboard.path)
83
+ if not os.path.exists(dir_path):
84
+ os.makedirs(dir_path)
85
+
86
+ with tempfile.NamedTemporaryFile(
87
+ delete=not save_file,
88
+ suffix=".html",
89
+ mode="w",
90
+ dir=dir_path,
91
+ prefix=f"{wallet.name}_{subnet_data['block_number']}_",
92
+ ) as f:
93
+ f.write(html_content)
94
+ temp_path = f.name
95
+ file_url = f"file://{os.path.abspath(temp_path)}"
96
+
97
+ if not save_file:
98
+ with console.status(
99
+ "[dark_sea_green3]Loading dashboard...[/dark_sea_green3]",
100
+ spinner="material",
101
+ ):
102
+ webbrowser.open(file_url)
103
+ await asyncio.sleep(10)
104
+ return True
105
+
106
+ console.print("[green]Dashboard View opened in your browser[/green]")
107
+ console.print(f"[yellow]The HTML file is saved at: {temp_path}[/yellow]")
108
+ webbrowser.open(file_url)
109
+ return True
68
110
 
69
111
  except Exception as e:
70
112
  print(f"Error: {e}")
@@ -76,21 +118,33 @@ def int_to_ip(int_val: int) -> str:
76
118
  return str(netaddr.IPAddress(int_val))
77
119
 
78
120
 
79
- def get_hotkey_identity(
121
+ def get_identity(
80
122
  hotkey_ss58: str,
81
123
  identities: dict,
82
124
  old_identities: dict,
83
125
  trucate_length: int = 4,
126
+ return_bool: bool = False,
127
+ lookup_hk: bool = True,
84
128
  ) -> str:
85
129
  """Fetch identity of hotkey from both sources"""
86
- if hk_identity := identities["hotkeys"].get(hotkey_ss58):
87
- return hk_identity.get("identity", {}).get("name", "") or hk_identity.get(
88
- "display", "~"
89
- )
90
- elif old_identity := old_identities.get(hotkey_ss58):
130
+ if lookup_hk:
131
+ if hk_identity := identities["hotkeys"].get(hotkey_ss58):
132
+ return hk_identity.get("identity", {}).get("name", "") or hk_identity.get(
133
+ "display", "~"
134
+ )
135
+ else:
136
+ if ck_identity := identities["coldkeys"].get(hotkey_ss58):
137
+ return ck_identity.get("identity", {}).get("name", "") or ck_identity.get(
138
+ "display", "~"
139
+ )
140
+
141
+ if old_identity := old_identities.get(hotkey_ss58):
91
142
  return old_identity.display
92
143
  else:
93
- return f"{hotkey_ss58[:trucate_length]}...{hotkey_ss58[-trucate_length:]}"
144
+ if return_bool:
145
+ return False
146
+ else:
147
+ return f"{hotkey_ss58[:trucate_length]}...{hotkey_ss58[-trucate_length:]}"
94
148
 
95
149
 
96
150
  async def fetch_subnet_data(
@@ -164,7 +218,7 @@ def process_subnet_data(raw_data: Dict[str, Any]) -> Dict[str, Any]:
164
218
  stake_dict.setdefault(stake.netuid, []).append(
165
219
  {
166
220
  "hotkey": stake.hotkey_ss58,
167
- "hotkey_identity": get_hotkey_identity(
221
+ "hotkey_identity": get_identity(
168
222
  stake.hotkey_ss58, ck_hk_identities, old_identities
169
223
  ),
170
224
  "amount": stake.stake.tao,
@@ -228,7 +282,7 @@ def process_subnet_data(raw_data: Dict[str, Any]) -> Dict[str, Any]:
228
282
 
229
283
  # Add identities
230
284
  for hotkey in meta_info.hotkeys:
231
- identity = get_hotkey_identity(
285
+ identity = get_identity(
232
286
  hotkey, ck_hk_identities, old_identities, trucate_length=2
233
287
  )
234
288
  metagraph_info["updated_identities"].append(identity)
@@ -278,9 +332,22 @@ def process_subnet_data(raw_data: Dict[str, Any]) -> Dict[str, Any]:
278
332
  }
279
333
  )
280
334
  subnets.sort(key=lambda x: x["market_cap"], reverse=True)
335
+
336
+ wallet_identity = get_identity(
337
+ wallet.coldkeypub.ss58_address,
338
+ ck_hk_identities,
339
+ old_identities,
340
+ return_bool=True,
341
+ lookup_hk=False,
342
+ )
343
+ if not wallet_identity:
344
+ wallet_identity = wallet.name
345
+ else:
346
+ wallet_identity = f"{wallet_identity} ({wallet.name})"
347
+
281
348
  return {
282
349
  "wallet_info": {
283
- "name": wallet.name,
350
+ "name": wallet_identity,
284
351
  "balance": balance.tao,
285
352
  "coldkey": wallet.coldkeypub.ss58_address,
286
353
  "total_ideal_stake_value": total_ideal_stake_value.tao,
@@ -319,6 +386,7 @@ def generate_full_page(data: Dict[str, Any]) -> str:
319
386
  <!DOCTYPE html>
320
387
  <html>
321
388
  <head>
389
+ <meta charset="UTF-8">
322
390
  <title>Bittensor CLI Interface</title>
323
391
  <style>
324
392
  {get_css_styles()}
@@ -617,6 +685,7 @@ def generate_main_header(wallet_info: Dict[str, Any], block_number: int) -> str:
617
685
 
618
686
  return f"""
619
687
  <div class="header">
688
+ <meta charset="UTF-8">
620
689
  <div class="wallet-info">
621
690
  <span class="wallet-name">{wallet_info["name"]}</span>
622
691
  <div class="wallet-address-container" onclick="copyToClipboard('{wallet_info["coldkey"]}', this)">
@@ -14,7 +14,6 @@ from rich.align import Align
14
14
  from rich.table import Column, Table
15
15
  from rich.tree import Tree
16
16
  from rich.padding import Padding
17
- import typer
18
17
 
19
18
  from bittensor_cli.src import COLOR_PALETTE
20
19
  from bittensor_cli.src.bittensor import utils
@@ -120,7 +119,7 @@ async def regen_hotkey(
120
119
  if json_path:
121
120
  if not os.path.exists(json_path) or not os.path.isfile(json_path):
122
121
  err_console.print(f"File {json_path} does not exist")
123
- raise typer.Exit()
122
+ return False
124
123
  with open(json_path, "r") as f:
125
124
  json_str = f.read()
126
125
 
@@ -1362,15 +1361,6 @@ async def set_id(
1362
1361
  "github_repo": github_repo.encode(),
1363
1362
  }
1364
1363
 
1365
- for field, value in identity_data.items():
1366
- max_size = 64 # bytes
1367
- if len(value) > max_size:
1368
- err_console.print(
1369
- f"[red]Error:[/red] Identity field [white]{field}[/white] must be <= {max_size} bytes.\n"
1370
- f"Value '{value.decode()}' is {len(value)} bytes."
1371
- )
1372
- return False
1373
-
1374
1364
  if not unlock_key(wallet).success:
1375
1365
  return False
1376
1366
 
bittensor_cli/version.py CHANGED
@@ -15,5 +15,5 @@ def version_as_int(version):
15
15
  __new_signature_version__ = 360
16
16
  return __version_as_int__
17
17
 
18
- __version__ = "9.1.0"
18
+ __version__ = "9.1.2"
19
19
  __version_as_int__ = version_as_int(__version__)
@@ -1,31 +1,15 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bittensor-cli
3
- Version: 9.1.0
3
+ Version: 9.1.2
4
4
  Summary: Bittensor CLI
5
- Home-page: https://github.com/opentensor/btcli
6
5
  Author: bittensor.com
7
- Author-email:
8
- License: MIT
9
- Classifier: Development Status :: 5 - Production/Stable
10
- Classifier: Intended Audience :: Developers
11
- Classifier: Topic :: Software Development :: Build Tools
12
- Classifier: License :: OSI Approved :: MIT License
13
- Classifier: Programming Language :: Python :: 3 :: Only
14
- Classifier: Programming Language :: Python :: 3.9
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Classifier: Topic :: Scientific/Engineering
19
- Classifier: Topic :: Scientific/Engineering :: Mathematics
20
- Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
- Classifier: Topic :: Software Development
22
- Classifier: Topic :: Software Development :: Libraries
23
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
- Requires-Python: >=3.9
6
+ Project-URL: homepage, https://github.com/opentensor/btcli
7
+ Project-URL: Repository, https://github.com/opentensor/btcli
8
+ Requires-Python: <3.13,>=3.9
25
9
  Description-Content-Type: text/markdown
26
10
  Requires-Dist: wheel
27
11
  Requires-Dist: async-property==0.2.2
28
- Requires-Dist: async-substrate-interface>=1.0.3
12
+ Requires-Dist: async-substrate-interface>=1.0.5
29
13
  Requires-Dist: aiohttp~=3.10.2
30
14
  Requires-Dist: backoff~=2.2.1
31
15
  Requires-Dist: GitPython>=3.0.0
@@ -33,7 +17,7 @@ Requires-Dist: fuzzywuzzy~=0.18.0
33
17
  Requires-Dist: netaddr~=1.3.0
34
18
  Requires-Dist: numpy>=2.0.1
35
19
  Requires-Dist: Jinja2
36
- Requires-Dist: pycryptodome
20
+ Requires-Dist: pycryptodome<4.0.0,>=3.0.0
37
21
  Requires-Dist: PyYAML~=6.0.1
38
22
  Requires-Dist: pytest
39
23
  Requires-Dist: python-Levenshtein
@@ -42,12 +26,12 @@ Requires-Dist: scalecodec==1.2.11
42
26
  Requires-Dist: typer~=0.12
43
27
  Requires-Dist: websockets>=14.1
44
28
  Requires-Dist: bittensor-wallet>=3.0.4
45
- Requires-Dist: plotille
46
- Requires-Dist: pywry
47
- Requires-Dist: plotly
29
+ Requires-Dist: plotille>=5.0.0
30
+ Requires-Dist: pywry>=0.6.2
31
+ Requires-Dist: plotly>=6.0.0
48
32
  Provides-Extra: cuda
33
+ Requires-Dist: torch<2.6.0,>=1.13.1; extra == "cuda"
49
34
  Requires-Dist: cubit>=1.1.0; extra == "cuda"
50
- Requires-Dist: torch; extra == "cuda"
51
35
 
52
36
  <div align="center">
53
37
 
@@ -1,36 +1,35 @@
1
1
  bittensor_cli/__init__.py,sha256=Lpv4NkbAQgwrfqFOnTMuR_S-fqGdaWCSLhxnFnGTHM0,1232
2
- bittensor_cli/cli.py,sha256=bqGrWcqcm8C347v89pwYN8RxMsJANg6DvosUTWqtKsE,195266
2
+ bittensor_cli/cli.py,sha256=b74SuigzFMoGItVfdkpWoIOktTBLx1ALc-oHb7nprvs,198453
3
3
  bittensor_cli/doc_generation_helper.py,sha256=GexqjEIKulWg84hpNBEchJ840oOgOi7DWpt447nsdNI,91
4
- bittensor_cli/version.py,sha256=CNkEZiT2D-9y52rGJS2OIcNA9TJ-Nr5-2zIbPQhQ7-I,622
5
- bittensor_cli/src/__init__.py,sha256=R21KfHmy4IfVfxdTSRt_j1NeRTtJkm4cCuZUz3xaDMs,26490
4
+ bittensor_cli/version.py,sha256=3RfWXsWszr62jh5wKR5mVQE7z-U3vsAuhEbQzn3kiUg,622
5
+ bittensor_cli/src/__init__.py,sha256=9JLmK4Z-auboWXX74sMkJiFSvcjw3jcRUUw5lRRABy8,26553
6
6
  bittensor_cli/src/bittensor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  bittensor_cli/src/bittensor/balances.py,sha256=q5KkxF8wmUguWAFddEKstfDKTxPe5ISHpT6br8x32rc,11148
8
8
  bittensor_cli/src/bittensor/chain_data.py,sha256=hG61nRpp_4A1NkmEQcbmL89Z1UqL1IF1F9om896Pp-g,41616
9
9
  bittensor_cli/src/bittensor/minigraph.py,sha256=BIzmSVLfBYiRAeGD_i1LAC8Cw7zxp38a91SIFEPMqYc,10479
10
10
  bittensor_cli/src/bittensor/networking.py,sha256=pZLMs8YXpZzDMLXWMBb_Bj6TVkm_q9khyY-lnbwVMuE,462
11
- bittensor_cli/src/bittensor/subtensor_interface.py,sha256=iSORcGNR8OFzVc-ouDfSITPU0qqE2JovHFOJ8Tx9TZI,54491
12
- bittensor_cli/src/bittensor/utils.py,sha256=HbtLcHNScqRy-pqwuNu-aZZwPFfOQINUHPw0Ps585j4,46805
11
+ bittensor_cli/src/bittensor/subtensor_interface.py,sha256=tOhQEkjO03J7YJNYdUOmmH333SR5wNybmgYNZ7aoygk,54507
12
+ bittensor_cli/src/bittensor/utils.py,sha256=z3KsDpOVgL1O16aXYJWDspaK1UFgei-eXxX2biZZ98s,47539
13
13
  bittensor_cli/src/bittensor/extrinsics/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
14
  bittensor_cli/src/bittensor/extrinsics/registration.py,sha256=3mJZ3hw_wZEa-8I0R8WVuKjMQi4Y9EV5FjTCvbY37Iw,63780
15
15
  bittensor_cli/src/bittensor/extrinsics/root.py,sha256=N9Fg4VaveRRP1ZN4EZjIWCe04FpTNBKWFqx8USKp9uQ,19062
16
16
  bittensor_cli/src/bittensor/extrinsics/transfer.py,sha256=FyrRo3yk-065toN4f-1Xes23CE5tqP5KU0dBJkKofUc,8476
17
- bittensor_cli/src/bittensor/templates/table.j2,sha256=P2EFiksnO1cQsB8zjK6hVJwUryHTsLslzRE0YtobAV8,10601
18
17
  bittensor_cli/src/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- bittensor_cli/src/commands/sudo.py,sha256=erQVUDj101iJcmDL1Ej7tGQHNfWRP5BHpx0dXLHQYbE,31487
20
- bittensor_cli/src/commands/view.py,sha256=AE2YV92Hj1p9WgcWWTEeL4lySep_uEr6ExJhzXnQbAM,107101
21
- bittensor_cli/src/commands/wallets.py,sha256=hv9gzh-zPS8gXzgJIfENQVR6BFYxh6abjxmHUjqMhsY,51086
18
+ bittensor_cli/src/commands/sudo.py,sha256=GICsjDYvaoJpq5zAUHb3gIcXvD0t1VNOkz8hghz_AUs,31475
19
+ bittensor_cli/src/commands/view.py,sha256=2MdhWWbY9rwGqDilzs8r2ioa0l2GzrYxe8pKkavEVWs,109517
20
+ bittensor_cli/src/commands/wallets.py,sha256=DuG3LQx75NRsi-f0-8Y-FriOSqj57nViNf1xryHHFV0,50711
22
21
  bittensor_cli/src/commands/weights.py,sha256=uI7aACKD90JOtYt61VdKL76z7Fe_wh4WtdwMXL6ydD4,16269
23
22
  bittensor_cli/src/commands/stake/__init__.py,sha256=uxomMv_QrYt5Qn3_X5UWFFh45ISjB0JmDmCFxVyX8nQ,6495
24
- bittensor_cli/src/commands/stake/add.py,sha256=F93eM-RBjrjeoSQAq0iBj6LJwBF9BQJVDd4f9bub1f8,24979
23
+ bittensor_cli/src/commands/stake/add.py,sha256=aVdBEdrfJ5-IK9MpQ00lYQylw_e6BgimCsxMFJZe75E,24911
25
24
  bittensor_cli/src/commands/stake/children_hotkeys.py,sha256=STUeMc9PdzJPx_nfFzPLBUkSc4Wf9b40PJZZZEAKHfI,29630
26
- bittensor_cli/src/commands/stake/list.py,sha256=AnzBhUjuwckFAXYwgv9Yk1JHUKu2w1h8_m8-hf77g80,28625
27
- bittensor_cli/src/commands/stake/move.py,sha256=JuCvAIWd_elB5IDOmzMgJj6w3NikFXiUtRnLZ0N95P0,37236
28
- bittensor_cli/src/commands/stake/remove.py,sha256=gaQN7Gck0l0GCmtiYhZMWlD6OudcNy6cEIEkE8OzQRo,46863
25
+ bittensor_cli/src/commands/stake/list.py,sha256=TfoFrXsYRTQeTiDd6pHu-f6rZ2ev0VK9fMybQ295X7Q,28600
26
+ bittensor_cli/src/commands/stake/move.py,sha256=RSFtMgpBlK82U9CtCcijeZWBOABABnaPzyIgC2ug_Tc,34635
27
+ bittensor_cli/src/commands/stake/remove.py,sha256=iEVV79RmhA2mCL2UGydc98pcCNTO6dpP7gJeq6OfbG0,46884
29
28
  bittensor_cli/src/commands/subnets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
29
  bittensor_cli/src/commands/subnets/price.py,sha256=TWcRXUFeS_Q-pfyv0YIluAL8SE7d2gzTODK-9M2J5pw,29878
31
- bittensor_cli/src/commands/subnets/subnets.py,sha256=YuJ0pmd63O2dg9j3WmTwAeZQ7CGweqw6-ORF6ZaaNh0,84041
32
- bittensor_cli-9.1.0.dist-info/METADATA,sha256=m4srVvkIryqPEtu9SUYUB32Ortgu39AI9cATyo2AqpY,6855
33
- bittensor_cli-9.1.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
34
- bittensor_cli-9.1.0.dist-info/entry_points.txt,sha256=hBTLGLbVxmAKy69XSKaUZvjTCmyEzDGZKq4S8UOto8I,49
35
- bittensor_cli-9.1.0.dist-info/top_level.txt,sha256=DvgvXpmTtI_Q1BbDZMlK90LFcGFCreN1daViEPV2iFw,14
36
- bittensor_cli-9.1.0.dist-info/RECORD,,
30
+ bittensor_cli/src/commands/subnets/subnets.py,sha256=vEhCB756458KstHDSS1nEahympyGWNEmjujUhZ-6NSg,83936
31
+ bittensor_cli-9.1.2.dist-info/METADATA,sha256=KLPzzTfK7BqqmOPUKfAt5PVljkSkm8aNMursZA5ysqQ,6145
32
+ bittensor_cli-9.1.2.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
33
+ bittensor_cli-9.1.2.dist-info/entry_points.txt,sha256=hBTLGLbVxmAKy69XSKaUZvjTCmyEzDGZKq4S8UOto8I,49
34
+ bittensor_cli-9.1.2.dist-info/top_level.txt,sha256=DvgvXpmTtI_Q1BbDZMlK90LFcGFCreN1daViEPV2iFw,14
35
+ bittensor_cli-9.1.2.dist-info/RECORD,,
@@ -1,267 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <title>{{ title }}</title>
6
- <link href="https://fonts.googleapis.com/css2?family=DM+Mono:wght@400&display=swap" rel="stylesheet">
7
- <link href="https://unpkg.com/tabulator-tables@6.2.5/dist/css/tabulator.min.css" rel="stylesheet">
8
- <script type="text/javascript" src="https://oss.sheetjs.com/sheetjs/xlsx.full.min.js"></script>
9
- <script type="text/javascript" src="https://unpkg.com/tabulator-tables@6.2.5/dist/js/tabulator.min.js"></script>
10
- </head>
11
- <style>
12
- .tabulator {
13
- font-family: 'DM Mono', monospace !important;
14
- }
15
-
16
- .tabulator .tabulator-header .tabulator-col {
17
- font-family: 'DM Mono', monospace !important;
18
- }
19
-
20
- .tabulator .tabulator-tableHolder .tabulator-row .tabulator-cell {
21
- font-family: 'DM Mono', monospace !important;
22
- }
23
-
24
- .tabulator .tabulator-header {
25
- background-color: #282A2D !important;
26
- color: white !important;
27
- }
28
-
29
- .tabulator .tabulator-header .tabulator-col {
30
- background-color: #282A2D !important;
31
- color: white !important;
32
- padding: 16px 12px;
33
- }
34
-
35
- .tabulator .tabulator-tableHolder .tabulator-row .tabulator-cell {
36
- padding: 16px 12px;
37
- }
38
-
39
- .tabulator .tabulator-tableHolder .tabulator-row:nth-child(even) {
40
- background-color: #f2f2f2;
41
- }
42
-
43
- .tabulator .tabulator-tableHolder .tabulator-row:hover {
44
- background-color: #ddd;
45
- }
46
-
47
- /* Apply styles only to the filter section selects */
48
- .filter-section select {
49
- -webkit-appearance: none;
50
- -moz-appearance: none;
51
- appearance: none;
52
- padding: 8px;
53
- border: 1px solid #DBDDE1;
54
- border-radius: 4px;
55
- font-family: 'DM Mono', monospace;
56
- font-size: 12px;
57
- color: #0E1013;
58
- background-repeat: no-repeat;
59
- background-position: right 10px center;
60
- background-size: 10px;
61
- background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 4 5"><path fill="%230E1013" d="M2 0L0 2h4z"/></svg>');
62
- }
63
-
64
- /* Specific styles for each select element */
65
- .filter-section #filter-field {
66
- background-color: #F0F0F0;
67
- }
68
-
69
- .filter-section #filter-type {
70
- background-color: #E8E8E8;
71
- border: 1px solid #C0C0C0;
72
- }
73
-
74
- .filter-section #filter-value {
75
- background-color: #FFFFFF;
76
- border: 1px solid #DBDDE1;
77
- }
78
-
79
- </style>
80
- <body style="margin: 0; padding: 0;">
81
- <!-- Header Bar -->
82
- <div style="position: relative; width: 1648px; height: 48px; margin: 32px auto 0 auto; background-color: white; color: #282A2D;">
83
- <!-- Tao Symbol on the Left -->
84
- <div style="position: absolute; width: 48px; height: 48px; left: 16px; top: 0; text-align: center;
85
- font-family: 'GFS Neohellenic', serif; font-weight: 400; font-size: 48px; line-height: 48px; color: #282A2D;">
86
- <?xml version="1.0" encoding="UTF-8"?>
87
- <svg id="a" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 34.44 36.91">
88
- <path d="M20.88,28.32V13.19c0-3.78-3.12-6.86-6.9-6.86V30.51c0,4.81,4.08,6.4,6.6,6.4,2.09,0,3.27-.36,4.69-1.36-3.98-.42-4.39-2.82-4.39-7.23Z"/>
89
- <path d="M6.29,0C2.82,0,0,2.87,0,6.34H28.15c3.47,0,6.29-2.87,6.29-6.34H6.29Z"/>
90
- </svg>
91
-
92
- </div>
93
- <!-- Table Info slightly off-center and constrained to one line -->
94
- <div style="position: absolute; left: 25%; transform: translateX(-25%); top: 12px; width: 60%;
95
- font-family: 'DM Mono', monospace; font-weight: 400; font-size: 12px; letter-spacing: 0.05em;
96
- text-transform: uppercase; color: #282A2D; white-space: nowrap;">
97
- {{ table_info }}
98
- </div>
99
- </div>
100
-
101
- <!-- Filter Section -->
102
- <div class="filter-section"
103
- style="display: flex; flex-direction: row; align-items: center; gap: 16px; padding: 16px; border: 1px dashed #0E1013; border-radius: 5px; width: fit-content; margin: 20px auto; box-sizing: border-box;">
104
-
105
- <!-- Filter Field -->
106
- <div style="display: flex; flex-direction: column; gap: 4px; flex-grow: 0;">
107
- <label for="filter-field"
108
- style="font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">
109
- Filter Field
110
- </label>
111
- <select id="filter-field">
112
- <option></option>
113
- {% for col in column_names %}
114
- <option value="{{ col }}">{{ col }}</option>
115
- {% endfor %}
116
- </select>
117
- </div>
118
-
119
- <!-- Filter Type -->
120
- <div style="display: flex; flex-direction: column; gap: 4px; flex-grow: 0;">
121
- <label for="filter-type"
122
- style="font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">
123
- Filter Type
124
- </label>
125
- <select id="filter-type">
126
- <option value="=">=</option>
127
- <option value="<"><</option>
128
- <option value="<="><=</option>
129
- <option value=">">></option>
130
- <option value=">=">>=</option>
131
- <option value="!=">!=</option>
132
- <option value="like">like</option>
133
- </select>
134
- </div>
135
-
136
- <!-- Filter Value -->
137
- <div style="display: flex; flex-direction: column; gap: 4px; flex-grow: 0;">
138
- <label for="filter-value"
139
- style="font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">
140
- Filter Value
141
- </label>
142
- <input id="filter-value" type="text" placeholder="value to filter"
143
- style="padding: 8px; border: 1px solid #DBDDE1; border-radius: 4px; background: #FFFFFF; font-family: 'DM Mono'; font-size: 12px; color: #0E1013;">
144
- </div>
145
-
146
- <!-- Clear Button -->
147
- <div style="display: flex; align-items: flex-end; flex-grow: 0;">
148
- <button id="filter-clear"
149
- style="padding: 8px 16px; border: 1px solid #DBDDE1; border-radius: 4px; background: #FFFFFF; font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">
150
- Clear Filter
151
- </button>
152
- </div>
153
-
154
- </div>
155
-
156
- <!-- Table Placeholder (for reference) -->
157
- <div id="my-table" style="width: 1648px; margin: 20px auto;">
158
- <!-- Table content will go here -->
159
- </div>
160
-
161
- <div style="margin: 20px auto; text-align: center;">
162
- <button id="download-csv" style="padding: 8px 16px; border: 1px solid #DBDDE1; border-radius: 4px; background: #FFFFFF; font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">Download CSV</button>
163
- <button id="download-json" style="padding: 8px 16px; border: 1px solid #DBDDE1; border-radius: 4px; background: #FFFFFF; font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">Download JSON</button>
164
- <button id="download-xlsx" style="padding: 8px 16px; border: 1px solid #DBDDE1; border-radius: 4px; background: #FFFFFF; font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">Download XLSX</button>
165
- <button id="download-html" style="padding: 8px 16px; border: 1px solid #DBDDE1; border-radius: 4px; background: #FFFFFF; font-family: 'DM Mono'; font-size: 12px; text-transform: uppercase; color: #0E1013;">Download HTML</button>
166
- </div>
167
-
168
- </body>
169
-
170
- <script>
171
- const columns = {{ columns|safe }};
172
- columns.forEach(column => {
173
- if (column.customFormatter === "millify") {
174
- column.formatter = millify;
175
- delete column.customFormatter;
176
- }
177
- });
178
- const None = null;
179
- const table = new Tabulator("#my-table",
180
- {
181
- columns: columns,
182
- data: {{ rows|safe }},
183
- pagination: "local",
184
- paginationSize: 50,
185
- paginationSizeSelector: [50, 100, 150, 200],
186
- movableColumns: true,
187
- paginationCounter: "rows",
188
- layout: "fitDataFill",
189
- {% if tree %} dataTree: true, {% endif %}
190
- }
191
- )
192
- //Define variables for input elements
193
- const fieldEl = document.getElementById("filter-field");
194
- const typeEl = document.getElementById("filter-type");
195
- const valueEl = document.getElementById("filter-value");
196
-
197
- //Custom filter example
198
- function customFilter(data) {
199
- return data.car && data.rating < 3;
200
- }
201
-
202
- //Trigger setFilter function with correct parameters
203
- function updateFilter() {
204
- var filterVal = fieldEl.options[fieldEl.selectedIndex].value;
205
- var typeVal = typeEl.options[typeEl.selectedIndex].value;
206
-
207
- var filter = filterVal == "function" ? customFilter : filterVal;
208
-
209
- if (filterVal == "function") {
210
- typeEl.disabled = true;
211
- valueEl.disabled = true;
212
- } else {
213
- typeEl.disabled = false;
214
- valueEl.disabled = false;
215
- }
216
-
217
- if (filterVal) {
218
- table.setFilter(filter, typeVal, valueEl.value);
219
- }
220
- }
221
-
222
- function millify(cell, formatterParams, onRendered) {
223
- const millNames = ["", "K", "M", "B", "T"];
224
- const n = cell.getValue();
225
- const nAbs = Math.abs(n);
226
- const millIdx = Math.max(0, Math.min(millNames.length - 1, Math.floor(nAbs === 0 ? 0 : Math.log10(nAbs) / 3)));
227
-
228
- return (n / Math.pow(10, 3 * millIdx)).toFixed(2) + millNames[millIdx];
229
- }
230
-
231
- //Update filters on value change
232
- document.getElementById("filter-field").addEventListener("change", updateFilter);
233
- document.getElementById("filter-type").addEventListener("change", updateFilter);
234
- document.getElementById("filter-value").addEventListener("keyup", updateFilter);
235
-
236
- //Clear filters on "Clear Filters" button click
237
- document.getElementById("filter-clear").addEventListener("click", function () {
238
- fieldEl.value = "";
239
- typeEl.value = "=";
240
- valueEl.value = "";
241
-
242
- table.clearFilter();
243
- });
244
-
245
- //trigger download of data.csv file
246
- document.getElementById("download-csv").addEventListener("click", function () {
247
- table.download("csv", "{{ title }}.csv");
248
- });
249
-
250
- //trigger download of data.json file
251
- document.getElementById("download-json").addEventListener("click", function () {
252
- table.download("json", "{{ title }}.json");
253
- });
254
-
255
- //trigger download of data.xlsx file
256
- document.getElementById("download-xlsx").addEventListener("click", function () {
257
- table.download("xlsx", "{{ title }}.xlsx", {sheetName: "My Data"});
258
- });
259
-
260
- //trigger download of data.html file
261
- document.getElementById("download-html").addEventListener("click", function () {
262
- table.download("html", "{{ title }}.html", {style: true});
263
- });
264
-
265
- </script>
266
-
267
- </html>