0%

Capstone: A Persistent Lending Tool · practice

Register a Member

A member row now has five values that must survive the command boundary together. The owns text cleanup, the database owns the parameterized insert, and neither should take over the other’s job.

The demo makes this exact call after initializing the database and importing the three items:

main([
    "register-member", str(database_path), "17", "MB-017",
    "Riley", "riley@example.test", "--loan-limit", "2.5",
])

Argparse passes six namespace fields to register_member_command: args.database, args.member_id, args.member_code, args.name, args.email, and args.loan_limit. The ID is already a positive int. The explicit limit is already a positive finite float; without the option, its is the integer 3.

Add this small text , then add register_member_command(args):

def trimmed(value, label):
    text = value.strip()
    if not text:
        raise ValueError(f"{label} must not be blank.")
    return text

First require the database path to exist. Then trim the member code, name, and email with trimmed(value, label). Use the labels MEMBER_CODE, NAME, and EMAIL, so whitespace-only input becomes one exact message such as Error: EMAIL must not be blank.. Do this before schema preparation or connection opening because invalid input should not change database state.

Call prepare_database(args.database) to validate or migrate the existing file. Open the application connection with open_database, and close that connection in finally, whether registration succeeds or raises a documented conflict. Call the function that inserts a member once:

register_member(
    connection, args.member_id, member_code, name, email, args.loan_limit
)

That function uses placeholders, commits the five explicit values, leaves its caller’s connection open, and translates only its insert’s integrity conflict to ValueError("The member ID or code is already in use."). Catch documented results at the CLI boundary. Do not copy its SQL into lending.py or catch unrelated database failures.

Only after the connection has closed successfully, print the trimmed code and return 0. Add the exact branch if args.command == "register-member": return register_member_command(args) in main.

Press Run to see durable state after reopening:

== register-member ==
Status: 0
stdout: Member registered: MB-017
stderr: <empty>
Stored member after reopening: 17 | MB-017 | Riley | 2.5 | riley@example.test

A thorough check uses default and fractional limits, Unicode and SQL-looking text, duplicate IDs and codes, blank fields, and a missing path. Literal text must remain data throughout.

Task

Add the exact trimmed(value, label) from the instructions, add or complete register_member_command(args), and add this branch in main:

if args.command == "register-member":
    return register_member_command(args)

Refuse a missing database, trim and validate MEMBER_CODE, NAME, and EMAIL, prepare the database, and call the supplied register_member boundary with all five member values. Close the application connection in finally. On success print Member registered: CODE and return 0; map only documented results through report_error.