CHAPTER 38: Reply representations and complete pagination

38a: Turn reply bytes into a campaign name
38b: Follow each page until the supplier says stop

HOW TO RUN
Open a terminal in the extracted companion folder, the folder containing
automation, sql and visual_labs. Run:

    python visual_labs/ch38/walkthrough.py

Use python3 or py instead of python if that is the command established in
Chapter 15. No extra Python package is needed. Run it only after predicting
the displayed states. The exact output is also in expected_output.txt.

When a lab imports the automation examples, its short Path/sys.path setup
locates those existing modules relative to this file. It changes where this
Python process looks for imports; it does not change your operating-system
configuration. Preserve the extracted folder structure.

SOURCE AND MEANING
Board 38a uses the explicitly tiny byte-string reply printed on the page.
Board 38b reads the original page1.json and page2.json fixture files, then
uses in-memory dictionaries as replies. This lab is fully offline: it does
not start a server or perform HTTP requests. The two-terminal HTTP exercise
remains available in Chapter 38 and the original automation scripts.

FOLLOW BEFORE YOU RUN
Point to bytes, decoded text, the parsed dictionary, the results list, its
first dictionary and its campaign string. Explain why [0] asks for the first
list item but ["campaign"] asks for a named dictionary value.

PREDICT, RUN, COMPARE
Trace the normal two-page loop. Predict what happens when only page2's
next_page changes to page1.json. The lab makes that change in memory and
reports the repeated-page error. It then restores the end marker, adds C04
to the second page in memory and traces the valid four-record extraction.

ONE MORE INPUT TO REASON THROUGH
Change only page1's continuation to None while leaving its two records intact.
How many records would this client return? Would that prove the source truly
contained only two campaigns?

EXPLAINED ANSWER
The loop would return C01 and C02, then stop: a count of two. It would not know
about C03 because the source no longer points to page2. That is why a valid
loop and valid JSON still need reconciliation with a trustworthy source count.
To check, copy walkthrough.py to practice.py in this folder, set
pages["page1.json"]["next_page"] = None after loading the fixture pages, and
call trace_pages(pages) in an isolated practice section. Do not reinterpret
the intentionally changed result as the unchanged fixture's complete count.

EXPECTED OUTPUT (verified by execution)

38a: Interpret a reply without making a request
Raw representation: bytes
Decoded representation: str
Parsed representation: dict
Spring search
Empty results: no first item; IndexError
38b: Normal fixture pages
Read page1.json: C01, C02
Read page2.json: C01, C02, C03
Stop: next_page is None
Complete record count: 3
Change only page2's next_page to page1.json:
Read page1.json: C01, C02
Read page2.json: C01, C02, C03
Rejected: Repeated page; stopping incomplete extraction
Restore the end marker and add C04:
Read page1.json: C01, C02
Read page2.json: C01, C02, C03, C04
Stop: next_page is None
Complete record count: 4

All data in this lab are fictional teaching examples.
