Skip to content

Enhance frontend dashboard UX and backend SDK with improved error handling and type hints #699

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ Preswald tracks state and dependencies, so computations update only when needed

# **🚀 Getting Started**

```markdown
## **Installation**

First, install Preswald using pip. https://pypi.org/project/preswald/
First, install Preswald using pip (requires Python 3.7+)
[Preswald on PyPI](https://pypi.org/project/preswald/)

```bash
pip install preswald
Expand All @@ -72,6 +74,12 @@ or
uv pip install preswald
```

**Note:**
If you encounter issues during installation, make sure you have an updated version of pip:

```bash
pip install --upgrade pip

![Demo GIF](assets/demo1.gif)

## **👩‍💻 Quick Start**
Expand Down Expand Up @@ -158,6 +166,7 @@ The first time you deploy, you'll be prompted to enter your **GitHub username**
Now your app is live, shareable, and scalable—without any extra setup.



## **🔧 Configuration**

Preswald uses `preswald.toml` for project settings and theming. It’s straightforward, and it makes your app look polished.
Expand Down Expand Up @@ -189,6 +198,25 @@ format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"

<br>

## 🛠 Troubleshooting

### Port already in use

If you run `preswald run` and see an error like:
```
OSError: [Errno 98] Address already in use
```
it means another application is already using the default port (8501).

You have two options:
- Stop the application that's using port 8501
- Or run Preswald on a different port:

```bash
preswald run --port 8502



## **📚 Documentation**

We’re here to help! Check out our full documentation at [Preswald Docs](https://docs.preswald.com/).
Expand Down
19 changes: 11 additions & 8 deletions frontend/src/components/pages/Dashboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,24 @@ const Dashboard = ({ components, error, handleComponentUpdate }) => {
);
}

if (!isValidComponents) {
if (!components || !components.rows) {
return (
<div className="dashboard-loading-container">
<LoadingState

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem wise to remove loadingstate component

isConnected={true}
customText={!components ? 'Loading components' : 'Invalid components data'}
/>
<div className="dashboard-empty">
<p className="dashboard-empty-text">

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the existing loadingstate component is good

{!components
? 'Loading components...'
: 'Invalid components data. Please check your configuration or data source.'}
</p>
</div>
);
}

if (components.rows.length === 0) {
return (
<div className="dashboard-empty">
<p className="dashboard-empty-text">No components to display</p>
<p className="dashboard-empty-text">
No components to display. Try running a query or verifying your configuration settings.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this messaging is right

</p>
</div>
);
}
Expand Down
28 changes: 26 additions & 2 deletions preswald/interfaces/data.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
import time

import pandas as pd

Expand All @@ -12,6 +13,9 @@
def connect():
"""
Connect to all listed data sources in preswald.toml

Returns:
A DuckDBPyConnection instance if connection is successful, otherwise None.
"""
try:
service = PreswaldService.get_instance()
Expand All @@ -21,25 +25,44 @@ def connect():
return duckdb_conn
except Exception as e:
logger.error(f"Error connecting to datasources: {e}")
return None


def query(sql: str, source_name: str) -> pd.DataFrame:
def query(sql: str, source_name: str) -> pd.DataFrame | None:
"""
Query a data source using sql from preswald.toml by name
Query a data source using sql from preswald.toml by name.

Args:
sql: The SQL query to run.
source_name: The name of the data source configured in preswald.toml.

Returns:
A pandas DataFrame if successful, otherwise None
"""
try:
service = PreswaldService.get_instance()
# Measure and log query execution time for performance monitoring
start_time = time.time()
df_result = service.data_manager.query(sql, source_name)
elapsed_time = time.time() - start_time

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be removed since its not used

logger.info(f"Successfully queried data source: {source_name}")
return df_result
except Exception as e:
logger.error(f"Error querying data source: {e}")
return None

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo, should error out instead of returning None



def get_df(source_name: str, table_name: str | None = None) -> pd.DataFrame:
"""
Get a dataframe from the named data source from preswald.toml
If the source is a database/has multiple tables, you must specify a table_name

Args:
source_name: The name of the configured data source.
table_name: Optional; name of the table to load if the source has multiple tables.

Returns:
A pandas DataFrame if successful, otherwise None.
"""
try:
service = PreswaldService.get_instance()
Expand All @@ -48,3 +71,4 @@ def get_df(source_name: str, table_name: str | None = None) -> pd.DataFrame:
return df_result
except Exception as e:
logger.error(f"Error getting a dataframe from data source: {e}")
return None