Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
bd4236f
feat: add `print_data_frame` using `rich.table` for printing data frames
KristijanArmeni Aug 5, 2025
9a07204
use print_data_frame in new_analysis
KristijanArmeni Aug 5, 2025
2bc7d4a
feat: add `print_dialog_section_title()`
KristijanArmeni Aug 5, 2025
efc0b80
feat: use print_data_frame and print_dialogue_section_title
KristijanArmeni Aug 5, 2025
6936419
feat: update ascii art welcome page, use rich.Panel
KristijanArmeni Aug 7, 2025
315b08e
chore: add credits
KristijanArmeni Aug 7, 2025
47fa4b3
fix: try moving script comment
KristijanArmeni Aug 7, 2025
3db08be
fix: reapply formatting
KristijanArmeni Aug 7, 2025
c08d7c4
chore: add rich to requirements
KristijanArmeni Aug 8, 2025
d8bbc91
feat: add option to color-code cols based on datatype and smarter col…
KristijanArmeni Aug 28, 2025
4464383
feat: add smart_print_data_frame to only show summary for large data_…
KristijanArmeni Aug 28, 2025
76ebff2
feat: fix no-coloring mode, simplify and remove column width computation
KristijanArmeni Aug 28, 2025
978588c
feat: use smart dataframe printing in project selection
KristijanArmeni Aug 28, 2025
30f4901
refactor: revert to using print for section titles
KristijanArmeni Aug 28, 2025
930520c
feat: use table captions in smart_print_data_frame
KristijanArmeni Aug 28, 2025
638275d
chore: use print, remove unused imports
KristijanArmeni Aug 28, 2025
a8a4232
feat: add three different logo sizes
KristijanArmeni Aug 29, 2025
abe6225
feat: add three different logo sizes
KristijanArmeni Aug 29, 2025
3509b78
chore: add docstring to smart_print_data_frame
KristijanArmeni Aug 29, 2025
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
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ repos:
- id: isort

- repo: https://github.com/psf/black
rev: 23.9.1
rev: 24.10.0
hooks:
- id: black
29 changes: 18 additions & 11 deletions components/analysis_params.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from tempfile import TemporaryDirectory

import polars as pl
from pydantic import BaseModel

from analyzer_interface import (
Expand All @@ -11,7 +12,7 @@
)
from app import ProjectContext
from context import InputColumnProvider, PrimaryAnalyzerDefaultParametersContext
from terminal_tools import print_ascii_table, prompts
from terminal_tools import prompts, smart_print_data_frame

from .context import ViewContext

Expand Down Expand Up @@ -58,7 +59,7 @@ def customize_analysis(
}

