AI Feders original practice project: fictional US stationery sales Fixture version: 2026-10-15 Purpose Turn sales.csv into a small dashboard with a cleaned table, a rejection log, a state filter, accepted order count, units sold and sales value in USD. This is synthetic learning data, not actual business or customer information. The text, CSV and JSON files can be read without installing software. Building the dashboard requires your own Python and Streamlit setup. No API key is needed. Input model The CSV has a header and 15 data rows. Number data rows from 1, excluding the header. An accepted row is one order with one product. Preserve the original. The required columns are order_id, order_date, state, product, units and unit_price_usd. Do not silently add missing columns or accept extra columns. For this exercise all fields must be non-empty, with no surrounding whitespace. Stop and report a schema or empty-field error if either condition is violated. Validation rules Process rows in their original order. Apply these checks in this exact order and record the first failed rule for each rejected row: 1. duplicate_order_id: reject an ID already present in an accepted earlier row. 2. invalid_date: require exactly YYYY-MM-DD and a real calendar date. 3. invalid_state: accept only CA, TX, NY and WA in this exercise. 4. invalid_units: require a positive whole number written using digits only. 5. invalid_price: require a positive price written using digits with an optional decimal point and one or two fractional digits; no signs or currency symbols. These four state codes are the project's scope, not the complete US code list. Only add an order ID to the seen-ID set after the row passes every check. Keep the row number, order ID and rejection reason in the rejection log. Calculations Line sales = units multiplied by unit_price_usd. Use decimal arithmetic or integer cents for the money checks. All monetary values are USD. The file has no tax, shipping, refunds or currency conversion; do not invent any of these. Compare your result with expected-results.json, which is an answer key, not an input data source. Reconcile raw rows = accepted orders + rejected rows. Expected checks 15 input rows; 10 accepted orders; 5 rejected rows; 24 accepted units; USD 247.00 sales. Accepted dates run from 2026-09-01 through 2026-09-07. Rejected data rows: 11 duplicate_order_id; 12 invalid_date; 13 invalid_state; 14 invalid_units; 15 invalid_price. CA: 3 orders, 9 units, USD 86.00. TX: 3 orders, 8 units, USD 83.00. NY: 2 orders, 2 units, USD 30.50. WA: 2 orders, 5 units, USD 47.50. Notebook: 4 orders, 10 units, USD 125.00. Pen Set: 3 orders, 10 units, USD 50.00. Desk Mat: 3 orders, 4 units, USD 72.00. Dashboard checks Calculate displayed totals from the same filtered table that is shown. Selecting CA must show 3 orders, 9 units and USD 86.00. Resetting the filter must restore all accepted rows and USD 247.00. A filter with no matching rows should show a clear empty state, zero orders, zero units and USD 0.00. Reading the same file twice must not append another copy of the orders. Write down the package versions, validation rules and checks you completed. Optional extensions Add a date-range filter, a product filter or a download of accepted rows. Introduce an extra deliberate error in a separate copy and document the new expected result before testing it. Do not change the supplied baseline file.