Files
claudetools/clients/cascades-tucson/scripts/generate-user-questionnaire.py
Howard Enos c4fdb5a233 sync: auto-sync from ACG-TECH03L at 2026-04-19 12:50:13
Author: Howard Enos
Machine: ACG-TECH03L
Timestamp: 2026-04-19 12:50:13
2026-04-19 12:50:24 -07:00

694 lines
29 KiB
Python

"""
Generate one Cascades Tucson user-confirmation questionnaire per recipient
(Meredith, Ashley, John). Output: three .docx files plus a shared .md source.
Format: Section 1 is the roster grouped by department with a [Keep] checkbox,
editable name/title, and a Notes line for role description. Sections 2-7 are
per-attribute checklists ("Who is a driver?", "Who uses ALIS?", etc.),
organized by department so scanning is fast. Section 8 is free text.
Usage:
python generate-user-questionnaire.py
"""
from pathlib import Path
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from lxml import etree
OUT_DIR = Path(__file__).parent.parent / "docs" / "cloud" / "questionnaires"
OUT_DIR.mkdir(parents=True, exist_ok=True)
# Word XML namespaces for content control checkboxes (clickable in Word)
W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
W14_NS = "http://schemas.microsoft.com/office/word/2010/wordml"
# Global counter for unique SDT IDs
_sdt_id_counter = [100000]
def _next_sdt_id():
_sdt_id_counter[0] += 1
return _sdt_id_counter[0]
def add_clickable_checkbox(paragraph):
"""
Append a Word content-control checkbox to the given paragraph. Renders as
an empty box that toggles to a checked box on click in Word 2010+, Word
for the web, and Word mobile. Adds a trailing space run for readability.
"""
sdt_id = _next_sdt_id()
xml = f'''<w:sdt xmlns:w="{W_NS}" xmlns:w14="{W14_NS}">
<w:sdtPr>
<w:rPr>
<w:rFonts w:ascii="MS Gothic" w:eastAsia="MS Gothic" w:hAnsi="MS Gothic" w:hint="eastAsia"/>
</w:rPr>
<w:id w:val="{sdt_id}"/>
<w14:checkbox>
<w14:checked w14:val="0"/>
<w14:checkedState w14:val="2612" w14:font="MS Gothic"/>
<w14:uncheckedState w14:val="2610" w14:font="MS Gothic"/>
</w14:checkbox>
</w:sdtPr>
<w:sdtContent>
<w:r>
<w:rPr>
<w:rFonts w:ascii="MS Gothic" w:eastAsia="MS Gothic" w:hAnsi="MS Gothic" w:hint="eastAsia"/>
</w:rPr>
<w:t>\u2610</w:t>
</w:r>
</w:sdtContent>
</w:sdt>'''
sdt = etree.fromstring(xml)
paragraph._p.append(sdt)
# Trailing space after the checkbox so the label has breathing room
space_run = paragraph.add_run(" ")
return space_run
RECIPIENTS = [
("Meredith Kuhn", "Executive Director"),
("Ashley Jensen", "Assistant Executive Director"),
("John Trozzi", "Maintenance Director"),
]
# -----------------------------------------------------------------------------
# Staff roster by department
# Each entry: (display_name, title, notes_flag)
# notes_flag text is prefilled into the Notes line when helpful
# -----------------------------------------------------------------------------
STAFF = {
"Administrative": [
("Meredith Kuhn", "Executive Director", ""),
("Ashley Jensen", "Assistant Executive Director", ""),
("Lauren Hasselman", "Business Office Director", ""),
("Allison Reibschied", "Accounting Assistant", ""),
],
"Marketing / Sales": [
("Megan Hiatt", "Sales Director", "Handles resident intake forms (PHI)"),
("Crystal Rodriguez", "Sales Associate", "Handles resident intake forms (PHI)"),
("Tamra Matthews", "Move-In Coordinator", "Leaving June 2026 — please confirm"),
],
"Care, Assisted Living (Nursing / Clinical)": [
("Lois Lane", "Health Services Director", ""),
("Karen Rossini", "Health Services Manager", ""),
("Britney Thompson", "Memory Care Nurse", "Title says Memory Care but department currently Assisted Living — which is correct?"),
("Veronica Feller", "Care, Assisted Living Aide", ""),
],
"Care, Memory Care": [
("Shelby Trozzi", "Memory Care Director", ""),
("Christine Nyanzunda", "Memory Care Admin Assistant", "Also appears on caregiver list (Sun/Mon AM) — same person, right?"),
],
"Resident Services": [
("Christina DuPras", "Resident Services Director", ""),
("Cathy Kingston", "Receptionist", "Front desk shared PC"),
("Shontiel Nunn", "Receptionist", "Front desk shared PC"),
("Kyla Quick Tiffany", "Receptionist", "NEW — not yet in AD. Spelling correct?"),
("Michelle Shestko", "MC Receptionist", "MC front desk shared PC"),
("Sebastian Leon", "Courtesy Patrol", ""),
("Sheldon Gardfrey", "Courtesy Patrol", ""),
("Ray Rai", "Courtesy Patrol", ""),
],
"Life Enrichment": [
("Susan Hicks", "Life Enrichment Director", ""),
("Sharon Edwards", "Life Enrichment Assistant", ""),
],
"Culinary": [
("JD Martin", "Culinary Director", ""),
("Ramon Castaneda", "Kitchen Manager", ""),
("Alyssa Brooks", "Dining Manager", ""),
],
"Maintenance": [
("John Trozzi", "Maintenance Director", ""),
("Matt Brooks", "Memory Care Receptionist", "HR record shows department = Maintenance. Which is correct — Maintenance or Memory Care?"),
],
"Housekeeping": [
("Lupe Sanchez", "Housekeeping Director", "AKA Guadalupe Sanchez"),
],
"Transportation": [
("Richard Adams", "Driver", ""),
("Julian Crim", "Driver", ""),
("Christopher Holick", "Driver", ""),
],
}
# -----------------------------------------------------------------------------
# Caregivers by shift pattern (39 total, none currently in AD/M365)
# Each entry: (display_name, role, location, phone_number, flag_note)
# -----------------------------------------------------------------------------
CAREGIVERS = {
"Tuesday-Saturday (15)": [
("Thelma Abainza", "Caregiver", "Tower", "520-867-2579", ""),
("Niel Castro", "MedTech / CCG", "Tower", "520-697-4644", ""),
("Espe Esperance", "MedTech", "Tower", "520-788-9558", ""),
("Barbara Johnson", "Caregiver", "Tower", "520-204-3449", ""),
("Kasey Flores", "Caregiver", "Memory Care", "520-250-1451", ""),
("Richard Flores", "Caregiver", "Memory Care", "520-873-7727", ""),
("Marie Kastner", "Caregiver", "Memory Care", "714-576-9858", ""),
("Bella Mendoza", "Caregiver", "Memory Care", "520-358-2000", ""),
("Rosa Morales", "MedTech", "Memory Care", "312-213-8780", ""),
("Sandra Padilla", "MedTech / CCG", "Tower", "520-585-3317", ""),
("Polett Pinazavala", "MedTech", "Memory Care", "520-449-5533", "Please confirm spelling"),
("Whisper Reed", "MedTech", "Tower (overnight)", "520-312-7575", ""),
("Patricia Sandoval-Beck", "MedTech", "Tower", "520-343-8093", "Hyphenated last name correct?"),
("Charity Sika", "Caregiver", "Memory Care", "623-251-8032", ""),
("Ederick Yuzon", "Caregiver", "Tower", "520-603-8816", "Please confirm spelling"),
],
"Sunday-Thursday (10)": [
("Juan Andrade", "Caregiver", "Memory Care", "520-528-4078", ""),
("Jahmeka Clarke", "MedTech", "Memory Care", "520-649-7034", ""),
("Karina Aziakpo", "MedTech / CCG", "Memory Care (overnight)", "520-392-6859", ""),
("Jinnelle Dittbenner", "Caregiver", "Tower", "520-499-9996", ""),
("Christine Nyanzunda (AM Sun/Mon only)", "MedTech", "Memory Care", "520-304-4251", "SAME person as the Memory Care Admin Assistant? (One account, not two)"),
("Agnes McFerren", "Caregiver", "Tower", "520-406-3063", ""),
("Samuel Ramirez", "Caregiver", "Tower", "520-488-5798", ""),
("Erica Sanchez", "Caregiver", "Memory Care", "520-528-3387", ""),
("Katrina Wyzykowski", "MedTech", "Memory Care", "520-347-1448", ""),
("Corey Tate", "Caregiver (no MedTech)", "Tower (NOC)", "520-535-7821", ""),
],
"Friday-Monday / weekend (5)": [
("Ashli Atwood", "MedTech / CCG", "Memory Care (overnight)", "715-200-1295", ""),
("Cole Johnson", "MedTech", "Tower", "818-970-0890", ""),
("Roseline Cooper", "Caregiver", "Memory Care (overnight)", "520-278-6817", ""),
("Monique Lopez", "Caregiver (Fri+Sat doubles)", "Tower", "520-596-0969", ""),
("Gloria Williford", "MedTech (Fri+Sat doubles)", "Memory Care", "928-551-1682", ""),
],
"Thursday-Monday (3)": [
("Sarah Carroll", "Caregiver", "Tower", "520-409-2341", ""),
("Luke Hogan", "Caregiver", "Tower", "520-312-0141", ""),
("Gina Williams", "Caregiver", "Tower", "520-612-5075", ""),
],
"Split / partial-week (3)": [
("Jen Higdon", "Caregiver", "Tower (M/W/F AM)", "520-730-3548", ""),
("Mary Kariuki", "Caregiver", "Tower (Sat-Mon + Wed PM)", "520-309-1247", ""),
("CeCe Lassey", "Caregiver", "Tower (Sun/Mon doubles + Tue PM)", "520-248-5982", ""),
],
"Sunday & Monday only (1)": [
("Paty Doran", "MedTech / CCG", "Tower", "520-591-7368", "Paty, Patti, or Patricia? Please confirm"),
],
"PRN / float (2)": [
("Ezekiel Huerta", "Caregiver (PRN)", "Tower", "520-591-6113", ""),
("Maia Baker", "MedTech (PRN)", "Memory Care", "TBD", "On secondary sheet only — still employed?"),
],
}
# Flat list of ALL people, grouped by section, for the attribute checklists
def all_people_by_group():
groups = []
for dept, people in STAFF.items():
names = [p[0] for p in people]
groups.append((dept, names))
cg_all = []
for _, people in CAREGIVERS.items():
cg_all.extend(p[0] for p in people)
groups.append(("Caregivers (all 39)", cg_all))
return groups
# -----------------------------------------------------------------------------
# Document helpers
# -----------------------------------------------------------------------------
def add_heading(doc, text, level=1):
h = doc.add_heading(text, level=level)
return h
def add_para(doc, text, bold=False, size=11, italic=False, color=None):
p = doc.add_paragraph()
run = p.add_run(text)
run.font.size = Pt(size)
run.bold = bold
run.italic = italic
if color:
run.font.color.rgb = color
return p
def add_checkbox_line(doc, text, indent=0.25):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(indent)
p.paragraph_format.space_after = Pt(2)
run = p.add_run(f"{CHECKBOX} {text}")
run.font.size = Pt(11)
return p
def add_notes_line(doc, label="Notes: ", indent=0.5):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(indent)
p.paragraph_format.space_after = Pt(6)
run = p.add_run(label + "_" * 80)
run.font.size = Pt(10)
run.font.color.rgb = RGBColor(0x60, 0x60, 0x60)
return p
def add_blank_line(doc):
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(0)
return p
def add_separator(doc):
p = doc.add_paragraph()
run = p.add_run("_" * 90)
run.font.color.rgb = RGBColor(0xCC, 0xCC, 0xCC)
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
def add_attribute_question(doc, question_num, question_text, helper_text, include_caregivers=True):
"""A question that lists names by department with a checkbox to mark yes."""
add_heading(doc, f"{question_num}. {question_text}", level=2)
if helper_text:
add_para(doc, helper_text, italic=True, size=10, color=RGBColor(0x50, 0x50, 0x50))
add_blank_line(doc)
for dept, people in STAFF.items():
add_para(doc, dept, bold=True, size=11)
for name, title, _ in people:
add_checkbox_line(doc, f"{name}{title}", indent=0.35)
add_blank_line(doc)
if include_caregivers:
add_para(doc, "Caregivers (none in M365 yet)", bold=True, size=11)
for group_name, people in CAREGIVERS.items():
add_para(doc, f" {group_name}", italic=True, size=10)
for name, role, location, phone, _ in people:
add_checkbox_line(doc, f"{name}{role} ({location})", indent=0.5)
add_blank_line(doc)
# -----------------------------------------------------------------------------
# Document builder
# -----------------------------------------------------------------------------
def build_doc(recipient_name, recipient_title):
doc = Document()
style = doc.styles["Normal"]
style.font.name = "Calibri"
style.font.size = Pt(11)
# ---- Title ----
title = doc.add_heading("Cascades of Tucson — User + Access Confirmation", level=0)
p = doc.add_paragraph()
r = p.add_run(f"Prepared for: {recipient_name}, {recipient_title}")
r.bold = True
r.font.size = Pt(12)
p = doc.add_paragraph()
r = p.add_run("Prepared by: Howard Enos, Computer Guru \u2022 Date: 2026-04-18")
r.font.size = Pt(10)
r.italic = True
add_separator(doc)
# ---- Intro ----
add_heading(doc, "Why we need this", level=1)
add_para(
doc,
"Before we purchase the Microsoft 365 Business Premium licenses, create the 39 new "
"caregiver accounts, and configure the security policies that lock down the shared "
"phones and PHI access, I need to know four things about every person on your staff:",
)
add_checkbox_line(doc, "Is their name and department correct?", indent=0.3)
add_checkbox_line(doc, "How do they use technology day-to-day (company phone, PC, both)?", indent=0.3)
add_checkbox_line(doc, "Should they be allowed to sign in from outside the building?", indent=0.3)
add_checkbox_line(doc, "Which systems / mailboxes / apps do they actually need?", indent=0.3)
add_para(
doc,
"Please answer the sections below at your own pace. Mark a box by double-clicking the "
"\u2610 and replacing it with an X, or simply type an X in front of the name. Cross out "
"anyone who should be removed. Write additions where it says \"Add anyone missing.\" "
"Leave Comments / Notes where helpful \u2014 the more context, the better the lockdown.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_para(
doc,
"A quick note about ALIS: ALIS is the resident records website. When we tie it to "
"Microsoft, users who log into their Microsoft account will automatically be signed "
"into ALIS \u2014 no second username/password or second MFA prompt. That only works if "
"we know who needs it (Section 5).",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
doc.add_page_break()
# ---- Section 1: Roster ----
add_heading(doc, "Section 1 \u2014 The Staff Roster", level=1)
add_para(
doc,
"Below is every person we currently have on file, organized by department. For "
"each person: leave the Keep box checked if they are a current employee at that title, "
"or uncheck and strike through anyone who should be removed. Correct spellings "
"inline. Add a short note on the Notes line describing what they actually do day-to-day, "
"especially anything involving resident information (PHI), medication, or outside-network "
"access. Blank rows are at the bottom of each department for additions.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_blank_line(doc)
for dept, people in STAFF.items():
add_heading(doc, dept, level=2)
for name, title, flag in people:
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(2)
p.paragraph_format.left_indent = Inches(0.25)
r = p.add_run(f"{CHECKBOX} Keep ")
r.bold = True
r = p.add_run(f"{name} \u2014 {title}")
if flag:
p2 = doc.add_paragraph()
p2.paragraph_format.left_indent = Inches(0.5)
p2.paragraph_format.space_after = Pt(2)
r = p2.add_run(f"\u203A {flag}")
r.italic = True
r.font.size = Pt(10)
r.font.color.rgb = RGBColor(0xB0, 0x40, 0x00)
add_notes_line(doc, " Notes: ")
# Three blank add-row lines
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.25)
r = p.add_run("Add anyone missing from this department:")
r.italic = True
r.font.size = Pt(10)
r.font.color.rgb = RGBColor(0x40, 0x40, 0x80)
for _ in range(3):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.25)
p.paragraph_format.space_after = Pt(2)
r = p.add_run(f"{CHECKBOX} Add Name: ________________________ Title: ________________________")
r.font.size = Pt(10)
add_notes_line(doc, " Notes: ")
add_blank_line(doc)
# ---- Caregivers section ----
doc.add_page_break()
add_heading(doc, "Section 1b \u2014 Caregivers (NEW \u2014 no current accounts)", level=1)
add_para(
doc,
"These are the 39 staff currently using shared workstation logins. None has an email "
"account or a Microsoft identity yet. They are the reason we are deploying the 25 "
"shared Android phones. Please confirm each name + role, correct any spelling, and "
"add anyone missing. Default assumption for every caregiver: phone-only, no outside "
"access (locked to Cascades network + managed shared phone only). Flag exceptions "
"in the Notes line.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_blank_line(doc)
for group_name, people in CAREGIVERS.items():
add_heading(doc, group_name, level=2)
for name, role, location, phone, flag in people:
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(2)
p.paragraph_format.left_indent = Inches(0.25)
r = p.add_run(f"{CHECKBOX} Keep ")
r.bold = True
r = p.add_run(f"{name} \u2014 {role} ({location}) [{phone}]")
if flag:
p2 = doc.add_paragraph()
p2.paragraph_format.left_indent = Inches(0.5)
p2.paragraph_format.space_after = Pt(2)
r = p2.add_run(f"\u203A {flag}")
r.italic = True
r.font.size = Pt(10)
r.font.color.rgb = RGBColor(0xB0, 0x40, 0x00)
add_notes_line(doc, " Notes: ")
add_blank_line(doc)
# Add-new-caregiver block
p = doc.add_paragraph()
r = p.add_run("Add any caregivers missing from this list:")
r.italic = True
r.font.size = Pt(10)
r.font.color.rgb = RGBColor(0x40, 0x40, 0x80)
for _ in range(5):
p = doc.add_paragraph()
p.paragraph_format.left_indent = Inches(0.25)
p.paragraph_format.space_after = Pt(2)
r = p.add_run(f"{CHECKBOX} Add Name: _______________________ Role: _______________________ Phone: ______________")
r.font.size = Pt(10)
# ---- Attribute-based questions ----
doc.add_page_break()
add_heading(doc, "Section 2 \u2014 Roles (check everyone who fits)", level=1)
add_attribute_question(
doc,
"2a",
"Who is a Driver? (Transportation / residents to appointments)",
"Drivers typically use a company phone for dispatch and communication \u2014 usually no personal PC.",
include_caregivers=False,
)
add_attribute_question(
doc,
"2b",
"Who is a Nurse, MedTech, or Clinical Staff?",
"People in this group see resident medical information (PHI) and are the main reason we care about HIPAA audit logs and ALIS single sign-on.",
include_caregivers=True,
)
add_attribute_question(
doc,
"2c",
"Who handles resident intake paperwork, leases, or medical forms by email?",
"These people receive PHI via email from doctors, families, or facilities. They need the strongest anti-phishing and DLP (data loss prevention) policies.",
include_caregivers=False,
)
# ---- Section 3: Access questions ----
doc.add_page_break()
add_heading(doc, "Section 3 \u2014 How They Use Technology", level=1)
add_para(
doc,
"For each person, please tell us whether they sit at a company PC, use a company "
"phone on shift, or both.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_attribute_question(
doc,
"3a",
"Who uses a COMPANY PC / DESKTOP?",
"Anyone who signs into a Windows computer at Cascades. Includes shared front-desk PCs.",
include_caregivers=True,
)
add_attribute_question(
doc,
"3b",
"Who uses a COMPANY PHONE (the 25 shared Android phones)?",
"Anyone who will pick up a shared phone at start of shift and use it for work (medication passes, resident records, email, rounds).",
include_caregivers=True,
)
add_attribute_question(
doc,
"3c",
"Who uses a COMPANY TABLET / iPad?",
"Specifically the 9 kitchen iPads + any others. Likely Culinary staff.",
include_caregivers=True,
)
# ---- Section 4: Outside access ----
doc.add_page_break()
add_heading(doc, "Section 4 \u2014 Sign-In From Outside the Building", level=1)
add_attribute_question(
doc,
"4a",
"Who should be allowed to sign in from OUTSIDE the building?",
"Home office, travel, personal cell, hotel, etc. Anyone NOT checked will be locked to "
"the Cascades network and managed devices only \u2014 which is what HIPAA and "
"Conditional Access prefer by default.",
include_caregivers=True,
)
add_attribute_question(
doc,
"4b",
"Who should be allowed to check email on their PERSONAL cell phone?",
"Separate question: some people may need outside access on a company laptop but NOT on a personal phone (HIPAA risk). Check only the people who genuinely need email on a personal device.",
include_caregivers=True,
)
# ---- Section 5: ALIS ----
doc.add_page_break()
add_heading(doc, "Section 5 \u2014 ALIS (Resident Records)", level=1)
add_para(
doc,
"ALIS is the resident records website. If we connect ALIS to Microsoft, users who "
"sign into their Microsoft account will be automatically signed into ALIS \u2014 no "
"second username / password, no second MFA prompt, no separate login. This is also "
"where the per-person audit log lives (who opened what chart, when, from which device) "
"which is the HIPAA story.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_para(
doc,
"We only want to connect ALIS for people who actually use it, so please check "
"carefully.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_attribute_question(
doc,
"5a",
"Who LOGS INTO ALIS today (resident charts, care plans, medication)?",
"",
include_caregivers=True,
)
add_attribute_question(
doc,
"5b",
"Who should be able to access ALIS from OUTSIDE the building?",
"Only matters for the people checked in 5a. Example: an on-call nurse charting from home.",
include_caregivers=True,
)
# ---- Section 6: Shared mailboxes ----
doc.add_page_break()
add_heading(doc, "Section 6 \u2014 Shared Mailboxes (Accounting@, Frontdesk@, etc.)", level=1)
add_para(
doc,
"These are department mailboxes multiple people can access from their own sign-in. "
"For each mailbox below, check everyone who should receive and send email through it. "
"Leaving a mailbox blank means nobody needs it and we will decommission it.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_blank_line(doc)
shared_mailboxes = [
("accounting@cascadestucson.com", "Accounting / AR / AP"),
("accountingassistant@cascadestucson.com", "Accounting assistant desk"),
("boadmin@cascadestucson.com", "Business Office admin"),
("frontdesk@cascadestucson.com", "Main front desk (Tower)"),
("memcarereceptionist@cascadestucson.com", "Memory Care front desk"),
("hr@cascadestucson.com", "HR / employee matters"),
("nurse@cascadestucson.com", "Clinical nursing desk"),
("medtech@cascadestucson.com", "MedTech medication pass"),
("transportation@cascadestucson.com", "Drivers / resident transport"),
("security@cascadestucson.com", "Security / courtesy patrol"),
("courtesypatrol@cascadestucson.com", "Courtesy Patrol (new \u2014 please confirm it\u2019s needed)"),
("training@cascadestucson.com", "Training"),
("sales@cascadestucson.com", "Sales inquiries"),
("fax@cascadestucson.com", "Fax-to-email"),
]
for addr, desc in shared_mailboxes:
add_heading(doc, f"{addr} \u2014 {desc}", level=3)
for dept, people in STAFF.items():
add_para(doc, dept, bold=True, size=10)
for name, title, _ in people:
add_checkbox_line(doc, f"{name} \u2014 {title}", indent=0.35)
add_blank_line(doc)
# ---- Section 7: Odds and ends ----
doc.add_page_break()
add_heading(doc, "Section 7 \u2014 Specific Questions", level=1)
add_heading(doc, "7a. Departments that may need a re-label", level=2)
add_para(doc, "Please mark the correct department for each:")
add_checkbox_line(doc, "Britney Thompson \u2014 currently \"Care, Assisted Living,\" title says Memory Care Nurse. Correct department is:", indent=0.35)
add_notes_line(doc, " ")
add_checkbox_line(doc, "Matt Brooks \u2014 HR record says Maintenance, title says Memory Care Receptionist. Correct department is:", indent=0.35)
add_notes_line(doc, " ")
add_checkbox_line(doc, "Christine Nyanzunda \u2014 Memory Care Admin Assistant AND appears on caregiver shift list (Sun/Mon AM). One account or two?", indent=0.35)
add_notes_line(doc, " ")
add_blank_line(doc)
add_heading(doc, "7b. Spelling confirmations (caregivers)", level=2)
for name, reason in [
("Polett Pinazavala", "unusual spelling"),
("Patricia Sandoval-Beck", "hyphenated last name"),
("Ederick Yuzon", "unusual spelling"),
("Paty Doran", "Paty / Patti / Patricia"),
("Maia Baker", "only on secondary sheet \u2014 still employed?"),
("Kyla Quick Tiffany", "three names or hyphenated?"),
]:
add_checkbox_line(doc, f"{name} \u2014 {reason} Correct spelling: ________________________", indent=0.35)
add_blank_line(doc)
add_heading(doc, "7c. Departures / upcoming changes", level=2)
add_para(doc, "Anyone leaving, transferring, or changing role in the next 90 days?", italic=True, size=10)
for _ in range(5):
add_notes_line(doc, "")
# ---- Section 8: Free text ----
doc.add_page_break()
add_heading(doc, "Section 8 \u2014 Anything Else", level=1)
add_para(
doc,
"Anything we should know about a specific person or situation? Unusual access needs, "
"contractors, people who share a phone / PC, dual-role employees, anyone who handles "
"legal or compliance records, etc. Write freely below.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_blank_line(doc)
for _ in range(20):
p = doc.add_paragraph()
p.paragraph_format.space_after = Pt(2)
r = p.add_run("_" * 100)
r.font.color.rgb = RGBColor(0xCC, 0xCC, 0xCC)
# ---- Closing ----
doc.add_page_break()
add_heading(doc, "Thank you", level=1)
add_para(
doc,
f"{recipient_name.split()[0]} \u2014 thank you for taking the time on this. Once I have "
"your answers (and Meredith\u2019s and Ashley\u2019s / John\u2019s), I will merge the "
"three responses into one final list and come back with the exact license count, the "
"exact accounts we are creating, and the exact Conditional Access rules before I change "
"anything in your tenant. Nothing gets purchased or changed until you\u2019ve seen the "
"final plan.",
)
add_para(
doc,
"Easiest way to return this: save and email back to howard@azcomputerguru.com. "
"You can also drop a copy on our shared OneDrive or Teams folder and I\u2019ll pick it up.",
italic=True,
size=10,
color=RGBColor(0x50, 0x50, 0x50),
)
add_para(
doc,
"\u2014 Howard",
bold=True,
)
return doc
# -----------------------------------------------------------------------------
# Generate files
# -----------------------------------------------------------------------------
if __name__ == "__main__":
for name, title in RECIPIENTS:
doc = build_doc(name, title)
first = name.split()[0]
filename = OUT_DIR / f"cascades-user-access-questionnaire-{first}.docx"
doc.save(str(filename))
print(f"[OK] {filename}")
print(f"\nAll files written to: {OUT_DIR}")