while True:
with context.terminal.nest("Customization"):
with context.terminal.nest("◆◆ Parameter customization ◆◆"):
param_states = [
ParamState(
param_spec=param_spec,
Expand All @@ -67,15 +68,21 @@ def customize_analysis(
for param_spec in analyzer.params
]

print_ascii_table(
[
[
param_state.param_spec.print_name,
print_param_value(param_state.value),
]
for param_state in param_states
],
header=["parameter name", "parameter value"],
smart_print_data_frame(
data_frame=pl.DataFrame(
{
"parameter name": [
param_state.param_spec.print_name
for param_state in param_states
],
"parameter value": [
print_param_value(param_state.value)
for param_state in param_states
],
}
),
title="Analysis Parameters",
apply_color=None,
)

has_all_params = all(
Expand Down
77 changes: 50 additions & 27 deletions components/new_analysis.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from tempfile import TemporaryDirectory
from traceback import format_exc
from typing import Optional

Expand All @@ -7,14 +6,12 @@
from analyzer_interface import (
AnalyzerInterface,
InputColumn,
ParamValue,
UserInputColumn,
column_automap,
get_data_type_compatibility_score,
)
from app import ProjectContext
from context import InputColumnProvider, PrimaryAnalyzerDefaultParametersContext
from terminal_tools import draw_box, print_ascii_table, prompts, wait_for_key
from terminal_tools import draw_box, prompts, smart_print_data_frame, wait_for_key

from .analysis_params import customize_analysis
from .context import ViewContext
Expand Down Expand Up @@ -44,20 +41,33 @@ def new_analysis(

with terminal.nest(draw_box(analyzer.name, padding_lines=0)):
with terminal.nest("◆◆ About this test ◆◆"):

print("")
print(analyzer.long_description or analyzer.short_description)
print("")
print("◆◆ Required Input ◆◆")
print("The test requires these columns in the input data:")
print("")
for index, input_column in enumerate(analyzer.input.columns):
print(
f"[{index + 1}] {input_column.human_readable_name_or_fallback()}"
f" ({input_column.data_type})"

required_cols_dict = {"Column ID": [], "Description": []}
for input_column in analyzer.input.columns:
required_cols_dict["Column ID"].append(
input_column.human_readable_name_or_fallback()
)
print(input_column.description or "")
print("")
required_cols_dict["Description"].append(input_column.description)

smart_print_data_frame(
data_frame=pl.DataFrame(required_cols_dict),
title=None,
apply_color="row-wise",
)

# for index, input_column in enumerate(analyzer.input.columns):
# print(
# f"[{index + 1}] {input_column.human_readable_name_or_fallback()}"
# f" ({input_column.data_type})"
# )
# print(input_column.description or "")
# print("")

user_columns = project.columns
user_columns_by_name = {
Expand All @@ -75,8 +85,8 @@ def new_analysis(
print("These columns cannot be satisfied:")
for input_column in unmapped_columns:
print(
f"- {input_column.human_readable_name_or_fallback()
} ({input_column.data_type})"
f"- {input_column.human_readable_name_or_fallback()}"
+ f" ({input_column.data_type})"
)

print("")
Expand All @@ -92,16 +102,25 @@ def new_analysis(

final_column_mapping = draft_column_mapping
while True:
with terminal.nest("Column mapping") as column_mapping_scope:
print_ascii_table(
rows=[
[
input_column.human_readable_name_or_fallback(),
'"' + draft_column_mapping.get(input_column.name) + '"',
]
for input_column in analyzer.input.columns
],
header=["Test's Input Column", "← Your Dataset's Column"],
with terminal.nest("◆◆ Column selection ◆◆") as column_mapping_scope:
mapping_df = pl.DataFrame(
{
"Column Name for Analyzer Input": [
input_column.human_readable_name_or_fallback()
for input_column in analyzer.input.columns
],
"← Column Name In Your Dataset": [
draft_column_mapping.get(input_column.name)
for input_column in analyzer.input.columns
],
}
)

smart_print_data_frame(
data_frame=mapping_df,
title=None,
apply_color="row-wise",
smart_print=False,
)

sample_input_df = pl.DataFrame(
Expand All @@ -115,7 +134,12 @@ def new_analysis(
}
)
print("Your test data would look like this:")
print(sample_input_df)
smart_print_data_frame(
data_frame=sample_input_df,
title="Sample input data",
apply_color="column-wise",
smart_print=False,
)

mapping_ok = prompts.confirm(
"Are you happy with this mapping?",
Expand Down Expand Up @@ -163,8 +187,8 @@ def new_analysis(
print("Explanation: " + selected_analyzer_column.description)
print("")
print(
f"The test requires data type [{
selected_analyzer_column.data_type}] for this column."
"The test requires data type"
+ f"[{selected_analyzer_column.data_type}] for this column."
)
print("")

Expand All @@ -191,7 +215,6 @@ def new_analysis(
draft_column_mapping[selected_analyzer_column.name] = (
selected_user_column.name
)

param_values = customize_analysis(
context, project, analyzer, final_column_mapping
)
Expand Down
21 changes: 6 additions & 15 deletions components/select_project.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Optional

from app import ProjectContext
from terminal_tools import draw_box, print_ascii_table, prompts, wait_for_key
from terminal_tools import draw_box, prompts, smart_print_data_frame, wait_for_key

from .context import ViewContext

Expand Down Expand Up @@ -30,20 +30,11 @@ def select_project(ctx: ViewContext):
draw_box(f"Project: {project.display_name}", padding_lines=0)
):
df = project.preview_data
print_ascii_table(
[
[preview_value(cell) for cell in row]
for row in df.head(10).iter_rows()
],
header=df.columns,
)
print(f"(Total {project.data_row_count} rows)")
print("Inferred column semantics:")
print_ascii_table(
rows=[
[col.name, col.semantic.semantic_name] for col in project.columns
],
header=["Column", "Semantic"],
smart_print_data_frame(
data_frame=df.head(5),
title="Input data preview",
apply_color=None,
caption=f"Total rows: {project.data_row_count:,}",
)

confirm_load = prompts.confirm("Load this project?", default=True)
Expand Down
86 changes: 75 additions & 11 deletions components/splash.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,76 @@
from rich import print
from rich.console import Console
from rich.panel import Panel

from meta import get_version
from terminal_tools import clear_terminal, wait_for_key


def splash():
console = Console()

# Calculate exact width thresholds for each logo size
# Measured from actual ASCII art content + Rich Panel borders/padding
BIG_LOGO_WIDTH = 102 # Big logo longest line
SMALL_LOGO_WIDTH = 78 # Small logo longest line
PANEL_PADDING = 4 # Rich Panel border + padding (2 chars each side)

BIG_THRESHOLD = BIG_LOGO_WIDTH + PANEL_PADDING # 106 chars
SMALL_THRESHOLD = SMALL_LOGO_WIDTH + PANEL_PADDING # 82 chars

clear_terminal()
print(_ascii_splash)
print("")
print(f"{get_version() or '<development version>'}")

# Three-tier adaptive logo display
if console.size.width < SMALL_THRESHOLD:
print(_LOGO_MINI) # Very narrow terminals
elif console.size.width < BIG_THRESHOLD:
print(_ASCII_LOGO_SMALL) # Medium terminals
else:
print(_ASCII_LOGO_BIG) # Wide terminals

print(_ASCII_TREE)
print("")
wait_for_key(True)


_ascii_splash: str = """
-..*+:..-
_VERSION = f"[dim]{get_version() or 'development version'}[/dim]"
_TITLE = "A Civic Tech DC Project"

_LOGO_MINI = Panel.fit(
"""[orange1 bold]
CIB MANGO TREE
[/orange1 bold]""",
title=_TITLE,
title_align="center",
subtitle=_VERSION,
)

_ASCII_LOGO_SMALL = Panel.fit(
"""[orange1]
____ ___ ____ __ __ _____
/ ___|_ _| __ ) | \/ | __ _ _ __ __ _ ___ |_ _| __ ___ ___
| | | || _ \ | |\/| |/ _` | '_ \ / _` |/ _ \ | || '__/ _ \/ _ \\
| |___ | || |_) | | | | | (_| | | | | (_| | (_) | | || | | __/ __/
\____|___|____/ |_| |_|\__,_|_| |_|\__, |\___/ |_||_| \___|\___|
|___/[/orange1]""",
title=_TITLE,
subtitle=_VERSION,
)

_ASCII_LOGO_BIG = Panel.fit(
"""[orange1]
██████╗ ██╗ ██████╗ ███╗ ███╗ █████╗ ███╗ ██╗ ██████╗ ██████╗ ████████╗ ██████╗ ███████╗ ███████╗
██╔════╝ ██║ ██╔══██╗ ████╗ ████║ ██╔══██╗ ████╗ ██║ ██╔════╝ ██╔═══██╗ ╚══██╔══╝ ██╔══██╗ ██╔════╝ ██╔════╝
██║ ██║ ██████╔╝ ██╔████╔██║ ███████║ ██╔██╗ ██║ ██║ ███╗ ██║ ██║ ██║ ██████╔╝ █████╗ █████╗
██║ ██║ ██╔══██╗ ██║╚██╔╝██║ ██╔══██║ ██║╚██╗██║ ██║ ██║ ██║ ██║ ██║ ██╔══██╗ ██╔══╝ ██╔══╝
╚██████╗ ██║ ██████╔╝ ██║ ╚═╝ ██║ ██║ ██║ ██║ ╚████║ ╚██████╔╝ ╚██████╔╝ ██║ ██║ ██║ ███████╗ ███████╗
╚═════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚══════╝[/orange1]""",
title=_TITLE,
subtitle=_VERSION,
)

_ASCII_TREE: str = """
-..*+:..-.
-.=-+%@%##+-=.-
= =:*%:...=:..=@*:+ =
:: -:=#==#*=:::-=-...-:::
Expand All @@ -26,14 +84,20 @@ def splash():
@@#=
@@%
@@@
"""

C I B M A N G O T R E E

A Civic Tech DC Project

_FOOTER: str = Panel.fit(
"""
A Civic Tech DC Project
[red]
╱ * * * ╱ ╲
╲ ===== ╱ ╱
╲ ===== ╱ ╱[/red]
"""
)

"""
I generated this using https://www.asciiart.eu/image-to-ascii
Notes:
Logo generated with: https://github.com/shinshin86/oh-my-logo
(used as: `npx oh-my-logo "CIB Mango Tree" gold --filled --no-color`)
Ascii tree was generated with: https://www.asciiart.eu/image-to-ascii
"""
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ shinywidgets==0.6.2
starlette==0.47.1
uvicorn==0.34.3
a2wsgi==1.10.10
python-json-logger==2.0.7
python-json-logger==2.0.7
rich==14.0.0
2 changes: 2 additions & 0 deletions terminal_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@
enable_windows_ansi_support,
open_directory_explorer,
print_ascii_table,
print_dialog_section_title,
smart_print_data_frame,
wait_for_key,
)
2 changes: 2 additions & 0 deletions terminal_tools/inception.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
in memory and printed to the terminal at each refresh.
"""

# from terminal_tools import print_dialog_section_title

from .utils import clear_terminal


Expand Down
Loading