694 lines
29 KiB
Python
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}")
|