I can't run code or write files in this session, so the script below is untested. Run it once and use the checks at the end.
build_budget.py
from openpyxl import Workbook
from openpyxl.formatting.rule import FormulaRule
from openpyxl.styles import Alignment, Border, Font, PatternFill, Side
from openpyxl.utils import get_column_letter
OUT = "household_budget.xlsx"
MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
INCOME = {
"Salary": [5200] * 12,
"Side income": [300, 250, 400, 350, 300, 200, 0, 300, 350, 250, 300, 200],
"Other income": [0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 0],
}
EXPENSES = {
"Rent / Mortgage": [1800] * 12,
"Utilities": [220, 210, 190, 160, 150, 170, 190, 185, 160, 170, 200, 230],
"Groceries": [620, 590, 640, 600, 610, 580, 650, 620, 600, 630, 640, 720],
"Transport": [180, 170, 190, 200, 210, 220, 260, 200, 190, 185, 180, 200],
"Insurance": [250] * 12,
"Dining out": [240, 200, 260, 230, 250, 280, 350, 260, 240, 270, 300, 420],
"Entertainment": [120, 100, 150, 130, 140, 160, 300, 120, 110, 140, 150, 350],
"Health": [60, 40, 80, 50, 45, 60, 70, 300, 50, 60, 40, 90],
"Subscriptions": [65] * 12,
"Other": [100, 80, 120, 90, 150, 110, 1500, 100, 90, 130, 110, 1400],
}
CURRENCY = '$#,##0;[Red]($#,##0);"-"'
PERCENT = '0.0%;[Red]-0.0%;"-"'
FIRST_COL, LAST_COL, TOTAL_COL = 2, 13, 14 # B..M months, N total
HEADER_FILL = PatternFill("solid", fgColor="1F3864")
SECTION_FILL = PatternFill("solid", fgColor="D9E1F2")
TOTAL_FILL = PatternFill("solid", fgColor="F2F2F2")
FLAG_FILL = PatternFill("solid", bgColor="F8CBAD", fgColor="F8CBAD")
INPUT_FONT = Font(color="0000FF")
BOLD = Font(bold=True)
TOP_BORDER = Border(top=Side(style="thin"))
wb = Workbook()
ws = wb.active
ws.title = "Budget"
ws["A1"] = "Household Budget"
ws["A1"].font = Font(bold=True, size=16)
ws["A2"] = "Blue = enter your numbers. Black = formulas. Months with expenses above income are highlighted."
ws["A2"].font = Font(italic=True, color="595959")
# Header row
HDR = 4
ws.cell(HDR, 1, "Category")
for i, m in enumerate(MONTHS):
ws.cell(HDR, FIRST_COL + i, m)
ws.cell(HDR, TOTAL_COL, "Year Total")
for c in range(1, TOTAL_COL + 1):
cell = ws.cell(HDR, c)
cell.font = Font(bold=True, color="FFFFFF")
cell.fill = HEADER_FILL
cell.alignment = Alignment(horizontal="left" if c == 1 else "right")
first, last = get_column_letter(FIRST_COL), get_column_letter(LAST_COL)
tot = get_column_letter(TOTAL_COL)
def section(row, title):
for c in range(1, TOTAL_COL + 1):
ws.cell(row, c).fill = SECTION_FILL
ws.cell(row, 1, title).font = BOLD
def data_rows(start, items):
r = start
for name, vals in items.items():
ws.cell(r, 1, name).alignment = Alignment(indent=1)
for i, v in enumerate(vals):
cell = ws.cell(r, FIRST_COL + i, v)
cell.font = INPUT_FONT
cell.number_format = CURRENCY
ws.cell(r, TOTAL_COL, f"=SUM({first}{r}:{last}{r})").number_format = CURRENCY
ws.cell(r, TOTAL_COL).font = BOLD
r += 1
return start, r - 1
def total_row(row, label, a, b):
ws.cell(row, 1, label)
for c in range(FIRST_COL, TOTAL_COL + 1):
L = get_column_letter(c)
ws.cell(row, c, f"=SUM({L}{a}:{L}{b})").number_format = CURRENCY
for c in range(1, TOTAL_COL + 1):
cell = ws.cell(row, c)
cell.font = BOLD
cell.fill = TOTAL_FILL
cell.border = TOP_BORDER
r = HDR + 1
section(r, "INCOME")
inc_a, inc_b = data_rows(r + 1, INCOME)
INC_TOT = inc_b + 1
total_row(INC_TOT, "Total Income", inc_a, inc_b)
r = INC_TOT + 2
section(r, "EXPENSES")
exp_a, exp_b = data_rows(r + 1, EXPENSES)
EXP_TOT = exp_b + 1
total_row(EXP_TOT, "Total Expenses", exp_a, exp_b)
SAV = EXP_TOT + 2
RATE = SAV + 1
ws.cell(SAV, 1, "Savings (Income - Expenses)")
ws.cell(RATE, 1, "Savings Rate (Savings / Income)")
for c in range(FIRST_COL, TOTAL_COL + 1):
L = get_column_letter(c)
ws.cell(SAV, c, f"={L}{INC_TOT}-{L}{EXP_TOT}").number_format = CURRENCY
ws.cell(RATE, c, f"=IF({L}{INC_TOT}=0,0,{L}{SAV}/{L}{INC_TOT})").number_format = PERCENT
for row in (SAV, RATE):
for c in range(1, TOTAL_COL + 1):
ws.cell(row, c).font = BOLD
for c in range(1, TOTAL_COL + 1):
ws.cell(SAV, c).border = TOP_BORDER
# Highlight months where expenses > income (header + summary rows only,
# so the input cells stay clean).
for rng, top in ((f"{first}{HDR}:{last}{HDR}", HDR),
(f"{first}{INC_TOT}:{last}{INC_TOT}", INC_TOT),
(f"{first}{EXP_TOT}:{last}{EXP_TOT}", EXP_TOT),
(f"{first}{SAV}:{last}{RATE}", SAV)):
ws.conditional_formatting.add(
rng,
FormulaRule(formula=[f"{first}${EXP_TOT}>{first}${INC_TOT}"],
fill=FLAG_FILL, font=Font(bold=True, color="9C0006")),
)
ws.column_dimensions["A"].width = 34
for c in range(FIRST_COL, TOTAL_COL + 1):
ws.column_dimensions[get_column_letter(c)].width = 11
ws.column_dimensions[tot].width = 13
ws.freeze_panes = ws.cell(HDR + 1, FIRST_COL)
ws.sheet_view.showGridLines = False
wb.save(OUT)
print("Saved", OUT)
Run it
python3 -m pip install openpyxl
python3 build_budget.py
open household_budget.xlsx
How to check it
- Formulas calculate. Excel calculates on open. If you see blanks or zeros, press Ctrl+Alt+F9 (Cmd+= on Mac). No cell should show
#DIV/0!or#NAME?. - Spot-check totals. January income should be $5,500 and January expenses $3,655, so savings are $1,845. That's a 33.5% savings rate. Year Total (column N) should equal the sum of the twelve months, both down the Total rows and across the category rows.
- Highlight. The sample data overspends in Jul (income $5,200, expenses $6,435) and Dec (income $5,400, expenses $6,325). Those two columns should turn red in the header and summary rows, and no other months should. Change a Jul input, such as Salary to 9000, and the highlight should disappear.
- Formats. Currency shows as
$1,234, negatives are red in parentheses, and zeros show as-. Savings rate shows as a percentage, and it goes red when negative. - Optional automated check. If you have LibreOffice, run
soffice --headless --convert-to pdf household_budget.xlsxand look at the PDF. You can also recalculate and read the values back withopenpyxl.load_workbook(..., data_only=True).
Notes
- Blue cells are inputs, so overwrite them freely.
- To add a category, insert a row inside the existing block, between the first and last row of that section. The
SUMranges expand automatically. Inserting after the last row won't extend the total. - Savings rate uses
IF(income=0, 0, ...)to avoid divide-by-zero errors in empty months.
Real run in an isolated folder with only this skill installed. Only the Skill and Read tools were enabled, so no command was run and no file was written. The scenario is fictional. The model could not run code in this run. The script was run by hand afterwards and builds a valid workbook, and the months that overspend (July and December) are right. However, the July and December expense totals quoted in step 3 are wrong: the script gives 5,435 and 5,525. Check the workbook itself, not the quoted figures.
하는 일
openpyxl과 pandas로 .xlsx, .csv, .tsv 파일을 만들고 바꿉니다. 새 통합 문서에서는 계산으로 나오는 값을 모두 수식으로 두고(결과를 하드코딩하지 않음), 깔끔한 머리글, 숫자와 날짜 서식, 읽기 좋은 열 너비를 갖추며, 선택적으로 색상 규칙(파랑은 입력, 검정은 수식)을 씁니다. 기존 통합 문서는 수식, 참조, 서식을 깨지 않고 수정하고, LibreOffice와 Poppler가 있으면 렌더링해서 눈으로 확인합니다. 재무 관행(음수는 빨간 괄호, 머리글에 단위, 출처는 셀 메모)과 투자은행식 레이아웃도 다룹니다.
알아 둘 점
openpyxl은 수식을 계산하지 않으므로 값은 Excel이나 Sheets에서 파일을 열 때 나타납니다. 실행 가능한 openpyxl 예제 4개가 들어 있습니다.
이런 때 좋습니다
예산표, 관리표, 보고서, 모델. 표 형식 데이터 정리와 요약, 어수선한 통합 문서 정돈.
중간 위험:.xlsx와 .csv 파일을 쓰고, Python 패키지(openpyxl, pandas)와 선택적으로 LibreOffice, Poppler를 설치할 수 있으며(Ubuntu 안내는 sudo apt-get, macOS는 brew), 끝난 뒤 AI 자신의 임시 파일을 지우라고 지시합니다. 함께 든 예제는 같은 이름의 파일을 묻지 않고 덮어써 저장하므로 실제 통합 문서를 편집하기 전에 백업하세요. openpyxl은 수식을 계산하지 않으니 숫자는 Excel에서 확인하세요. 시험 실행에서 AI는 코드를 실행하지 못했고 합계 두 개를 잘못 적었습니다. 스크립트 자체는 직접 실행해 동작을 확인했습니다. 이것은 OpenAI 스킬의 이전 개정판이며 OpenAI는 이후 자기 저장소에서 삭제했습니다. Apache-2.0, 수정 없음.