266 lines
12 KiB
Python
266 lines
12 KiB
Python
#!/usr/bin/env python3
|
|
"""Build the Dataforth Shared Drives Reorganization & Access Plan (.docx)."""
|
|
import os
|
|
from docx import Document
|
|
from docx.shared import Pt, RGBColor, Inches
|
|
from docx.enum.text import WD_ALIGN_PARAGRAPH
|
|
from docx.enum.table import WD_TABLE_ALIGNMENT
|
|
from docx.oxml.ns import qn
|
|
from docx.oxml import OxmlElement
|
|
|
|
NAVY = RGBColor(0x1F, 0x39, 0x64)
|
|
ACCENT = RGBColor(0x2E, 0x74, 0xB5)
|
|
RED = RGBColor(0xB0, 0x20, 0x20)
|
|
GREY = RGBColor(0x59, 0x59, 0x59)
|
|
HDR_FILL = "1F3964"
|
|
SENS_FILL = "FBE4E4"
|
|
ALT_FILL = "F2F6FB"
|
|
|
|
doc = Document()
|
|
|
|
# Base style
|
|
normal = doc.styles["Normal"]
|
|
normal.font.name = "Calibri"
|
|
normal.font.size = Pt(11)
|
|
normal.paragraph_format.space_after = Pt(6)
|
|
|
|
for hs, sz, col in [("Heading 1", 15, NAVY), ("Heading 2", 12.5, ACCENT)]:
|
|
st = doc.styles[hs]
|
|
st.font.name = "Calibri"
|
|
st.font.size = Pt(sz)
|
|
st.font.color.rgb = col
|
|
st.font.bold = True
|
|
|
|
def shade(cell, fill):
|
|
tcPr = cell._tc.get_or_add_tcPr()
|
|
shd = OxmlElement("w:shd")
|
|
shd.set(qn("w:val"), "clear")
|
|
shd.set(qn("w:fill"), fill)
|
|
tcPr.append(shd)
|
|
|
|
def set_cell_text(cell, text, bold=False, color=None, size=10.5, align=None):
|
|
cell.text = ""
|
|
p = cell.paragraphs[0]
|
|
if align:
|
|
p.alignment = align
|
|
run = p.add_run(text)
|
|
run.font.size = Pt(size)
|
|
run.font.bold = bold
|
|
if color:
|
|
run.font.color.rgb = color
|
|
|
|
def header_row(table, labels):
|
|
for i, lab in enumerate(labels):
|
|
c = table.rows[0].cells[i]
|
|
set_cell_text(c, lab, bold=True, color=RGBColor(0xFF, 0xFF, 0xFF))
|
|
shade(c, HDR_FILL)
|
|
|
|
def add_table(headers, rows, widths=None, sensitive_rows=None):
|
|
sensitive_rows = sensitive_rows or set()
|
|
t = doc.add_table(rows=1, cols=len(headers))
|
|
t.alignment = WD_TABLE_ALIGNMENT.CENTER
|
|
t.style = "Table Grid"
|
|
header_row(t, headers)
|
|
for r_idx, row in enumerate(rows):
|
|
cells = t.add_row().cells
|
|
for i, val in enumerate(row):
|
|
set_cell_text(cells[i], val)
|
|
if r_idx in sensitive_rows:
|
|
for c in cells:
|
|
shade(c, SENS_FILL)
|
|
elif r_idx % 2 == 1:
|
|
for c in cells:
|
|
shade(c, ALT_FILL)
|
|
if widths:
|
|
for row in t.rows:
|
|
for i, w in enumerate(widths):
|
|
row.cells[i].width = Inches(w)
|
|
return t
|
|
|
|
def spacer(pts=4):
|
|
p = doc.add_paragraph()
|
|
p.paragraph_format.space_after = Pt(pts)
|
|
|
|
# ---- Title block ----
|
|
title = doc.add_paragraph()
|
|
title.alignment = WD_ALIGN_PARAGRAPH.LEFT
|
|
r = title.add_run("Dataforth Shared Drives")
|
|
r.font.size = Pt(24); r.font.bold = True; r.font.color.rgb = NAVY
|
|
sub = doc.add_paragraph()
|
|
r = sub.add_run("Reorganization & Access Plan")
|
|
r.font.size = Pt(15); r.font.color.rgb = ACCENT
|
|
meta = doc.add_paragraph()
|
|
r = meta.add_run("Prepared by Arizona Computer Guru · June 2026")
|
|
r.font.size = Pt(10); r.font.color.rgb = GREY; r.italic = True
|
|
|
|
# rule
|
|
pr = doc.add_paragraph()
|
|
pbdr = OxmlElement("w:pPr"); bdr = OxmlElement("w:pBdr")
|
|
bottom = OxmlElement("w:bottom")
|
|
bottom.set(qn("w:val"), "single"); bottom.set(qn("w:sz"), "12")
|
|
bottom.set(qn("w:space"), "1"); bottom.set(qn("w:color"), "2E74B5")
|
|
bdr.append(bottom); pbdr.append(bdr)
|
|
pr._p.insert(0, pbdr)
|
|
|
|
# ---- Intro ----
|
|
doc.add_paragraph(
|
|
"This document explains how Dataforth's shared network drives are organized today, why we "
|
|
"recommend changing that, and what a cleaner, department-based setup would look like. The goal "
|
|
"is simple: keep files organized by department, give each team access to what it needs, and "
|
|
"protect sensitive information — without anyone losing access to the files they use day to day."
|
|
)
|
|
note = doc.add_paragraph()
|
|
r = note.add_run("Nothing will change until you have reviewed and approved a plan. We will make changes "
|
|
"in stages so the transition is smooth.")
|
|
r.italic = True; r.font.color.rgb = GREY
|
|
|
|
# ---- Section 1: situation today ----
|
|
doc.add_heading("1. The situation today", level=1)
|
|
doc.add_paragraph(
|
|
"Right now, every shared drive at Dataforth is open to every employee. Anyone who logs in can "
|
|
"open, change, move, or delete files on all of them — there are no department-level "
|
|
"restrictions in place."
|
|
)
|
|
doc.add_paragraph(
|
|
"Think of it like a building where every filing cabinet is unlocked: convenient, but it means "
|
|
"payroll records, employee safety files, purchase orders, and the accounting books are all sitting "
|
|
"open to everyone in the company. It also makes accidental changes or deletions easy, and if a "
|
|
"single employee account is ever compromised, that account can reach everything at once."
|
|
)
|
|
doc.add_paragraph(
|
|
"On top of access, the drives have simply grown messy over the years — the same files in "
|
|
"multiple places, folders named “Do not use,” and personal folders left behind by former "
|
|
"employees. Files are arranged by history, not by department."
|
|
)
|
|
|
|
# ---- Section 2: what we recommend ----
|
|
doc.add_heading("2. What we recommend", level=1)
|
|
for b in [
|
|
"Organize files by department, so each team's data lives in one clear place.",
|
|
"Give each department access to only the folders it needs — full access where they work, "
|
|
"view-only where they just need to reference, and no access to areas that don't concern them.",
|
|
"Lock down sensitive data (payroll, employee/OSHA records, purchase orders, accounting) so only "
|
|
"the right people can see it.",
|
|
"Clean up duplicate, outdated, and orphaned folders as we go (with your approval before anything "
|
|
"is removed).",
|
|
"Set it up so access is easy to manage going forward — adding a new hire becomes “put them "
|
|
"in the Sales group” rather than hand-setting permissions folder by folder.",
|
|
]:
|
|
doc.add_paragraph(b, style="List Bullet")
|
|
|
|
# ---- Section 3: current drives ----
|
|
doc.add_heading("3. Your shared drives today", level=1)
|
|
doc.add_paragraph("Here is what is on each shared drive now. Drives marked with a red row contain "
|
|
"sensitive information that should not be open to all staff.")
|
|
rows = [
|
|
["Q: (“c-drive”)", "General company files: documents, manufacturing, production control, shipping, scanned documents — plus Payroll, OSHA records, and Purchase Orders", "Yes"],
|
|
["T: (“e-drive”)", "Engineering & manufacturing files (ENGR, ECOs, FMEA, Test Engineering) — plus QuickBooks accounting files", "Yes"],
|
|
["S: (“sage”)", "Sage accounting system, invoices, and financial reports", "Yes"],
|
|
["W: (“sales”)", "Sales & marketing materials, contacts, RMAs, videos", "No"],
|
|
["Y: (“archive”)", "Archived engineering data", "No"],
|
|
["B: (“Engineering”)", "The main, large Engineering data store", "No"],
|
|
["itsvc", "IT software, drivers, and server tools (used by IT)", "No"],
|
|
["X: (“webshare”)", "Files for the automated website datasheet system (mostly automated)", "No"],
|
|
]
|
|
sens = {i for i, row in enumerate(rows) if row[2] == "Yes"}
|
|
add_table(["Drive", "What's on it today", "Sensitive?"], rows, widths=[1.3, 5.0, 0.9], sensitive_rows=sens)
|
|
small = doc.add_paragraph()
|
|
r = small.add_run("There is also a “test” drive used by the DOS test stations on the manufacturing "
|
|
"floor. It must stay exactly as it is for those machines to keep working, so it is not "
|
|
"part of this reorganization.")
|
|
r.font.size = Pt(9.5); r.italic = True; r.font.color.rgb = GREY
|
|
|
|
# ---- Section 4: proposed structure ----
|
|
doc.add_heading("4. A cleaner structure — organized by department", level=1)
|
|
doc.add_paragraph(
|
|
"Instead of a handful of catch-all drives that everyone can see, we propose organizing the data "
|
|
"into clear department areas. Each area would have its own access list. The areas marked "
|
|
"“restricted” would be limited to specific people or departments."
|
|
)
|
|
prop = [
|
|
["Company (All Staff)", "Company-wide documents, forms, policies, templates, announcements", "Everyone"],
|
|
["Engineering", "Engineering files, ECOs, FMEA, test engineering, engineering archive", "Engineering"],
|
|
["Manufacturing & Production", "Manufacturing, production control, SMT, assembly documents", "Manufacturing"],
|
|
["Quality & Calibration", "Quality records, calibration, inspection data", "Quality"],
|
|
["Sales & Marketing", "Sales materials, contacts, RMAs, marketing", "Sales"],
|
|
["Shipping & Receiving", "Shipping paperwork, receiving, RMA handoffs", "Shipping"],
|
|
["Accounting & Finance (restricted)", "Sage, QuickBooks, invoices, purchase orders, financial reports", "Accounting only"],
|
|
["Human Resources (restricted)", "Payroll, OSHA 300, safety training, personnel files", "HR only"],
|
|
["IT (restricted)", "IT software, drivers, server tools", "IT only"],
|
|
]
|
|
restricted = {6, 7, 8}
|
|
add_table(["Proposed department area", "What would live here", "Primary owner"], prop,
|
|
widths=[2.4, 3.8, 1.3], sensitive_rows=restricted)
|
|
|
|
# ---- Section 5: where things move ----
|
|
doc.add_heading("5. Where today's sensitive items would move", level=1)
|
|
doc.add_paragraph("A few concrete examples of how today's exposed folders would be relocated and protected:")
|
|
moves = [
|
|
["Payroll", "Q: (open to all)", "Human Resources (restricted)"],
|
|
["OSHA 300 / Safety Training", "Q: (open to all)", "Human Resources (restricted)"],
|
|
["Purchase Orders", "Q: (open to all)", "Accounting & Finance (restricted)"],
|
|
["QuickBooks files (QBfiles)", "T: (open to all)", "Accounting & Finance (restricted)"],
|
|
["Sage accounting", "S: (open to all)", "Accounting & Finance (restricted)"],
|
|
["Engineering (ENGR, ECOs, FMEA)", "Spread across T:, Y:, B:", "Engineering"],
|
|
["“Do not use” / old / personal folders", "Various drives", "Archived or removed (with your OK)"],
|
|
]
|
|
add_table(["Item", "Where it is now", "Proposed new home"], moves, widths=[2.6, 2.3, 2.6])
|
|
|
|
# ---- Section 6: what we need ----
|
|
doc.add_heading("6. What we need from you", level=1)
|
|
doc.add_paragraph("To build the plan, please help us with the following. A short call works too if that's easier.")
|
|
for b in [
|
|
"Confirm your department list (the areas in Section 4 are our starting guess).",
|
|
"Fill in the access plan below — who should be able to add/edit files in each area, and who only "
|
|
"needs to view them.",
|
|
"Tell us exactly who should have access to the restricted areas (Accounting, HR, IT).",
|
|
"Provide a list of employees by department (or an org chart) so we can set up the groups.",
|
|
"Let us know of any old folders you already know are safe to clean up.",
|
|
]:
|
|
doc.add_paragraph(b, style="List Bullet")
|
|
|
|
doc.add_heading("Access plan — please fill in", level=2)
|
|
doc.add_paragraph("For each area, write which departments (or people) need full access vs. view-only. "
|
|
"Anyone not listed simply won't have access to that area.")
|
|
acc = [
|
|
["Company (All Staff)", "All staff", "—"],
|
|
["Engineering", "", ""],
|
|
["Manufacturing & Production", "", ""],
|
|
["Quality & Calibration", "", ""],
|
|
["Sales & Marketing", "", ""],
|
|
["Shipping & Receiving", "", ""],
|
|
["Accounting & Finance (restricted)", "", ""],
|
|
["Human Resources (restricted)", "", ""],
|
|
["IT (restricted)", "", ""],
|
|
]
|
|
t = add_table(["Department area", "Full access (add / edit)", "View-only"], acc,
|
|
widths=[2.4, 2.8, 2.3], sensitive_rows={6, 7, 8})
|
|
# give the fill-in rows a little height
|
|
for row in t.rows[1:]:
|
|
trPr = row._tr.get_or_add_trPr()
|
|
h = OxmlElement("w:trHeight"); h.set(qn("w:val"), "420"); h.set(qn("w:hRule"), "atLeast")
|
|
trPr.append(h)
|
|
|
|
# ---- Section 7: how we do it safely ----
|
|
doc.add_heading("7. How we'll do this safely", level=1)
|
|
for b in [
|
|
"We start from your answers and send back a clear “who sees what” plan for your sign-off.",
|
|
"Nothing changes until you approve it.",
|
|
"We make the changes in stages, one area at a time, and confirm each department can still reach "
|
|
"their files before moving on.",
|
|
"If anyone is missing access after a change, it's a quick fix — we add them to the right group.",
|
|
"We keep a full backup of the current setup so any change can be reversed.",
|
|
]:
|
|
doc.add_paragraph(b, style="List Bullet")
|
|
|
|
doc.add_paragraph()
|
|
close = doc.add_paragraph()
|
|
r = close.add_run("Questions or want to walk through this together? We're happy to jump on a call.")
|
|
r.italic = True; r.font.color.rgb = GREY
|
|
|
|
out = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
|
"Dataforth-Shared-Drives-Reorganization-Plan.docx")
|
|
doc.save(out)
|
|
print("WROTE:", out)
|
|
print("paragraphs:", len(doc.paragraphs), "tables:", len(doc.tables))
|