Import and export¶
Import adds an external CSV as a new learning set. Export copies an existing
set out through the shared file picker. Validation and repair live in
data/csv_processor.py; the Import/Export screen in
ui/screens/import_export_control.py drives the dialogs.
Set schemas and storage paths are described in Data and storage. Message enums are in the Constants API.
Import flow¶
flowchart TD
Pick[FilePicker CSV] --> Validate[CSVProcessor.validate_file]
Validate -->|errors| Block[Show errors; do not import]
Validate -->|valid| Form[Title / subtitle form]
Form --> CatalogOK{files.csv valid?}
CatalogOK -->|no| Restart[Ask user to restart]
CatalogOK -->|yes| Branch{requires_specific_actions?}
Branch -->|no| SaveSimple[save_set_with_no_specific_actions]
Branch -->|yes| SaveFix[save_set_with_specific_actions]
SaveSimple --> KeepStats{has_statistics?}
SaveFix --> KeepStats
KeepStats -->|ask| Dialog[Keep progress Yes/No]
KeepStats -->|no stats| Done[Catalog row + set CSV]
Dialog --> Done
ImportExportControl picks a single .csv file with its own
FilePicker (csv_file_selector on the screen) using with_data=True so the
file contents are available even when path is None. In that case the
control materializes file.bytes into a temporary file before calling
validate_file. It then shows warnings or errors with create_alert_dialog
from ui/page_functions.py (title + message, optional second action button for
Yes/No flows such as keep-progress). On success it stores validation flags on
the control and lets the user confirm title and subtitle before save. There is
no separate API page for that helper — call it from screen code when you need
a blocking alert over Import/Export (or reuse the same pattern elsewhere).
Export is separate: tile actions use
AppSession.get_export_csv_picker() (see
State and persistence). That shared picker is a
runtime service for export only, not for choosing an import file and not a
place to hold CSV content.
Validate a set file¶
from learning_app.data.csv_processor import CSVProcessor
result = CSVProcessor.validate_file(file_path, original_name=picked_file_name)
Pass original_name (picker display name / basename) when the path may be a
temporary file (web/mobile). Suffix checks and name_suggestion use that
name, not the temp path.
validate_file returns a dict:
| Key | Meaning |
|---|---|
is_valid |
False when import must stop |
errors |
Fatal messages (Errors enum values) |
warnings |
Non-fatal messages (Warnings enum values) |
requires_specific_actions |
True when save must use the repair path |
has_statistics |
True when all stats columns look usable |
data_type |
"words" or "definitions" when detected |
name_suggestion |
Title stem only when the original name ends in _words.csv or _definitions.csv; otherwise empty |
Errors versus warnings¶
- Errors block import (
is_validbecomesFalse): not a CSV, load failure, missing required content columns, too many rows (MAX_ROWS), empty content that cannot form a set, and similar hard failures. - Warnings allow import to continue but set
requires_specific_actions: bad index column, unexpected extra columns, broken or inconsistent statistics, sparse rows that should be dropped on save, and related issues. - Missing
_words.csv/_definitions.csvon the original name also setsrequires_specific_actionsso save can allocate a correct basename, but that is not surfaced as a user-facing warning.
When is_valid is False, the UI shows the error list and does not open the
title form. When valid with warnings, the UI still proceeds and surfaces the
warning text.
Save after validation¶
Choose the save helper from the validation flags:
| Condition | Helper |
|---|---|
| Valid, no specific actions | save_set_with_no_specific_actions |
| Valid, specific actions required | save_set_with_specific_actions |
Both helpers:
- Read the picked file.
- Normalize or rebuild statistics columns according to
has_statisticsandkeep_statistics. - Rebuild a contiguous index from zero.
- Allocate a unique basename under
csv_files/viaallocate_unique_set_basename()fromdata/demo_sets.py(also used when creating a set). Catalog entries, on-disk files, and reserved demo basenames count as taken, so import/create cannot claim a demo slot. - Call
add_new_fileandsave_set.
CSVProcessor.save_set_with_no_specific_actions(
file_path,
file_name,
title,
subtitle,
has_statistics=True,
keep_statistics=True,
)
info = CSVProcessor.save_set_with_specific_actions(
file_path,
file_name,
title,
subtitle,
data_type="words",
has_statistics=True,
warnings=result["warnings"],
keep_statistics=False,
)
save_set_with_specific_actions may return an information string (for
example when statistics columns were discarded because of type errors). The
Import screen shows that text in a follow-up dialog.
Keep progress dialog¶
When has_statistics is True, the UI asks whether to keep progress. That
maps to keep_statistics=True (Yes) or False / treating the file as
without usable stats (No). Do not invent a third persistence path in screen
code; pass the flag into the matching save helper.
Catalog integrity: files.csv¶
Before adding a set, the Import screen checks the catalog:
if not CSVProcessor.validate_files_csv()["is_valid"]:
# UI asks the user to restart; navigation may stay locked
...
validate_files_csv checks existence, required columns, empty names/titles,
duplicate basenames, suffix rules, and whether listed files exist on disk.
Missing files are warnings; structural problems are errors.
repair_files_csv attempts cleanup (recover rows when pandas cannot parse
the file, add missing catalog columns or rebuild from set files on disk,
drop bad rows, fill empty subtitle cells when they are missing, remove
duplicates or missing-file entries). A catalog that cannot be read as text is left unchanged rather
than replaced with an empty file. A missing catalog is valid and is created
later by catalog readers, not by repair. Prefer calling it from maintenance
or recovery flows after understanding the validation result; the default
Import path currently asks for an app restart when the catalog is invalid
rather than auto-repairing mid-import.
Export¶
Export tiles reuse the shared export TilesContainer from
Body registry. Choosing export on a tile reads
the set CSV from application storage, then calls
AppSession.get_export_csv_picker().save_file(..., src_bytes=...) so Flet
writes or downloads the bytes (required on web and mobile; also used on
desktop). The file is the set CSV only. Per-set overrides stay on the catalog
row and are not part of the export. A SnackBar confirms success; failures still use
create_alert_dialog. Export does not re-run validate_file; the file
already lives under application storage.
Search on the Export tab¶
The Import/Export screen shows the bottom-bar search button only while the
Export tab is selected. Clicking it calls go_search(..., mode="export"),
which hosts SearchControl in the shared AppBar over the export body
(registered when ImportExportControl first builds). Do not call export
search before visiting Import/Export — see
Navigation guide and
Body registry.
Ownership checklist¶
| Concern | Owner |
|---|---|
| Validate / repair / import save | CSVProcessor |
| Unique basename (incl. reserved demos) | allocate_unique_set_basename in demo_sets |
| Dialogs and form state | ImportExportControl (+ create_alert_dialog) |
| Import file picker | ImportExportControl.csv_file_selector |
| Shared export picker | AppSession |
| Disk paths and catalog helpers | FilePathManager / app_data helpers |
Do not keep imported DataFrames or catalog rows in AppSession.