mattrguerra/meridian-skillsmainPrivate on GitHub, published here in full

SKILL.md

Language Markdown8.2 KB146 lines

skills/thanksio-postcard-formatter/SKILL.md

  1. ---
  2. name: thanksio-postcard-formatter
  3. description: >-
  4. Transform raw Meridian Display Co. order export XLSX files (from Plinth, the in-house order
  5. system) into clean Thanks.io-ready postcard shipping CSVs. Use this skill when Matt mentions
  6. processing a postcard sheet, formatting addresses for Thanks.io, running the postcard formatter,
  7. dropping a new XLSX into the Thanksio folder, 'the postcard sheet,' 'Thanks.io export,' 'postcard
  8. addresses,' or any variant, including casual phrasing like 'process the new postcard sheet,'
  9. 'clean up the addresses for postcards,' or 'run the postcard formatter.' Also triggers when an
  10. XLSX is placed in the 'Thanksio-Post-Card-Sheet-Formatter/01 - New/' folder.
  11. ---
  12. # Thanks.io Postcard Sheet Formatter
  13. Transforms raw 21-column XLSX order exports from Plinth, Meridian Display Co.'s order system, into the 29-column CSV format required by Thanks.io's postcard shipping import. Handles guest-shipping cases (orders shipped to a hotel or convention center on behalf of the recipient), normalizes address and contact data, and validates the result before save.
  14. ## When to run
  15. Trigger when:
  16. - A new `.xlsx` file appears in `Thanksio-Post-Card-Sheet-Formatter/01 - New/` (or wherever Matt has dropped it).
  17. - Matt asks to "process the postcard sheet," "clean up postcard addresses," "format for Thanks.io," or any similar phrasing.
  18. ## What it does
  19. Reads the XLSX (rows 1-2 are report title and blank; row 3 is headers; row 4+ is data), applies address-selection logic to choose between shipping and billing fields, applies nine cleaning rules, runs four validation checks, and writes a UTF-8 CSV named `cleaned-postcard-addresses-YYYY-MM-DD.csv` into a sibling `02 - Formatted/` directory.
  20. ## How to run
  21. ```bash
  22. cd ~/.claude/skills/thanksio-postcard-formatter/scripts
  23. python3 format.py <path-to-input.xlsx>
  24. ```
  25. By default the output lands in `<input parent>/../02 - Formatted/`. To archive the input after a successful run:
  26. ```bash
  27. python3 format.py <path-to-input.xlsx> --archive
  28. ```
  29. This moves the input into `02 - Formatted/99 - Archive/` once the CSV is written.
  30. To override the output directory:
  31. ```bash
  32. python3 format.py <path-to-input.xlsx> --output-dir /some/other/path
  33. ```
  34. ## Input columns the skill reads
  35. The Plinth order export names its columns in Plinth's snake_case style. Of the 21 columns, the skill reads these:
  36. | Block | Columns |
  37. |---|---|
  38. | Shipping | `ship_name`, `ship_company`, `ship_street`, `ship_street2`, `ship_city`, `ship_region`, `ship_postcode`, `ship_country` |
  39. | Billing | `bill_name`, `bill_company`, `bill_street`, `bill_street2`, `bill_city`, `bill_region`, `bill_postcode`, `bill_country` |
  40. | Shared | `email`, `phone` |
  41. The remaining columns (order reference, dates, totals) are ignored.
  42. ## Address-selection logic
  43. Default: shipping address. The skill switches to the **billing** address fields if either of these is true:
  44. 1. `ship_name` starts with `Guest:` (case-insensitive). This indicates the order is being shipped to someone on behalf of the recipient (e.g., a hotel reservation under a guest name).
  45. 2. `ship_company` contains any of: `hotel`, `suites`, `marriott`, `hilton`, `hyatt`, `sheraton`, `resort`, `motel`, `lodge`, `convention center`, `expo center`, `fairground`, `conference center`, **AND** the billing street address (`bill_street`) differs from the shipping street address (`ship_street`).
  46. If the billing and shipping street addresses are identical, the skill retains shipping. That case is treated as the company's own office, not a hotel-shipping scenario.
  47. When the switch fires, Name, Company, Address, Address2, City, State, Postal Code, and Country are pulled from the `bill_*` columns instead of the `ship_*` columns. Email and Phone always come from the shared `email` and `phone` columns regardless of which address block is used.
  48. ## Cleaning rules
  49. Applied to every row:
  50. 1. **HTML entity decoding** via `html.unescape` on all text fields (e.g., `&#039;` → `'`, `&amp;` → `&`).
  51. 2. **Name:** strip `Guest:` prefix, strip whitespace, convert ALL CAPS to Title Case (mixed case left as-is).
  52. 3. **Company:** decode entities, convert ALL CAPS to Title Case if longer than three characters (short acronyms like `ABC` or `IBM` preserved), replace literal `"nan"` strings with empty.
  53. 4. **Address:** decode entities, collapse doubled commas to single, strip whitespace.
  54. 5. **City:** decode entities, ALL CAPS to Title Case, strip trailing commas.
  55. 6. **Postal Code:** strip `.0` float-conversion suffix, zero-pad 4-digit numeric codes to 5 (recovers lost-leading-zero Northeast US ZIPs).
  56. 7. **Country:** normalize `United States` / `USA` / `us` (any case) to `US`; non-US countries left as-is.
  57. 8. **Phone:** strip non-digit characters, prepend `1` for 10-digit US numbers, output digits only (e.g., `12065551234`).
  58. 9. **General nulls:** replace `NaN`, `None`, and literal `"nan"` strings with empty across the entire output.
  59. ## Validation
  60. Logged as warnings; **no rows are dropped**:
  61. - Empty `Name` after cleaning.
  62. - Empty `Address` after cleaning.
  63. - Suspicious `Company` field: contains dimensions (e.g., `10x8ft`) or words `ONLY` / `frame` / `banner`. These usually indicate the field actually holds an order note rather than a company name. Flag for manual review; do not auto-correct.
  64. - US row missing `State` or `Postal Code`.
  65. All rows ship to the CSV regardless of warning count. Review the warning log before importing to Thanks.io.
  66. ## Output schema
  67. 29 columns in this exact order:
  68. ```
  69. ID, Name, Company, Address, Address2, City, State, Postal Code, Country, Email, Phone, Custom 1, Custom 2, Custom 3, Custom 4, Order Item QR Scans, Order Item Status, Order Item Estimated Delivery Date, Order Item Failure, Message, Order Id, Order Type, Order Size, Order Method, Sub Account ID, Created At, Giftcard Reedem, Giftcard Brand, Giftcard Amount
  70. ```
  71. Defaults filled regardless of input:
  72. | Column | Default |
  73. |---|---|
  74. | `Order Type` | `postcard` |
  75. | `Order Size` | `4x6` |
  76. | `Order Method` | `cardbuilder` |
  77. | `Giftcard Reedem` | `N/A` |
  78. | `Giftcard Brand` | `N/A` |
  79. | `Giftcard Amount` | `N/A` |
  80. All other unmapped columns are left blank.
  81. The `Giftcard Reedem` column header is misspelled (should be `Redeem`). This matches Thanks.io's upstream import schema verbatim. **Do not "fix" it.** The import will fail if the column is renamed.
  82. ## Dependencies
  83. - Python 3.10+
  84. - `pandas` (for `read_excel`)
  85. - `openpyxl` (pandas' XLSX engine)
  86. - Standard library: `html`, `re`, `csv`, `datetime`, `pathlib`, `argparse`, `logging`
  87. Install once:
  88. ```bash
  89. pip install pandas openpyxl
  90. ```
  91. ## Known caveats
  92. - The hotel-keyword list is a fixed allowlist of 13 entries. A hotel-equivalent venue not matching any of those words will not trigger the address switch. To extend, edit `HOTEL_KEYWORDS` in `scripts/format.py`.
  93. - ALL-CAPS → Title Case is a heuristic. A name like `JD SMITH` becomes `Jd Smith`. Review names if source data quality is uneven.
  94. - The address-difference comparison is case- and whitespace-normalized but otherwise literal. Two functionally identical addresses written differently (`123 Main St` vs. `123 Main Street`) will be treated as different.
  95. - No persistence or run log. To audit a prior run, retain the archived XLSX and the produced CSV side by side.
  96. - The skill processes one XLSX per invocation. If multiple XLSX files exist in `01 - New/`, run the skill once per file.
  97. ## When something goes wrong
  98. - **`KeyError` on a `ship_...` or `bill_...` column:** the source export's column headers have drifted from the documented 21-column structure. Compare against the input columns listed under "Input columns the skill reads" above and fix the Plinth export rather than the skill.
  99. - **All rows produce "empty Name" or "empty Address" warnings:** likely a row-skip miscount. Check that the input XLSX still has the title-row + blank-row + headers pattern (i.e., headers on row 3). If the source format changed, update `skiprows=2` in `format.py`.
  100. - **Pandas raises on `read_excel`:** confirm `openpyxl` is installed (`pip install openpyxl`).
  101. - **Import to Thanks.io fails on column names:** the output column order or naming has drifted. Compare the output CSV header against the `OUTPUT_COLUMNS` constant in `scripts/format.py`. The misspelled `Giftcard Reedem` is intentional.