parqv 0.2.0__py3-none-any.whl → 0.3.0__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.
Files changed (36) hide show
  1. parqv/__init__.py +31 -0
  2. parqv/app.py +84 -102
  3. parqv/cli.py +112 -0
  4. parqv/core/__init__.py +31 -0
  5. parqv/core/config.py +26 -0
  6. parqv/core/file_utils.py +88 -0
  7. parqv/core/handler_factory.py +90 -0
  8. parqv/core/logging.py +46 -0
  9. parqv/data_sources/__init__.py +48 -0
  10. parqv/data_sources/base/__init__.py +28 -0
  11. parqv/data_sources/base/exceptions.py +38 -0
  12. parqv/{handlers/base_handler.py → data_sources/base/handler.py} +54 -25
  13. parqv/{handlers → data_sources/formats}/__init__.py +13 -5
  14. parqv/data_sources/formats/csv.py +460 -0
  15. parqv/{handlers → data_sources/formats}/json.py +68 -32
  16. parqv/{handlers → data_sources/formats}/parquet.py +67 -56
  17. parqv/views/__init__.py +38 -0
  18. parqv/views/base.py +98 -0
  19. parqv/views/components/__init__.py +13 -0
  20. parqv/views/components/enhanced_data_table.py +152 -0
  21. parqv/views/components/error_display.py +72 -0
  22. parqv/views/components/loading_display.py +44 -0
  23. parqv/views/data_view.py +119 -46
  24. parqv/views/metadata_view.py +57 -20
  25. parqv/views/schema_view.py +190 -200
  26. parqv/views/utils/__init__.py +19 -0
  27. parqv/views/utils/data_formatters.py +184 -0
  28. parqv/views/utils/stats_formatters.py +220 -0
  29. parqv/views/utils/visualization.py +204 -0
  30. {parqv-0.2.0.dist-info → parqv-0.3.0.dist-info}/METADATA +5 -6
  31. parqv-0.3.0.dist-info/RECORD +36 -0
  32. {parqv-0.2.0.dist-info → parqv-0.3.0.dist-info}/WHEEL +1 -1
  33. parqv-0.2.0.dist-info/RECORD +0 -17
  34. {parqv-0.2.0.dist-info → parqv-0.3.0.dist-info}/entry_points.txt +0 -0
  35. {parqv-0.2.0.dist-info → parqv-0.3.0.dist-info}/licenses/LICENSE +0 -0
  36. {parqv-0.2.0.dist-info → parqv-0.3.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,220 @@
1
+ """
2
+ Statistics formatting utilities for parqv views.
3
+ """
4
+
5
+ from typing import Any, Dict, List, Union
6
+
7
+ from rich.text import Text
8
+
9
+ from .visualization import create_text_histogram, should_show_histogram
10
+
11
+
12
+ def format_stats_for_display(stats_data: Dict[str, Any]) -> List[Union[str, Text]]:
13
+ """
14
+ Format statistics dictionary for display as lines of rich text.
15
+
16
+ Args:
17
+ stats_data: Raw statistics dictionary from handler
18
+
19
+ Returns:
20
+ List of formatted lines ready for display
21
+ """
22
+ if not stats_data:
23
+ return [Text.from_markup("[red]No statistics data available.[/red]")]
24
+
25
+ lines: List[Union[str, Text]] = []
26
+
27
+ # Extract basic column information
28
+ col_name = stats_data.get("column", "N/A")
29
+ col_type = stats_data.get("type", "Unknown")
30
+ nullable_val = stats_data.get("nullable")
31
+
32
+ # Format column header
33
+ lines.extend(_format_column_header(col_name, col_type, nullable_val))
34
+
35
+ # Handle calculation errors
36
+ calc_error = stats_data.get("error")
37
+ if calc_error:
38
+ lines.extend(_format_error_section(calc_error))
39
+
40
+ # Add informational messages
41
+ message = stats_data.get("message")
42
+ if message:
43
+ lines.extend(_format_message_section(message))
44
+
45
+ # Format calculated statistics
46
+ calculated = stats_data.get("calculated")
47
+ if calculated:
48
+ lines.extend(_format_calculated_stats(calculated, has_error=bool(calc_error)))
49
+
50
+ return lines
51
+
52
+
53
+ def format_column_info(column_name: str, column_type: str, nullable: Any) -> List[Union[str, Text]]:
54
+ """
55
+ Format basic column information for display.
56
+
57
+ Args:
58
+ column_name: Name of the column
59
+ column_type: Type of the column
60
+ nullable: Nullability information
61
+
62
+ Returns:
63
+ List of formatted lines for column info
64
+ """
65
+ return _format_column_header(column_name, column_type, nullable)
66
+
67
+
68
+ def _format_column_header(col_name: str, col_type: str, nullable_val: Any) -> List[Union[str, Text]]:
69
+ """Format the column header section."""
70
+ # Determine nullability display
71
+ if nullable_val is True:
72
+ nullable_str = "Nullable"
73
+ elif nullable_val is False:
74
+ nullable_str = "Required"
75
+ else:
76
+ nullable_str = "Unknown Nullability"
77
+
78
+ lines = [
79
+ Text.assemble(("Column: ", "bold"), f"`{col_name}`"),
80
+ Text.assemble(("Type: ", "bold"), f"{col_type} ({nullable_str})"),
81
+ "─" * (len(col_name) + len(col_type) + 20)
82
+ ]
83
+
84
+ return lines
85
+
86
+
87
+ def _format_error_section(calc_error: str) -> List[Union[str, Text]]:
88
+ """Format the error section."""
89
+ return [
90
+ Text("Calculation Error:", style="bold red"),
91
+ f"```\n{calc_error}\n```",
92
+ ""
93
+ ]
94
+
95
+
96
+ def _format_message_section(message: str) -> List[Union[str, Text]]:
97
+ """Format the informational message section."""
98
+ return [
99
+ Text(f"Info: {message}", style="italic cyan"),
100
+ ""
101
+ ]
102
+
103
+
104
+ def _format_calculated_stats(calculated: Dict[str, Any], has_error: bool = False) -> List[Union[str, Text]]:
105
+ """Format the calculated statistics section."""
106
+ lines = [Text("Calculated Statistics:", style="bold")]
107
+
108
+ # Define the order of statistics to display
109
+ stats_order = [
110
+ "Total Count", "Valid Count", "Null Count", "Null Percentage",
111
+ "Distinct Count", "Distinct Values (Approx)",
112
+ "Min", "Max", "Mean", "Median (50%)", "StdDev", "Variance",
113
+ "True Count", "False Count",
114
+ "Value Counts"
115
+ ]
116
+
117
+ found_stats = False
118
+
119
+ for key in stats_order:
120
+ if key in calculated:
121
+ found_stats = True
122
+ value = calculated[key]
123
+ lines.extend(_format_single_stat(key, value))
124
+
125
+ # Add any additional stats not in the predefined order (excluding internal histogram data)
126
+ for key, value in calculated.items():
127
+ if key not in stats_order and not key.startswith('_'): # Skip internal fields
128
+ found_stats = True
129
+ lines.extend(_format_single_stat(key, value))
130
+
131
+ # Handle case where no stats were found
132
+ if not found_stats and not has_error:
133
+ lines.append(Text(" (No specific stats calculated for this type)", style="dim"))
134
+
135
+ # Add histogram visualization for numeric data
136
+ if "_histogram_data" in calculated and "_data_type" in calculated:
137
+ if calculated["_data_type"] == "numeric":
138
+ lines.extend(_format_histogram_visualization(calculated))
139
+
140
+ return lines
141
+
142
+
143
+ def _format_single_stat(key: str, value: Any) -> List[Union[str, Text]]:
144
+ """Format a single statistic entry."""
145
+ lines = []
146
+
147
+ if key == "Value Counts" and isinstance(value, dict):
148
+ lines.append(f" - {key}:")
149
+ for sub_key, sub_val in value.items():
150
+ sub_val_str = _format_stat_value(sub_val)
151
+ lines.append(f" - {sub_key}: {sub_val_str}")
152
+ else:
153
+ formatted_value = _format_stat_value(value)
154
+ lines.append(f" - {key}: {formatted_value}")
155
+
156
+ return lines
157
+
158
+
159
+ def _format_stat_value(value: Any) -> str:
160
+ """Format a single statistic value."""
161
+ if isinstance(value, (int, float)):
162
+ if isinstance(value, int):
163
+ return f"{value:,}"
164
+ else:
165
+ return f"{value:,.4f}"
166
+ else:
167
+ return str(value)
168
+
169
+
170
+ def _format_histogram_visualization(calculated: Dict[str, Any]) -> List[Union[str, Text]]:
171
+ """Format histogram visualization for numeric data."""
172
+ lines = []
173
+
174
+ try:
175
+ histogram_data = calculated.get("_histogram_data", [])
176
+ if not histogram_data:
177
+ return lines
178
+
179
+ # Check if we should show histogram
180
+ distinct_count_str = calculated.get("Distinct Count", "0")
181
+ try:
182
+ # Remove commas and convert to int
183
+ distinct_count = int(distinct_count_str.replace(",", ""))
184
+ except (ValueError, AttributeError):
185
+ distinct_count = len(set(histogram_data))
186
+
187
+ total_count = len(histogram_data)
188
+
189
+ if should_show_histogram("numeric", distinct_count, total_count):
190
+ lines.append("")
191
+ lines.append(Text("Data Distribution:", style="bold cyan"))
192
+
193
+ # Create histogram
194
+ histogram_lines = create_text_histogram(
195
+ data=histogram_data,
196
+ bins=15,
197
+ width=50,
198
+ height=8,
199
+ title=None
200
+ )
201
+
202
+ # Add each histogram line
203
+ for line in histogram_lines:
204
+ if isinstance(line, str):
205
+ lines.append(f" {line}")
206
+ else:
207
+ lines.append(line)
208
+ else:
209
+ # For discrete data, show a note
210
+ if distinct_count < total_count * 0.1: # Less than 10% unique values
211
+ lines.append("")
212
+ lines.append(Text("Note: Data appears to be discrete/categorical", style="dim italic"))
213
+ lines.append(Text("(Histogram not shown for discrete values)", style="dim italic"))
214
+
215
+ except Exception as e:
216
+ # Don't fail the whole stats display if histogram fails
217
+ lines.append("")
218
+ lines.append(Text(f"Note: Could not generate histogram: {e}", style="dim red"))
219
+
220
+ return lines
@@ -0,0 +1,204 @@
1
+ """
2
+ Visualization utilities for parqv views.
3
+
4
+ Provides text-based data visualization functions like ASCII histograms.
5
+ """
6
+ import math
7
+ from typing import List, Union, Optional
8
+
9
+ TICK_CHARS = [' ', '▂', '▃', '▄', '▅', '▆', '▇', '█']
10
+
11
+
12
+ def create_text_histogram(
13
+ data: List[Union[int, float]],
14
+ bins: int = 15,
15
+ width: int = 60,
16
+ height: int = 8,
17
+ title: Optional[str] = None
18
+ ) -> List[str]:
19
+ """
20
+ Create a professional, text-based histogram from numerical data.
21
+
22
+ Args:
23
+ data: List of numerical values.
24
+ bins: The number of bins for the histogram.
25
+ width: The total character width of the output histogram.
26
+ height: The maximum height of the histogram bars in lines.
27
+ title: An optional title for the histogram.
28
+
29
+ Returns:
30
+ A list of strings representing the histogram, ready for printing.
31
+ """
32
+ if not data:
33
+ return ["(No data available for histogram)"]
34
+
35
+ # 1. Sanitize the input data
36
+ clean_data = [float(val) for val in data if isinstance(val, (int, float)) and math.isfinite(val)]
37
+
38
+ if not clean_data:
39
+ return ["(No valid numerical data to plot)"]
40
+
41
+ min_val, max_val = min(clean_data), max(clean_data)
42
+
43
+ if min_val == max_val:
44
+ return [f"(All values are identical: {_format_number(min_val)})"]
45
+
46
+ # 2. Create bins and count frequencies
47
+ # Add a small epsilon to the range to ensure max_val falls into the last bin
48
+ epsilon = (max_val - min_val) / 1e9
49
+ value_range = (max_val - min_val) + epsilon
50
+ bin_width = value_range / bins
51
+
52
+ bin_counts = [0] * bins
53
+ for value in clean_data:
54
+ bin_index = int((value - min_val) / bin_width)
55
+ bin_counts[bin_index] += 1
56
+
57
+ # 3. Render the histogram
58
+ return _render_histogram(
59
+ bin_counts=bin_counts,
60
+ min_val=min_val,
61
+ max_val=max_val,
62
+ width=width,
63
+ height=height,
64
+ title=title
65
+ )
66
+
67
+
68
+ def _render_histogram(
69
+ bin_counts: List[int],
70
+ min_val: float,
71
+ max_val: float,
72
+ width: int,
73
+ height: int,
74
+ title: Optional[str]
75
+ ) -> List[str]:
76
+ """
77
+ Internal function to render the histogram components into ASCII art.
78
+ """
79
+ lines = []
80
+ if title:
81
+ lines.append(title.center(width))
82
+
83
+ max_count = max(bin_counts) if bin_counts else 0
84
+ if max_count == 0:
85
+ return lines + ["(No data falls within histogram bins)"]
86
+
87
+ # --- Layout Calculations ---
88
+ y_axis_width = len(str(max_count))
89
+ plot_width = width - y_axis_width - 3 # Reserve space for "| " and axis
90
+ if plot_width <= 0:
91
+ return ["(Terminal width too narrow to draw histogram)"]
92
+
93
+ # Resample the data bins to fit the available plot_width.
94
+ # This stretches or shrinks the histogram to match the screen space.
95
+ display_bins = []
96
+ num_data_bins = len(bin_counts)
97
+ for i in range(plot_width):
98
+ # Find the corresponding data bin for this screen column
99
+ data_bin_index = int(i * num_data_bins / plot_width)
100
+ display_bins.append(bin_counts[data_bin_index])
101
+
102
+ # --- Y-Axis and Bars (Top to Bottom) ---
103
+ for row in range(height, -1, -1):
104
+ line = ""
105
+ # Y-axis labels
106
+ if row == height:
107
+ line += f"{max_count:<{y_axis_width}} | "
108
+ elif row == 0:
109
+ line += f"{0:<{y_axis_width}} +-"
110
+ else:
111
+ line += " " * y_axis_width + " | "
112
+
113
+ # Bars - now iterate over the resampled display_bins
114
+ for count in display_bins:
115
+ # Scale current count to the available height
116
+ scaled_height = (count / max_count) * height
117
+
118
+ # Determine character based on height relative to current row
119
+ if scaled_height >= row:
120
+ line += TICK_CHARS[-1] # Full block for the solid part of the bar
121
+ elif scaled_height > row - 1:
122
+ # This is the top of the bar, use a partial character
123
+ partial_index = int((scaled_height - row + 1) * (len(TICK_CHARS) - 1))
124
+ line += TICK_CHARS[max(0, partial_index)]
125
+ elif row == 0:
126
+ line += "-" # X-axis line
127
+ else:
128
+ line += " " # Empty space above the bar
129
+
130
+ lines.append(line)
131
+
132
+ # --- X-Axis Labels ---
133
+ x_axis_labels = _create_x_axis_labels(min_val, max_val, plot_width)
134
+ label_line = " " * (y_axis_width + 3) + x_axis_labels
135
+ lines.append(label_line)
136
+
137
+ return lines
138
+
139
+
140
+ def _create_x_axis_labels(min_val: float, max_val: float, plot_width: int) -> str:
141
+ """Create a formatted string for the X-axis labels."""
142
+ min_label = _format_number(min_val)
143
+ max_label = _format_number(max_val)
144
+
145
+ available_width = plot_width - len(min_label) - len(max_label)
146
+
147
+ if available_width < 4:
148
+ return f"{min_label}{' ' * (plot_width - len(min_label) - len(max_label))}{max_label}"
149
+
150
+ mid_val = (min_val + max_val) / 2
151
+ mid_label = _format_number(mid_val)
152
+
153
+ spacing1 = (plot_width // 2) - len(min_label) - (len(mid_label) // 2)
154
+ spacing2 = (plot_width - (plot_width // 2)) - (len(mid_label) - (len(mid_label) // 2)) - len(max_label)
155
+
156
+ if spacing1 < 1 or spacing2 < 1:
157
+ return f"{min_label}{' ' * (plot_width - len(min_label) - len(max_label))}{max_label}"
158
+
159
+ return f"{min_label}{' ' * spacing1}{mid_label}{' ' * spacing2}{max_label}"
160
+
161
+
162
+ def _format_number(value: float) -> str:
163
+ """Format a number nicely for display on an axis."""
164
+ if abs(value) < 1e-4 and value != 0:
165
+ return f"{value:.1e}"
166
+ if abs(value) >= 1e5:
167
+ return f"{value:.1e}"
168
+ if math.isclose(value, int(value)):
169
+ return str(int(value))
170
+ if abs(value) < 10:
171
+ return f"{value:.2f}"
172
+ if abs(value) < 100:
173
+ return f"{value:.1f}"
174
+ return str(int(value))
175
+
176
+
177
+ def should_show_histogram(data_type: str, distinct_count: int, total_count: int) -> bool:
178
+ """
179
+ Determine if a histogram should be shown for this data.
180
+ This function uses a set of heuristics to decide if the data is
181
+ continuous enough to warrant a histogram visualization.
182
+ """
183
+ # 1. Type Check: Histograms are only meaningful for numeric data.
184
+ if 'numeric' not in data_type and 'integer' not in data_type and 'float' not in data_type:
185
+ return False
186
+
187
+ # 2. Data Volume Check: Don't render if there's too little data or no variation.
188
+ if total_count < 20 or distinct_count <= 1:
189
+ return False
190
+
191
+ # 3. Categorical Data Filter: If the number of distinct values is very low,
192
+ # treat it as categorical data (e.g., ratings from 1-10, months 1-12).
193
+ if distinct_count < 15:
194
+ return False
195
+
196
+ # 4. High Cardinality Filter: If almost every value is unique (like an ID or index),
197
+ # a histogram is not useful as most bars would have a height of 1.
198
+ distinct_ratio = distinct_count / total_count
199
+ if distinct_ratio > 0.95:
200
+ return False
201
+
202
+ # 5. Pass: If the data passes all the above filters, it is considered
203
+ # sufficiently continuous to be visualized with a histogram.
204
+ return True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: parqv
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: An interactive Python TUI for visualizing, exploring, and analyzing files directly in your terminal.
5
5
  Author-email: Sangmin Yoon <sanspareilsmyn@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -23,14 +23,13 @@ Dynamic: license-file
23
23
 
24
24
  ---
25
25
 
26
- **Supported File Formats:** ✅ **Parquet** | ✅ **JSON** / **JSON Lines (ndjson)** | *(More planned!)*
26
+ **Supported File Formats:** ✅ **Parquet** | ✅ **JSON** / **JSON Lines (ndjson)** | ✅ **CSV / TSV** | *(More planned!)*
27
27
 
28
28
  ---
29
29
 
30
- **`parqv` is a Python-based interactive TUI (Text User Interface) tool designed to explore, analyze, and understand various data file formats directly within your terminal.** Initially supporting Parquet and JSON, `parqv` aims to provide a unified, visual experience for quick data inspection without leaving your console.
31
-
32
- ## 💻 Demo (Showing Parquet)
30
+ **`parqv` is a Python-based interactive TUI (Text User Interface) tool designed to explore, analyze, and understand various data file formats directly within your terminal.** `parqv` aims to provide a unified, visual experience for quick data inspection without leaving your console.
33
31
 
32
+ ## 💻 Demo
34
33
  ![parqv.gif](assets/parqv.gif)
35
34
  *(Demo shows Parquet features; UI adapts for other formats)*
36
35
 
@@ -47,7 +46,7 @@ Dynamic: license-file
47
46
  * **🔌 Extensible:** Designed with a handler interface to easily add support for more file formats in the future (like CSV, Arrow IPC, etc.).
48
47
 
49
48
  ## ✨ Features (TUI Mode)
50
- * **Multi-Format Support:** Currently supports **Parquet** (`.parquet`) and **JSON/JSON Lines** (`.json`, `.ndjson`). Run `parqv <your_file.{parquet,json,ndjson}>`.
49
+ * **Multi-Format Support:** Now supports **Parquet** (`.parquet`), **JSON/JSON Lines** (`.json`, `.ndjson`), and **CSV/TSV** (`.csv`, `.tsv`). Run `parqv <your_file.{parquet,json,ndjson,csv,tsv}>`.
51
50
  * **Metadata Panel:** Displays key file information (path, format, size, total rows, column count, etc.). *Fields may vary slightly depending on the file format.*
52
51
  * **Schema Explorer:**
53
52
  * Interactive list view of columns.
@@ -0,0 +1,36 @@
1
+ parqv/__init__.py,sha256=CcARikIb8knQqd3bGu6Y9exgSbzdywjdORz15VKKxmU,611
2
+ parqv/app.py,sha256=KXfL-RfMOOngzxn0uEeWa5UQgHRISBBl1IHK0ffQlzI,5556
3
+ parqv/cli.py,sha256=9KPOYywA53vUVp_5RI2lFgKtZay2EHosOsTo-sz4rOU,3242
4
+ parqv/parqv.css,sha256=C42ZXUwMX1ZXfGo0AmixbHxz0CWKzWBHZ_hkhq5aehg,2920
5
+ parqv/core/__init__.py,sha256=C8P-wqP72hk54qcjRNjDQ9X4hTu9eRttAyVynCAeDQw,791
6
+ parqv/core/config.py,sha256=HgKUcfs3gEDyRrNS79jiIhI_DOfXCUK7sN77rVuNOlA,561
7
+ parqv/core/file_utils.py,sha256=m7d8wD9nxXCmfUU1b6IGBqjW0mBL2Nr40r9M8NI67FY,2374
8
+ parqv/core/handler_factory.py,sha256=foVrcvcV2gWuatD_d11PUcGIKjnKLj-FMBrZff1qm3I,3074
9
+ parqv/core/logging.py,sha256=fom0zPykBgn7zpW_vlvaCDlCERI1lccdjtBRMES94-A,1173
10
+ parqv/data_sources/__init__.py,sha256=EGjRMSPeMzLPcdMdZ3axZzUfagg6PTWhyNLwLoQYuqE,951
11
+ parqv/data_sources/base/__init__.py,sha256=Alo4tGZJdUsfvGvDgpRodur3xn-SO7hgeODmhx6tA6U,524
12
+ parqv/data_sources/base/exceptions.py,sha256=QjTQqPw_LgEYchN2OjWCOkUuRV-Sn9I1J3gmoiS4ekA,802
13
+ parqv/data_sources/base/handler.py,sha256=I0Hmsf-WYhWyLGUHEV902iPS7Gg4dDkRPgyXOfGZSTQ,4429
14
+ parqv/data_sources/formats/__init__.py,sha256=825j-DwYpc-9nSKKhaAo-G72DK-L2AyjB6_HCLVDQpI,416
15
+ parqv/data_sources/formats/csv.py,sha256=SFIyQII4DEH8_2SlHLhR9cwzIySkopu48N85msNHC-E,18387
16
+ parqv/data_sources/formats/json.py,sha256=9pVnKbtK1MEuZCT1UbUbWnFJSi0GnH6Bnf0yhqgH8es,21570
17
+ parqv/data_sources/formats/parquet.py,sha256=J-oaxCojTz3590asPV59V_SK0LcIiYRrKxnVAm7WshQ,32821
18
+ parqv/views/__init__.py,sha256=aQAsH9akPMBVf7F0KNiELAtb-yoThQsR41nXk3G_mbQ,943
19
+ parqv/views/base.py,sha256=9kRsUjTXdxEoiy9gSlNmU1ksZsN7FkyXWepX4C0vBFQ,2948
20
+ parqv/views/data_view.py,sha256=Xk7XdXCN82AmH3hyrmjDAIGtc212zc1lrqE_2ijWKeY,4344
21
+ parqv/views/metadata_view.py,sha256=6qdLEyO0te1TMiVtTxq-L3PWTOxCtFkkrDH_4yaXPaw,2058
22
+ parqv/views/schema_view.py,sha256=r1ZHJZ7g20KT_EfGrh71AU0-gNj1Z4Z-WUDQCwOu3BY,8966
23
+ parqv/views/components/__init__.py,sha256=rBsX9UH67GCLn275LhG2Xyf84gRtrm-otuj6zW5JAiU,267
24
+ parqv/views/components/enhanced_data_table.py,sha256=57pJD9rT-kiy2llw4p5pZOReUtv5CZxN-2tD6e7QDaI,4952
25
+ parqv/views/components/error_display.py,sha256=Ak1AbT9dikkF3izVtVFxdRB0nas6jqjb5tPG3l-_JjQ,2444
26
+ parqv/views/components/loading_display.py,sha256=1lLxvt5oMBGDiojODTVQ6O_hVNFHCWqpWamk863BMkY,1440
27
+ parqv/views/utils/__init__.py,sha256=3ZmzT2X0XC19Bp9MJOeWGpvWUQ6b_2xNjzI4WcxBARQ,515
28
+ parqv/views/utils/data_formatters.py,sha256=NDm0UhvfAGq5P0ovWL5GSchDG_YZOyXlTmhcifo_BjQ,5324
29
+ parqv/views/utils/stats_formatters.py,sha256=ZKrCPcs29lKAJ-w_cwpShvrVNWV6CY92LJOLrBKKP7U,7160
30
+ parqv/views/utils/visualization.py,sha256=JXe_gOk0mFvHXKAofT84ggQCIBLbBUckq0oolP9OaEk,7064
31
+ parqv-0.3.0.dist-info/licenses/LICENSE,sha256=Ewl2wCa8r6ncxHlpf-ZZXb77c82zdfxHuEeKzBbm6nM,11324
32
+ parqv-0.3.0.dist-info/METADATA,sha256=B3F_GkB224DykJkXZ0VzAc9v4beviGn2JBuwANqRksY,5382
33
+ parqv-0.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ parqv-0.3.0.dist-info/entry_points.txt,sha256=8Tm8rTiIB-tbVItoOA4M7seEmFnrtK25BMH9UKzqfXg,44
35
+ parqv-0.3.0.dist-info/top_level.txt,sha256=_t3_49ZluJbvl0QU_P3GNVuXxCffqiTp37dzZIa2GEw,6
36
+ parqv-0.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,17 +0,0 @@
1
- parqv/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- parqv/app.py,sha256=QmGEBtHoASdNzDmSgLQ2BvpTnZOuxQj2l0QGhYfp48w,6476
3
- parqv/parqv.css,sha256=C42ZXUwMX1ZXfGo0AmixbHxz0CWKzWBHZ_hkhq5aehg,2920
4
- parqv/handlers/__init__.py,sha256=gplqWWpt0npYosDZfoX1Ek0sfvAD0YITMjlQEo-IYVc,343
5
- parqv/handlers/base_handler.py,sha256=JjlI0QdUZmozaMhAZnhzzS8nC2J63QRDX3rZqzXOpW8,3662
6
- parqv/handlers/json.py,sha256=KgujRGimqzE3kVGQFwfkCb2Yv3YWxB8w-b17q5X2Yj8,19794
7
- parqv/handlers/parquet.py,sha256=kG46oeVONmi6cCVzf5MaF88PkmXP7jCEGIAlDkG6N2M,32221
8
- parqv/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- parqv/views/data_view.py,sha256=T_LPbXdxm_KOmwtTQWAxlUeNTlSGfIgQIg853WDMAME,2355
10
- parqv/views/metadata_view.py,sha256=GnzhPJolMDZU8AAwY0h_uToz1sdM54iJbM2GLkD3caI,1013
11
- parqv/views/schema_view.py,sha256=3QUYlOxTk9DYKdipEmt79U6hSlNJa_j3a3SK9UNwWHk,11332
12
- parqv-0.2.0.dist-info/licenses/LICENSE,sha256=Ewl2wCa8r6ncxHlpf-ZZXb77c82zdfxHuEeKzBbm6nM,11324
13
- parqv-0.2.0.dist-info/METADATA,sha256=q1K9Vqntt70-ffbJ3eT2g8_TW4XABsFHCkdYbBF4nXM,5387
14
- parqv-0.2.0.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
15
- parqv-0.2.0.dist-info/entry_points.txt,sha256=8Tm8rTiIB-tbVItoOA4M7seEmFnrtK25BMH9UKzqfXg,44
16
- parqv-0.2.0.dist-info/top_level.txt,sha256=_t3_49ZluJbvl0QU_P3GNVuXxCffqiTp37dzZIa2GEw,6
17
- parqv-0.2.0.dist-info/RECORD,,