truthound-dashboard 1.4.0__py3-none-any.whl → 1.4.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.
@@ -17,9 +17,11 @@ Example:
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import logging
20
21
  from collections.abc import AsyncGenerator
21
22
  from contextlib import asynccontextmanager
22
23
 
24
+ from sqlalchemy import text
23
25
  from sqlalchemy.ext.asyncio import (
24
26
  AsyncEngine,
25
27
  AsyncSession,
@@ -31,6 +33,8 @@ from truthound_dashboard.config import get_settings
31
33
 
32
34
  from .base import Base
33
35
 
36
+ logger = logging.getLogger(__name__)
37
+
34
38
  # Global engine and session factory
35
39
  _engine: AsyncEngine | None = None
36
40
  _session_factory: async_sessionmaker[AsyncSession] | None = None
@@ -148,10 +152,54 @@ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
148
152
  yield session
149
153
 
150
154
 
155
+ async def _run_migrations(engine: AsyncEngine) -> None:
156
+ """Run database migrations for schema updates.
157
+
158
+ This handles backward compatibility when new columns are added.
159
+ SQLite doesn't support ALTER TABLE ADD COLUMN IF NOT EXISTS,
160
+ so we check column existence first.
161
+
162
+ Args:
163
+ engine: Database engine to use.
164
+ """
165
+ migrations = [
166
+ # (table_name, column_name, column_definition, default_value)
167
+ ("schedules", "trigger_type", "VARCHAR(50)", "cron"),
168
+ ("schedules", "trigger_config", "JSON", None),
169
+ ("schedules", "trigger_count", "INTEGER", 0),
170
+ ("schedules", "last_trigger_result", "JSON", None),
171
+ ]
172
+
173
+ async with engine.begin() as conn:
174
+ for table, column, col_type, default in migrations:
175
+ # Check if column exists
176
+ result = await conn.execute(
177
+ text(f"PRAGMA table_info({table})")
178
+ )
179
+ columns = [row[1] for row in result.fetchall()]
180
+
181
+ if column not in columns:
182
+ # Add the column
183
+ if default is not None:
184
+ if isinstance(default, str):
185
+ sql = f"ALTER TABLE {table} ADD COLUMN {column} {col_type} DEFAULT '{default}'"
186
+ else:
187
+ sql = f"ALTER TABLE {table} ADD COLUMN {column} {col_type} DEFAULT {default}"
188
+ else:
189
+ sql = f"ALTER TABLE {table} ADD COLUMN {column} {col_type}"
190
+
191
+ try:
192
+ await conn.execute(text(sql))
193
+ logger.info(f"Migration: Added column {table}.{column}")
194
+ except Exception as e:
195
+ logger.warning(f"Migration skipped for {table}.{column}: {e}")
196
+
197
+
151
198
  async def init_db(engine: AsyncEngine | None = None) -> None:
152
199
  """Initialize database tables.
153
200
 
154
- Creates all tables defined in models if they don't exist.
201
+ Creates all tables defined in models if they don't exist,
202
+ then runs migrations for schema updates.
155
203
 
156
204
  Args:
157
205
  engine: Optional engine to use. If None, uses default engine.
@@ -160,6 +208,9 @@ async def init_db(engine: AsyncEngine | None = None) -> None:
160
208
  async with target_engine.begin() as conn:
161
209
  await conn.run_sync(Base.metadata.create_all)
162
210
 
211
+ # Run migrations for existing databases
212
+ await _run_migrations(target_engine)
213
+
163
214
 
164
215
  async def drop_db(engine: AsyncEngine | None = None) -> None:
165
216
  """Drop all database tables.
@@ -6,7 +6,7 @@
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
7
  <meta name="description" content="Truthound Dashboard - Open-source data quality monitoring" />
8
8
  <title>Truthound Dashboard</title>
9
- <script type="module" crossorigin src="/assets/index-C7OZTzXa.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-DkU82VsU.js"></script>
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-C6JSrkHo.css">
11
11
  </head>
12
12
  <body>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: truthound-dashboard
3
- Version: 1.4.0
3
+ Version: 1.4.2
4
4
  Summary: Open-source data quality dashboard - GX Cloud alternative
5
5
  Author-email: Truthound Team <team@truthound.dev>
6
6
  License-Expression: Apache-2.0
@@ -24,6 +24,7 @@ Requires-Dist: cryptography>=41.0.0
24
24
  Requires-Dist: fastapi>=0.110.0
25
25
  Requires-Dist: httpx>=0.26.0
26
26
  Requires-Dist: jinja2>=3.1.0
27
+ Requires-Dist: numpy>=1.24.0
27
28
  Requires-Dist: polars>=0.20.0
28
29
  Requires-Dist: pydantic-settings>=2.1.0
29
30
  Requires-Dist: pydantic>=2.5.0
@@ -46,9 +47,6 @@ Requires-Dist: redis>=5.0.0; extra == 'redis'
46
47
  Provides-Extra: translate
47
48
  Description-Content-Type: text/markdown
48
49
 
49
-
50
- <img width="2551" height="911" alt="스크린샷 2026-01-08 오전 11 54 18" src="https://github.com/user-attachments/assets/5e6b6d39-ee69-48f3-b0df-577f73bcb03d" />
51
-
52
50
  # truthound-dashboard
53
51
 
54
52
  [![PyPI version](https://img.shields.io/pypi/v/truthound-dashboard.svg)](https://pypi.org/project/truthound-dashboard/)
@@ -59,7 +57,11 @@ Description-Content-Type: text/markdown
59
57
 
60
58
  A web-based data quality monitoring dashboard for [truthound](https://github.com/seadonggyun4/truthound).
61
59
 
62
- [Documentation](https://truthound.netlify.app) | [PyPI](https://pypi.org/project/truthound-dashboard/)
60
+ [Documentation](https://truthound.netlify.app) | [PyPI](https://pypi.org/project/truthound-dashboard/) | [Live Demo](https://truthound-dashborad.vercel.app/)
61
+
62
+ > **Try the Live Demo**: Experience the full dashboard interface with mock data at [truthound-dashborad.vercel.app](https://truthound-dashborad.vercel.app/). No installation required!
63
+ >
64
+ > **Note**: The demo page displays in English only. Language selection is not available in the demo due to Intlayer's runtime requirements. When you install and run truthound-dashboard locally, full multi-language support (English, Korean, and AI-translated languages) works as expected.
63
65
 
64
66
  ## Overview
65
67
  <img width="300" height="300" alt="Truthound_icon" src="https://github.com/user-attachments/assets/90d9e806-8895-45ec-97dc-f8300da4d997" />
@@ -165,7 +165,7 @@ truthound_dashboard/core/websocket/manager.py,sha256=y_pLWwE93ee6N2gIqKaiGvyfnVw
165
165
  truthound_dashboard/core/websocket/messages.py,sha256=_AwY_rm1gSb1AmyMgBeVCYm5MRKnxkZbTfivevQGi4s,3929
166
166
  truthound_dashboard/db/__init__.py,sha256=AzuuU-pWTnYqH_WUCS7coniWK81j1r7go9cGTPcvWl4,2957
167
167
  truthound_dashboard/db/base.py,sha256=nj6DbYxAl5JoY0hC5rtM7pshOKpe-OH-AFsQqPGQzsM,2832
168
- truthound_dashboard/db/database.py,sha256=yQ9FsQSi29HvvW0t6BrkyX5YcHijRJEjGTTGfcCCw10,5078
168
+ truthound_dashboard/db/database.py,sha256=vbO2OnRJoEXdUQzkuHbE5vKePmnFr-OBnBISuWLClKs,7028
169
169
  truthound_dashboard/db/models.py,sha256=Wv93Xk5Dxhq5nF4WDHWrMPp6xbpY_ZaOmHmsfQdeBbc,175626
170
170
  truthound_dashboard/db/repository.py,sha256=48AJd65rwTSt1XqTN4vUeJ8nk416buE254JqipDCEEE,6890
171
171
  truthound_dashboard/schemas/__init__.py,sha256=e3SXeKquiVFFYW8yc7-lOa70nXQg8qyMqwbFxOI4eb0,16500
@@ -219,7 +219,7 @@ truthound_dashboard/schemas/validators/string_validators.py,sha256=28Jm-6ioe4N2Q
219
219
  truthound_dashboard/schemas/validators/table_validators.py,sha256=hIt5LcGZ05QZMKPNI5J5SEWfUJfejeO7e1Q7DP3nX8k,13967
220
220
  truthound_dashboard/schemas/validators/uniqueness_validators.py,sha256=Y23ts3FAAuhluXqz8bdFW8VMN5HZIOM7Nz9Hub0ehRw,12110
221
221
  truthound_dashboard/static/favicon.ico,sha256=T9BJXnpyq6t6lpFHd6ivEimWF2uGFzwxDeTDyz2UVmQ,15406
222
- truthound_dashboard/static/index.html,sha256=zF4jSC_mYBpqp2NSiPQ-58ljscoJcsjEUMJiZHfScII,568
222
+ truthound_dashboard/static/index.html,sha256=FOVfkV8SOJJNTbA_BPnOQxcdW3KBbFRxZ23jKspIVpo,568
223
223
  truthound_dashboard/static/assets/logo--IpBiMPK.png,sha256=c7ne4oaZ1BpQR5yP2TVYoAE1s1vS7lRPxgWP1pzUMYE,213715
224
224
  truthound_dashboard/translate/__init__.py,sha256=Mv_xtMk4SaBGUsMgJqhwARnkJkZdq4OhJi5HdpdsBnw,1458
225
225
  truthound_dashboard/translate/config_updater.py,sha256=AtJ7ero4cHKwUYQRDbGaPAlLmcyBv0IeBewdiTo0X8c,12038
@@ -232,8 +232,8 @@ truthound_dashboard/translate/providers/mistral.py,sha256=j0oh_mGksdMuIfbuZKq0yo
232
232
  truthound_dashboard/translate/providers/ollama.py,sha256=XlAHE14VvdSPKFbrJIbB3KUHzrgQm0Zj08snL71otUQ,7286
233
233
  truthound_dashboard/translate/providers/openai.py,sha256=AeaOfRjNgCIgBdcX1gcYqqf41fXeEIyN3AiLAOy3SZc,6760
234
234
  truthound_dashboard/translate/providers/registry.py,sha256=iwfcWYJ2uKfwWjsalhV4jSoyZC7bSeujK6sTMuylMMY,6474
235
- truthound_dashboard-1.4.0.dist-info/METADATA,sha256=fh_f_UPYvhBAQ6eRQ5r5xNf3X1CHGtPEftAbZZ_tUWY,18083
236
- truthound_dashboard-1.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
237
- truthound_dashboard-1.4.0.dist-info/entry_points.txt,sha256=Xq8qadJ-Sqk4_0Ss_rhCqCv7uxPZZdwO3WUnbK0r6Hw,135
238
- truthound_dashboard-1.4.0.dist-info/licenses/LICENSE,sha256=qrBWTDMS8ZvwVJl3Yo2n8_AxOos3s8S9pcOzLW5Nivg,10763
239
- truthound_dashboard-1.4.0.dist-info/RECORD,,
235
+ truthound_dashboard-1.4.2.dist-info/METADATA,sha256=_RZUb1KLt_kdgRVgc5iJl7M-NybOmMvvbaOjFuEA78Y,18447
236
+ truthound_dashboard-1.4.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
237
+ truthound_dashboard-1.4.2.dist-info/entry_points.txt,sha256=Xq8qadJ-Sqk4_0Ss_rhCqCv7uxPZZdwO3WUnbK0r6Hw,135
238
+ truthound_dashboard-1.4.2.dist-info/licenses/LICENSE,sha256=qrBWTDMS8ZvwVJl3Yo2n8_AxOos3s8S9pcOzLW5Nivg,10763
239
+ truthound_dashboard-1.4.2.dist-info/RECORD,,