0%

Capstone: A Persistent Lending Tool · practice

Import Starter Items Without Duplicates

A starter import has two useful results: rows inserted for the first time and rows already present in exact agreement. The command must print both counts, but only after the entire CSV has been validated and the has committed.

The visible demo first initializes its temporary catalog. It then makes these exact calls:

main(["import-items", str(database_path), str(csv_path)])
main(["import-items", str(database_path), str(csv_path)])

The demo writes the exact three starter rows to csv_path inside the same temporary directory. For each call, argparse gives import_items_command a namespace with args.database and args.csv. Add import_error_is_documented(error), add import_items_command(args), and add this branch to main:

def import_error_is_documented(error):
    message = str(error)
    if setup_error_is_documented(error):
        return True
    if message in (
        "CSV file does not exist.",
        "CSV must be UTF-8 text.",
        "CSV is not valid.",
        "CSV must contain at least one item row.",
        "CSV headers must be exactly: id,asset_tag,name,category,loan_days.",
    ):
        return True
    for prefix in ("CSV row ", "CSV contains ", "CSV item "):
        if message.startswith(prefix):
            return True
    return False


if args.command == "import-items":
    return import_items_command(args)

Check Path(args.database).exists() before doing anything with the CSV. Every non-init command follows this rule. If the database is missing, report exactly Error: Database does not exist. Run init first. on stderr and return 1. The check must not create the named file, even when the CSV exists.

For an existing database, call import_items_csv(args.database, args.csv). Do not open the CSV, parse rows, issue SQL, or repeat schema setup in lending.py. That already stages the complete CSV, verifies compatibility, opens one configured application connection, performs one atomic idempotent import, and closes what it opens.

Catch , then re-raise it when import_error_is_documented returns False. A documented missing CSV becomes Error: CSV file does not exist.; validation, disagreement, and schema refusal messages keep their existing text. Each expected failure returns 1, leaves stdout empty, and writes one stderr line. An unrelated ValueError or another exception still propagates.

On success, unpack (inserted, unchanged). Print only after the call returns:

Items inserted: N
Items unchanged: M

Then return 0. Keeping both prints after the boundary prevents a first success-looking line from escaping when a later row makes the transaction roll back.

Press Run. The same three-row CSV is imported twice:

== import-items ==
First status: 0
Items inserted: 3
Items unchanged: 0
Second status: 0
Items inserted: 0
Items unchanged: 3
stderr: <empty>

A thorough check also uses a later-row disagreement and unseen paths. It confirms that no partial row survives and that a programming error is not relabeled as an import refusal.

Task

Add the exact import_error_is_documented(error) from the instructions, add or complete import_items_command(args), and add this branch in main:

if args.command == "import-items":
    return import_items_command(args)

Refuse a missing args.database before calling import_items_csv(args.database, args.csv). On success, print Items inserted: N followed by Items unchanged: M and return 0. Report only a accepted by the classifier; re-raise every other one and print nothing before success.