dataframe-textual 2.5.0__tar.gz → 2.6.0__tar.gz
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.
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/PKG-INFO +3 -2
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/README.md +2 -1
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/pyproject.toml +1 -1
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/data_frame_table.py +95 -51
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/uv.lock +1 -1
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/.gitignore +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/1811.csv.gz +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/LICENSE +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/large_malformed.tsv.gz +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/main.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/__init__.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/__main__.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/common.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/data_frame_help_panel.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/data_frame_viewer.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/sql_screen.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/table_screen.py +0 -0
- {dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/yes_no_screen.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dataframe-textual
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Interactive terminal viewer/editor for tabular data
|
|
5
5
|
Project-URL: Homepage, https://github.com/need47/dataframe-textual
|
|
6
6
|
Project-URL: Repository, https://github.com/need47/dataframe-textual.git
|
|
@@ -314,7 +314,8 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
314
314
|
| Key | Action |
|
|
315
315
|
|-----|--------|
|
|
316
316
|
| `Double-click` | Edit cell or rename column header |
|
|
317
|
-
| `
|
|
317
|
+
| `Delete` | Clear current cell (set to NULL) |
|
|
318
|
+
| `Shift+Delete` | Clear current column (set matching cells to NULL) |
|
|
318
319
|
| `e` | Edit current cell (respects data type) |
|
|
319
320
|
| `E` | Edit entire column with value/expression |
|
|
320
321
|
| `a` | Add empty column after current |
|
|
@@ -275,7 +275,8 @@ zcat compressed_data.csv.gz | dv -f csv
|
|
|
275
275
|
| Key | Action |
|
|
276
276
|
|-----|--------|
|
|
277
277
|
| `Double-click` | Edit cell or rename column header |
|
|
278
|
-
| `
|
|
278
|
+
| `Delete` | Clear current cell (set to NULL) |
|
|
279
|
+
| `Shift+Delete` | Clear current column (set matching cells to NULL) |
|
|
279
280
|
| `e` | Edit current cell (respects data type) |
|
|
280
281
|
| `E` | Edit entire column with value/expression |
|
|
281
282
|
| `a` | Add empty column after current |
|
{dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/data_frame_table.py
RENAMED
|
@@ -207,6 +207,7 @@ class DataFrameTable(DataTable):
|
|
|
207
207
|
- **X** - ❌ Delete row and those below
|
|
208
208
|
- **Ctrl+X** - ❌ Delete row and those above
|
|
209
209
|
- **delete** - ❌ Clear current cell (set to NULL)
|
|
210
|
+
- **Shift+Delete** - ❌ Clear current column (set matching cells to NULL)
|
|
210
211
|
- **-** - ❌ Delete current column
|
|
211
212
|
- **d** - 📋 Duplicate current column
|
|
212
213
|
- **D** - 📋 Duplicate current row
|
|
@@ -285,6 +286,7 @@ class DataFrameTable(DataTable):
|
|
|
285
286
|
("R", "replace_global", "Replace global"), # `Shift+R`
|
|
286
287
|
# Delete
|
|
287
288
|
("delete", "clear_cell", "Clear cell"),
|
|
289
|
+
("shift+delete", "clear_column", "Clear cells in current column that match cursor value"), # `Shift+Delete`
|
|
288
290
|
("minus", "delete_column", "Delete column"), # `-`
|
|
289
291
|
("x", "delete_row", "Delete row"),
|
|
290
292
|
("X", "delete_row_and_below", "Delete row and those below"),
|
|
@@ -795,6 +797,10 @@ class DataFrameTable(DataTable):
|
|
|
795
797
|
"""Clear the current cell (set to None)."""
|
|
796
798
|
self.do_clear_cell()
|
|
797
799
|
|
|
800
|
+
def action_clear_column(self) -> None:
|
|
801
|
+
"""Clear cells in the current column that match the cursor value."""
|
|
802
|
+
self.do_clear_column()
|
|
803
|
+
|
|
798
804
|
def action_select_row(self) -> None:
|
|
799
805
|
"""Select rows with cursor value in the current column."""
|
|
800
806
|
self.do_select_row()
|
|
@@ -2043,25 +2049,10 @@ class DataFrameTable(DataTable):
|
|
|
2043
2049
|
# Also update the view if applicable
|
|
2044
2050
|
# Update the value of col_name in df_view using the value of col_name from df based on RID mapping between them
|
|
2045
2051
|
if self.df_view is not None:
|
|
2046
|
-
# Get updated column from df
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
RID, pl.col(col_name).alias(col_updated), pl.lit(True).alias(col_exists)
|
|
2051
|
-
)
|
|
2052
|
-
# Join and use when/then/otherwise to handle all updates including NULLs
|
|
2053
|
-
self.df_view = (
|
|
2054
|
-
self.df_view.lazy()
|
|
2055
|
-
.join(lf_updated, on=RID, how="left")
|
|
2056
|
-
.with_columns(
|
|
2057
|
-
pl.when(pl.col(col_exists))
|
|
2058
|
-
.then(pl.col(col_updated))
|
|
2059
|
-
.otherwise(pl.col(col_name))
|
|
2060
|
-
.alias(col_name)
|
|
2061
|
-
)
|
|
2062
|
-
.drop(col_updated, col_exists)
|
|
2063
|
-
.collect()
|
|
2064
|
-
)
|
|
2052
|
+
# Get updated column from df
|
|
2053
|
+
lf_updated = self.df.lazy().select(RID, pl.col(col_name))
|
|
2054
|
+
# Update df_view by joining on RID
|
|
2055
|
+
self.df_view = self.df_view.lazy().update(lf_updated, on=RID, include_nulls=True).collect()
|
|
2065
2056
|
except Exception as e:
|
|
2066
2057
|
self.notify(
|
|
2067
2058
|
f"Error applying expression: [$error]{term}[/] to column [$accent]{col_name}[/]",
|
|
@@ -2147,18 +2138,26 @@ class DataFrameTable(DataTable):
|
|
|
2147
2138
|
|
|
2148
2139
|
# Update the cell to None in the dataframe
|
|
2149
2140
|
try:
|
|
2150
|
-
self.df =
|
|
2151
|
-
|
|
2152
|
-
.
|
|
2153
|
-
|
|
2154
|
-
|
|
2141
|
+
self.df = (
|
|
2142
|
+
self.df.lazy()
|
|
2143
|
+
.with_columns(
|
|
2144
|
+
pl.when(pl.arange(0, len(self.df)) == ridx)
|
|
2145
|
+
.then(pl.lit(None))
|
|
2146
|
+
.otherwise(pl.col(col_name))
|
|
2147
|
+
.alias(col_name)
|
|
2148
|
+
)
|
|
2149
|
+
.collect()
|
|
2155
2150
|
)
|
|
2156
2151
|
|
|
2157
2152
|
# Also update the view if applicable
|
|
2158
2153
|
if self.df_view is not None:
|
|
2159
2154
|
ridx_view = self.df.item(ridx, self.df.columns.index(RID))
|
|
2160
|
-
self.df_view =
|
|
2161
|
-
|
|
2155
|
+
self.df_view = (
|
|
2156
|
+
self.df_view.lazy()
|
|
2157
|
+
.with_columns(
|
|
2158
|
+
pl.when(pl.col(RID) == ridx_view).then(pl.lit(None)).otherwise(pl.col(col_name)).alias(col_name)
|
|
2159
|
+
)
|
|
2160
|
+
.collect()
|
|
2162
2161
|
)
|
|
2163
2162
|
|
|
2164
2163
|
# Update the display
|
|
@@ -2177,7 +2176,44 @@ class DataFrameTable(DataTable):
|
|
|
2177
2176
|
timeout=10,
|
|
2178
2177
|
)
|
|
2179
2178
|
self.log(f"Error clearing cell ({ridx}, {col_name}): {str(e)}")
|
|
2180
|
-
|
|
2179
|
+
|
|
2180
|
+
def do_clear_column(self) -> None:
|
|
2181
|
+
"""Clear the current column by setting all its values to None."""
|
|
2182
|
+
col_idx = self.cursor_column
|
|
2183
|
+
col_name = self.cursor_col_name
|
|
2184
|
+
value = self.cursor_value
|
|
2185
|
+
|
|
2186
|
+
# Add to history
|
|
2187
|
+
self.add_history(f"Cleared column [$success]{col_name}[/]", dirty=True)
|
|
2188
|
+
|
|
2189
|
+
try:
|
|
2190
|
+
# Update the entire column to None in the dataframe
|
|
2191
|
+
self.df = (
|
|
2192
|
+
self.df.lazy()
|
|
2193
|
+
.with_columns(
|
|
2194
|
+
pl.when(pl.col(col_name) == value).then(pl.lit(None)).otherwise(pl.col(col_name)).alias(col_name)
|
|
2195
|
+
)
|
|
2196
|
+
.collect()
|
|
2197
|
+
)
|
|
2198
|
+
|
|
2199
|
+
# Also update the view if applicable
|
|
2200
|
+
if self.df_view is not None:
|
|
2201
|
+
self.df_view = self.df_view.with_columns(
|
|
2202
|
+
pl.when(pl.col(col_name) == value).then(pl.lit(None)).otherwise(pl.col(col_name)).alias(col_name)
|
|
2203
|
+
)
|
|
2204
|
+
|
|
2205
|
+
# Recreate table for display
|
|
2206
|
+
self.setup_table()
|
|
2207
|
+
|
|
2208
|
+
# Move cursor to the cleared column
|
|
2209
|
+
self.move_cursor(column=col_idx)
|
|
2210
|
+
|
|
2211
|
+
# self.notify(f"Cleared column [$success]{col_name}[/]", title="Clear Column")
|
|
2212
|
+
except Exception as e:
|
|
2213
|
+
self.notify(
|
|
2214
|
+
f"Error clearing column [$error]{col_name}[/]", title="Clear Column", severity="error", timeout=10
|
|
2215
|
+
)
|
|
2216
|
+
self.log(f"Error clearing column `{col_name}`: {str(e)}")
|
|
2181
2217
|
|
|
2182
2218
|
def do_add_column(self, col_name: str = None) -> None:
|
|
2183
2219
|
"""Add acolumn after the current column."""
|
|
@@ -3385,18 +3421,23 @@ class DataFrameTable(DataTable):
|
|
|
3385
3421
|
# Only applicable to string columns for substring matches
|
|
3386
3422
|
if dtype == pl.String and not state.match_whole:
|
|
3387
3423
|
term_find = f"(?i){state.term_find}" if state.match_nocase else state.term_find
|
|
3424
|
+
new_value = (
|
|
3425
|
+
pl.lit(None)
|
|
3426
|
+
if state.term_replace == NULL
|
|
3427
|
+
else pl.col(col_name).str.replace_all(term_find, state.term_replace)
|
|
3428
|
+
)
|
|
3388
3429
|
self.df = self.df.with_columns(
|
|
3389
|
-
pl.when(mask)
|
|
3390
|
-
.then(pl.col(col_name).str.replace_all(term_find, state.term_replace))
|
|
3391
|
-
.otherwise(pl.col(col_name))
|
|
3392
|
-
.alias(col_name)
|
|
3430
|
+
pl.when(mask).then(new_value).otherwise(pl.col(col_name)).alias(col_name)
|
|
3393
3431
|
)
|
|
3394
3432
|
else:
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3433
|
+
if state.term_replace == NULL:
|
|
3434
|
+
value = None
|
|
3435
|
+
else:
|
|
3436
|
+
# Try to convert replacement value to column dtype
|
|
3437
|
+
try:
|
|
3438
|
+
value = DtypeConfig(dtype).convert(state.term_replace)
|
|
3439
|
+
except Exception:
|
|
3440
|
+
value = state.term_replace
|
|
3400
3441
|
|
|
3401
3442
|
self.df = self.df.with_columns(
|
|
3402
3443
|
pl.when(mask).then(pl.lit(value)).otherwise(pl.col(col_name)).alias(col_name)
|
|
@@ -3404,15 +3445,8 @@ class DataFrameTable(DataTable):
|
|
|
3404
3445
|
|
|
3405
3446
|
# Also update the view if applicable
|
|
3406
3447
|
if self.df_view is not None:
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
self.df_view = (
|
|
3410
|
-
self.df_view.lazy()
|
|
3411
|
-
.join(lf_updated, on=RID, how="left")
|
|
3412
|
-
.with_columns(pl.coalesce(pl.col(col_updated), pl.col(col_name)).alias(col_name))
|
|
3413
|
-
.drop(col_updated)
|
|
3414
|
-
.collect()
|
|
3415
|
-
)
|
|
3448
|
+
lf_updated = self.df.lazy().filter(mask).select(pl.col(RID), pl.col(col_name))
|
|
3449
|
+
self.df_view = self.df_view.lazy().update(lf_updated, on=RID, include_nulls=True).collect()
|
|
3416
3450
|
|
|
3417
3451
|
state.replaced_occurrence += len(ridxs)
|
|
3418
3452
|
|
|
@@ -3491,9 +3525,14 @@ class DataFrameTable(DataTable):
|
|
|
3491
3525
|
# Only applicable to string columns for substring matches
|
|
3492
3526
|
if dtype == pl.String and not state.match_whole:
|
|
3493
3527
|
term_find = f"(?i){state.term_find}" if state.match_nocase else state.term_find
|
|
3528
|
+
new_value = (
|
|
3529
|
+
pl.lit(None)
|
|
3530
|
+
if state.term_replace == NULL
|
|
3531
|
+
else pl.col(col_name).str.replace_all(term_find, state.term_replace)
|
|
3532
|
+
)
|
|
3494
3533
|
self.df = self.df.with_columns(
|
|
3495
3534
|
pl.when(pl.arange(0, len(self.df)) == ridx)
|
|
3496
|
-
.then(
|
|
3535
|
+
.then(new_value)
|
|
3497
3536
|
.otherwise(pl.col(col_name))
|
|
3498
3537
|
.alias(col_name)
|
|
3499
3538
|
)
|
|
@@ -3507,11 +3546,14 @@ class DataFrameTable(DataTable):
|
|
|
3507
3546
|
.alias(col_name)
|
|
3508
3547
|
)
|
|
3509
3548
|
else:
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3549
|
+
if state.term_replace == NULL:
|
|
3550
|
+
value = None
|
|
3551
|
+
else:
|
|
3552
|
+
# try to convert replacement value to column dtype
|
|
3553
|
+
try:
|
|
3554
|
+
value = DtypeConfig(dtype).convert(state.term_replace)
|
|
3555
|
+
except Exception:
|
|
3556
|
+
value = state.term_replace
|
|
3515
3557
|
|
|
3516
3558
|
self.df = self.df.with_columns(
|
|
3517
3559
|
pl.when(pl.arange(0, len(self.df)) == ridx)
|
|
@@ -3539,6 +3581,8 @@ class DataFrameTable(DataTable):
|
|
|
3539
3581
|
if not state.done:
|
|
3540
3582
|
# Get the new value of the current cell after replacement
|
|
3541
3583
|
new_cell_value = self.df.item(ridx, cidx)
|
|
3584
|
+
if new_cell_value is None:
|
|
3585
|
+
new_cell_value = NULL_DISPLAY
|
|
3542
3586
|
row_key = str(ridx)
|
|
3543
3587
|
col_key = col_name
|
|
3544
3588
|
self.update_cell(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/data_frame_help_panel.py
RENAMED
|
File without changes
|
{dataframe_textual-2.5.0 → dataframe_textual-2.6.0}/src/dataframe_textual/data_frame_viewer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|