Applied Module 12 · The Nick E. Playbook

Occupancy & Revenue Pulse Reporter

What you'll learn

~50 min
  • Standardize messy PMS exports into a unified KPI data model
  • Calculate week-over-week and month-to-date trend deltas automatically
  • Generate executive narrative text from metrics (AI-assisted 'risks + actions' section)
  • Package owner-ready HTML and PDF report output

What you’re building

Every Friday afternoon, Nick sits down to build the weekly report for his regional manager. He pulls a CSV from Yardi, opens Excel, manually calculates occupancy percentage, eyeballs which direction the numbers moved since last week, types up a paragraph about risks and actions, pastes in some charts, and emails the whole thing as a PDF. The process takes about 90 minutes. It is the same 90 minutes every single week, and the format never changes.

You are going to build a CLI tool that does the entire thing in one command. Feed it the Yardi export CSV, and it produces a polished HTML report with KPI cards, trend arrows, interactive charts, and an auto-generated “Risks & Actions” narrative — plus a PDF version ready to email. The report covers the metrics that matter at a 413-unit luxury community: occupancy percentage, pre-lease percentage, renewal rate, delinquency, concessions, and leasing velocity. Week-over-week and month-to-date deltas are calculated automatically so Nick can see at a glance whether the building is trending up or down.

💬90 minutes back, every week

Nick spends 90 minutes building this report manually. That is 78 hours a year — almost two full work weeks — copying numbers between systems and formatting cells. This tool gives him those hours back. More importantly, it eliminates the transcription errors that happen when you are manually pulling numbers at 4 PM on a Friday. The report is only as good as the data, and a machine does not fat-finger a decimal point.

Software pattern: Data ingestion, KPI calculation, narrative generation, dual-format export

Read raw tabular data, compute derived metrics and trend deltas, generate human-readable narrative from the numbers, and export to both web (HTML) and print (PDF) formats. This pattern appears in financial reporting, healthcare quality dashboards, logistics scorecards — anywhere a human needs to present metrics to a stakeholder on a recurring schedule.

🔍Property management primer: the numbers that run a building

If you have never worked in property management, here is what these KPIs mean and why they matter:

  • Yardi is property management software (PMS). It tracks leases, rent payments, maintenance, and accounting. Most large management companies use Yardi, RealPage, or Entrata. Yardi is the 800-pound gorilla. It exports data as CSVs, and those exports are the raw material for every report a property manager produces.
  • Occupancy % is the number of occupied units divided by total units. At the Concord (413 units), if 397 are occupied, occupancy is 96.1%. The target for a stabilized luxury property is 94-97%. Below 94% means revenue is leaking. Above 97% means you might be pricing too low.
  • Pre-lease % is the percentage of units that are leased for a future date. If 8 units are vacant but 5 already have signed leases starting next month, your pre-lease rate is high and the vacancy is temporary. Pre-lease percentage tells you whether today’s vacancy is a problem or just a timing gap.
  • Leasing velocity is how many new leases you sign per week (or per month). At 413 units with 12-month leases, roughly 34 leases expire per month. If leasing velocity matches or exceeds expirations, the building stays full. If velocity drops, vacancy rises a month or two later. It is a leading indicator.
  • Concessions are discounts offered to attract new residents — one month free, reduced rent for the first 3 months, waived application fees. They are a tool for filling units, but they reduce effective rent. A spike in concessions means the market is softening or the property is struggling to compete.
  • Delinquency is unpaid rent as a percentage of total rent due. At a luxury property like the Concord, delinquency should be under 2%. Higher than that signals collection problems, economic stress in the resident base, or both.
  • Renewal rate is the percentage of expiring leases where the resident signs a new lease instead of moving out. High renewal rates (above 55-60%) reduce turnover costs and maintain occupancy. Each turnover costs $3,000-5,000 in make-ready, vacancy loss, and leasing costs.

Who this is for

Nick is the General Manager at the Concord Crystal City, a 413-unit, 18-story Bozzuto-managed luxury apartment community in Arlington, VA. He reports to a Regional Vice President who oversees six properties. Every Friday, the RVP expects a one-page pulse report from each GM. Nick has never written a line of code. He uses Excel daily, Yardi constantly, and his phone for everything else. He is practical: if a tool saves him time, he will use it. If it requires 30 minutes of setup, he will not.


The showcase

The finished tool produces:

  • KPI cards across the top: occupancy %, pre-lease %, renewal rate, delinquency %, average concession, leasing velocity (leases/week). Each card shows the current value, a trend arrow (up/down/flat), and the week-over-week delta.
  • WoW and MTD trend section: a table showing this week vs. last week vs. month-to-date for every KPI, with green/red color coding for favorable/unfavorable movement.
  • Interactive Chart.js visualizations: occupancy trend line (last 12 weeks), leasing velocity bar chart (last 8 weeks), delinquency waterfall, and concession spend over time.
  • Auto-generated “Risks & Actions” narrative: three to five sentences summarizing what moved, what is at risk, and what actions Nick should highlight. Written in the tone of a property manager briefing a regional, not a robot reciting numbers.
  • HTML report: opens in any browser, dark professional styling with the Concord’s teal accent color, print-friendly layout.
  • PDF export: generated from the HTML using Puppeteer, ready to attach to an email.
  • Sample Yardi-format data: a realistic CSV so you can test immediately without access to a real Yardi system.

The prompt

Start your AI CLI tool in an empty directory and paste this prompt:

Build a Node.js CLI tool called pulse-reporter that reads a property management
CSV export and generates a weekly KPI report with trend analysis, interactive
charts, and an auto-generated narrative summary. The output is both an HTML
report and a PDF.
PROJECT STRUCTURE:
pulse-reporter/
package.json
src/
cli.js (entry point, argument parsing)
data-loader.js (CSV parser with Yardi format handling)
kpi-engine.js (KPI calculations, trend deltas, thresholds)
narrative.js (auto-generated risks & actions text)
chart-builder.js (Chart.js configuration generators)
report-builder.js (HTML report assembly using Handlebars)
pdf-export.js (Puppeteer-based HTML-to-PDF conversion)
sample-data.js (generates realistic Yardi-format test CSV)
templates/
report.hbs (Handlebars HTML report template)
static/
style.css (report styling)
REQUIREMENTS:
1. CLI INTERFACE (src/cli.js)
- Usage: node src/cli.js [options] <csv-file>
- --output or -o: output directory for report files (default: ./pulse-report)
- --week-ending or -w: report week-ending date in YYYY-MM-DD format
(default: most recent Friday)
- --property or -p: property name override (default: "Concord Crystal City")
- --units or -u: total unit count (default: 413)
- --pdf: also generate a PDF version (requires Puppeteer)
- --generate-sample: generate sample CSV data and exit
- --previous or -prev: path to previous week's CSV for trend comparison
(if not provided, trends show "N/A" instead of deltas)
2. DATA LOADER (src/data-loader.js)
Parse a Yardi-format CSV export. Yardi exports vary by report, but the
tool should handle this common format:
Expected columns (case-insensitive, flexible matching):
- Unit Number (or Unit, Unit #, UnitID)
- Unit Type (or FloorPlan, Floor Plan, Type)
- Sq Ft (or SqFt, Square Feet, Area)
- Market Rent (or MarketRent, Asking Rent)
- Actual Rent (or CurrentRent, Lease Rent, Charge)
- Status (or Unit Status, Occ Status)
Values: "Occupied", "Vacant", "Vacant-Leased", "Notice", "Down/Model"
- Lease Start (or LeaseStart, Move-In, MoveIn)
- Lease End (or LeaseEnd, LeaseExpiration, Expiration)
- Resident Name (or Resident, Tenant, Name) -- optional
- Move-In Date (or MoveInDate, Actual Move-In)
- Move-Out Date (or MoveOutDate, Actual Move-Out) -- blank if still occupied
- Concession (or Concession Amount, MonthlyConc) -- dollar amount, 0 if none
- Balance (or Balance Due, Delinquent Amount, AR Balance) -- current unpaid balance
- Renewal Status (or RenewalStatus) -- "Renewed", "MTM", "Pending", "NoticeGiven", blank
Auto-detect column names by fuzzy matching headers. Strip dollar signs,
commas, and whitespace from numeric fields. Parse dates flexibly (MM/DD/YYYY,
YYYY-MM-DD, M/D/YY). Log a warning for any unrecognized columns. Fail with
a clear error if critical columns (Unit Number, Status, Market Rent) are
missing.
3. KPI ENGINE (src/kpi-engine.js)
Calculate these KPIs from the parsed data:
a. OCCUPANCY
- Physical Occupancy %: occupied units / total leasable units
(exclude Down/Model units from denominator)
- Economic Occupancy %: actual collected rent / potential gross rent
- Vacant unit count and list
- Vacancy cost: sum of market rent for all vacant-unrented units
b. PRE-LEASE
- Pre-lease %: (occupied + vacant-leased) / total leasable units
- Units currently "Vacant-Leased" (signed lease but not yet moved in)
- Expected move-in dates for pre-leased units
c. LEASING VELOCITY
- New leases signed this week: count of units where Lease Start falls
within the report week (week-ending date minus 6 days to week-ending date)
- Leasing velocity: new leases / 7 days (as a daily rate)
- Trailing 4-week average for comparison
d. RENEWAL RATE
- Leases expiring this month: count where Lease End is in the report month
- Renewed: count with Renewal Status = "Renewed"
- MTM (month-to-month): count with Renewal Status = "MTM"
- Notice given: count with Renewal Status = "NoticeGiven"
- Renewal rate %: renewed / (renewed + noticeGiven)
(exclude MTM and pending from the calculation)
e. DELINQUENCY
- Total delinquent balance: sum of all positive Balance values
- Delinquency %: total delinquent / total monthly rent roll
- Count of delinquent units (balance > $0)
- Top 5 delinquent accounts (unit number and balance, no names in report)
f. CONCESSIONS
- Total monthly concession spend: sum of Concession column
- Average concession per concessed unit
- Concession as % of gross potential rent
- Count of units receiving concessions
TREND CALCULATION:
If a previous week's CSV is provided, calculate:
- Week-over-week delta for each KPI (current - previous)
- Direction: "up", "down", or "flat" (within 0.1% tolerance)
- Color coding: green if favorable (occupancy up, delinquency down),
red if unfavorable
THRESHOLDS (flag KPIs outside healthy ranges):
- Occupancy < 94%: flag red
- Delinquency > 2%: flag red
- Renewal rate < 50%: flag yellow
- Concessions > 3% of GPR: flag yellow
- Leasing velocity < 5 leases/week: flag yellow
4. NARRATIVE GENERATOR (src/narrative.js)
Generate a 3-5 sentence "Risks & Actions" paragraph from the KPI results.
Use template-based generation (not an LLM call -- this runs offline):
Rules:
- If occupancy dropped WoW: "Occupancy declined [X]pp to [Y]%, driven by
[N] move-outs against [M] move-ins this week."
- If delinquency exceeds 2%: "Delinquency stands at [X]% ($[Y] outstanding
across [N] units). Collections follow-up is the priority this week."
- If renewal rate is below 55%: "Renewal conversion is tracking at [X]%.
[N] leases expire next month -- outreach to undecided residents is recommended."
- If leasing velocity is strong: "Leasing velocity remains healthy at
[X] leases/week, [Y]% above the trailing 4-week average."
- If concessions are rising: "Concession spend increased to $[X] this
month ([Y]% of GPR). Monitor market comps to confirm pricing position."
- Always close with one forward-looking action statement.
- Tone: professional but direct. Written as a GM briefing a regional VP --
not a chatbot summary.
5. CHART BUILDER (src/chart-builder.js)
Generate Chart.js configuration objects (rendered client-side in the HTML):
a. Occupancy Trend Line: 12-week x-axis (use placeholder labels if only
1 week of data), occupancy % on y-axis, target line at 95%.
Dark theme: line color #0F766E (teal), grid #1e1e2a, background #111118.
b. Leasing Velocity Bar Chart: 8-week bars showing leases signed per week.
Color: #0F766E for bars, #374151 for grid.
c. Delinquency Breakdown: horizontal bar showing current-month delinquency
by aging bucket (0-30 days, 31-60, 61-90, 90+).
d. Concession Trend: area chart showing concession spend over the last
8 weeks as a percentage of GPR.
Each chart config should be a JSON object that the HTML template injects
into a <script> tag and renders with Chart.js loaded via CDN.
6. REPORT BUILDER (src/report-builder.js)
Assemble the HTML report using Handlebars:
Layout:
- Header: property name, management company ("Bozzuto Management"),
report period ("Week Ending March 7, 2026"), generated timestamp
- KPI Card Row: 6 cards in a responsive grid, each showing:
metric name, value (large), trend arrow (unicode or SVG), WoW delta,
status indicator (green/yellow/red dot based on thresholds)
- Trend Table: all KPIs in rows, columns for This Week, Last Week,
WoW Delta, MTD, status
- Charts Section: 2x2 grid of Chart.js canvases
- Risks & Actions: the auto-generated narrative in a highlighted card
- Unit Detail Appendix (collapsible): table of all vacant and notice
units with unit number, type, market rent, status, and days vacant
- Footer: "Generated by Pulse Reporter | Data source: Yardi export"
STYLING (static/style.css):
- Dark professional theme: background #09090b, cards #141414,
borders #1e1e2a, text #e5e5e5
- Accent color: #0F766E (Concord teal)
- KPI cards with subtle gradient borders
- Trend arrows: green (#10b981) for favorable, red (#ef4444) for
unfavorable, gray (#6b7280) for flat
- Print CSS: white background, black text, no interactive elements
- Responsive: stacks to single column on mobile
7. PDF EXPORT (src/pdf-export.js)
Use Puppeteer to convert the HTML report to PDF:
- Launch headless Chrome
- Load the generated HTML file
- Wait for Chart.js to render (wait for canvas elements)
- Export as Letter-size PDF with 0.5-inch margins
- Filename: pulse-report-YYYY-MM-DD.pdf
8. SAMPLE DATA GENERATOR (src/sample-data.js)
When --generate-sample is passed, create two CSV files:
- sample-current-week.csv: 413 units for the Concord Crystal City
with realistic data:
- 397 occupied, 10 vacant, 4 vacant-leased, 2 down/model
- Mix of unit types: Studio (45), 1BR (180), 2BR (150), 3BR (38)
- Market rents: Studio $1,850-2,100, 1BR $2,200-2,800,
2BR $2,900-3,600, 3BR $3,800-4,500
- 8 units with concessions ($200-500/month)
- 12 units with delinquent balances ($500-4,200)
- 6 new leases signed this week (Lease Start in current week)
- 15 leases expiring this month: 9 renewed, 2 MTM, 2 notice, 2 pending
- sample-previous-week.csv: same property one week earlier with slightly
different numbers (398 occupied, 9 vacant, occupancy 0.4% higher,
delinquency 0.2% lower) to demonstrate trend calculation
Use realistic Yardi column headers: "Unit Number", "Floor Plan",
"Sq Ft", "Market Rent", "Actual Rent", "Unit Status", "Lease Start",
"Lease End", "Concession", "Balance Due", "Renewal Status"
DEPENDENCIES: csv-parser, handlebars, puppeteer, chalk, commander
💡PDF generation is optional

The --pdf flag uses Puppeteer, which downloads a Chromium binary (~300 MB). If you just want the HTML report, skip the --pdf flag. The HTML report is fully self-contained and looks just as good printed from a browser via Ctrl+P.


What you get

After your AI CLI tool finishes, set up the project:

Terminal window
cd pulse-reporter
npm install

Generate sample data and run the report

Terminal window
# Generate realistic Yardi-format test CSVs
node src/cli.js --generate-sample -o ./test-report
# Run the report with both current and previous week data
node src/cli.js ./test-report/sample-current-week.csv \
--previous ./test-report/sample-previous-week.csv \
--week-ending 2026-03-06 \
-o ./test-report
# Also generate a PDF (requires Puppeteer)
node src/cli.js ./test-report/sample-current-week.csv \
--previous ./test-report/sample-previous-week.csv \
--week-ending 2026-03-06 \
-o ./test-report \
--pdf

Open ./test-report/pulse-report.html in your browser. You should see:

  • Six KPI cards across the top with occupancy around 96.1%, pre-lease around 97.1%, renewal rate around 81.8%, delinquency around 1.4%, average concession around $340, and leasing velocity around 6 leases/week.
  • Trend arrows: occupancy should show a red down arrow (dropped from previous week), delinquency should show a red up arrow (increased from previous week), and leasing velocity should be green or flat.
  • Four charts: occupancy trend line (only two data points with sample data, but the chart frame is there), leasing velocity bars, delinquency breakdown by aging, and concession trend.
  • Risks & Actions narrative: something like “Occupancy declined 0.4pp to 96.1%, driven by 3 move-outs against 2 move-ins this week. Delinquency ticked up to 1.4% — within threshold but trending unfavorably. Leasing velocity remains healthy at 6 leases/week. Three vacant-unrented units represent $8,400/month in vacancy loss. Priority this week: convert the 2 pending renewals before month-end.”
  • Vacant unit appendix (collapsed by default): a table listing all vacant and notice units with details.

Common issues and fixes

ProblemFollow-up prompt
CSV parsing fails on Yardi exportThe CSV parser is failing on my Yardi export. Yardi sometimes includes a header row with the report title before the actual column headers. Add logic to skip non-data rows at the top: scan the first 10 lines and find the row that contains "Unit" or "Unit Number" as a header -- that is the real header row. Skip everything above it.
KPI cards show NaNSeveral KPI values show NaN. This happens when the Status column has unexpected values like "Occupied-NTV" or "Vacant-Rented" that the parser does not recognize. Map all status variants to the canonical set: anything starting with "Occupied" maps to "Occupied", "Vacant-Leased" or "Vacant-Rented" maps to "Vacant-Leased", "Vacant" stays "Vacant", "Down" or "Model" or "Down/Model" maps to "Down/Model".
Trend arrows all show N/A even with previous fileThe trend calculation is returning N/A for everything even though I passed --previous. Check that the data-loader correctly parses both files and that the KPI engine receives both parsed datasets. The previous file might have a slightly different column order or header spelling -- the fuzzy column matcher should handle both files identically.
Charts do not render in PDFThe PDF shows blank rectangles where charts should be. Puppeteer is capturing the page before Chart.js finishes rendering. In pdf-export.js, after loading the HTML, add a waitForFunction that checks whether all canvas elements have been drawn: await page.waitForFunction('document.querySelectorAll("canvas").length > 0 && typeof Chart !== "undefined"'). Then add a 1-second delay to let animations complete before capturing.

🔧

When Things Go Wrong

Use the Symptom → Evidence → Request pattern: describe what you see, paste the error, then ask for a fix.

Symptom
Occupancy percentage is higher than expected
Evidence
The report shows 98.5% occupancy but I know we have 15 vacant units. 15 out of 413 should be about 96.4%.
What to ask the AI
"The occupancy calculation is including Down/Model units as occupied. Down units and model units are not revenue-generating and should be excluded from both the numerator and denominator. Update kpi-engine.js: total leasable = total units - down/model count. Occupied = units with status 'Occupied'. Occupancy = occupied / total leasable. Also make sure 'Vacant-Leased' units are NOT counted as occupied for physical occupancy -- they are leased but not yet moved in."
Symptom
Delinquency percentage seems way too high
Evidence
The report shows 8.3% delinquency but our actual delinquency is under 2%. The total delinquent balance shown is $85,000 which is wrong.
What to ask the AI
"The delinquency calculation is summing ALL balance values including credits (negative balances). Credits are prepayments or deposits -- they should not count as delinquency. Filter to only positive Balance values before summing. Also check that the denominator is the monthly rent roll (sum of Actual Rent for occupied units), not the annual amount or the Market Rent total."
Symptom
Renewal rate shows 0% even though we have renewals
Evidence
The renewal rate card shows 0% and 0 renewed, but I can see 'Renewed' values in the Renewal Status column of my CSV.
What to ask the AI
"The renewal rate calculation is not finding any matching Renewal Status values. The issue is likely case sensitivity or whitespace. Normalize the Renewal Status values: trim whitespace, convert to lowercase, then match against 'renewed', 'mtm', 'noticegiven', 'pending'. Also check if the column header is 'RenewalStatus' vs 'Renewal Status' -- the fuzzy matcher should handle both."
Symptom
The narrative section is empty or shows placeholder text
Evidence
The Risks & Actions section says '[narrative placeholder]' or is completely blank.
What to ask the AI
"The narrative generator is not being called or its output is not being passed to the Handlebars template. Check that report-builder.js calls narrative.generate(kpiResults) and passes the result as a 'narrative' variable to the template context. In the template, make sure it renders with triple braces {{{narrative}}} (not double braces) so HTML formatting is preserved."
Symptom
PDF export creates a blank or single-page document cutting off content
Evidence
The PDF is only one page and cuts off after the KPI cards. The full HTML report has much more content below.
What to ask the AI
"Puppeteer's default PDF settings are clipping the content. In pdf-export.js, set the PDF options to: format 'Letter', printBackground true, preferCSSPageSize false, and remove any fixed height setting. Add margin: { top: '0.5in', bottom: '0.5in', left: '0.5in', right: '0.5in' }. The report should flow naturally across multiple pages. Also make sure the CSS does not have any fixed viewport height that would constrain the page."

How it works

The tool follows a clean data pipeline:

  1. CLI (cli.js) parses arguments with Commander, validates that the CSV file exists, and orchestrates the pipeline stages: load data, calculate KPIs, generate narrative, build charts, assemble report, optionally export PDF.

  2. Data Loader (data-loader.js) reads the Yardi CSV with flexible column matching. It normalizes column names, parses dates and currency values, maps status variants to canonical values, and returns a structured array of unit records. It also loads the previous week’s data if provided.

  3. KPI Engine (kpi-engine.js) takes the parsed unit records and calculates every metric. It groups units by status, sums rent and balance fields, counts leases by date range, and computes percentages. If previous-week data is available, it runs the same calculations on that data and computes deltas. It returns a structured KPI results object with current values, previous values, deltas, directions, and threshold flags.

  4. Narrative Generator (narrative.js) takes the KPI results and applies template rules to produce a paragraph. It checks thresholds, identifies the most significant movements, and assembles sentences in priority order. The output reads like a human wrote it because the templates are written in a human voice — the logic just selects which templates to use and fills in the numbers.

  5. Chart Builder (chart-builder.js) generates Chart.js configuration objects as JSON. The HTML template embeds these in <script> tags so the charts render client-side when the report is opened in a browser. Each chart is configured with the dark theme colors and responsive sizing.

  6. Report Builder (report-builder.js) compiles the Handlebars template with all the data: KPI results, narrative, chart configs, property metadata, and the vacant unit list. It writes the HTML file and copies the CSS.

  7. PDF Export (pdf-export.js) launches Puppeteer, loads the HTML report, waits for charts to render, and exports to PDF with print-friendly CSS applied.


Customize it

Add email distribution

Add a --email flag that accepts one or more email addresses. After generating
the report, send it as an email with the HTML report as the email body and the
PDF as an attachment. Use nodemailer with SMTP configuration from environment
variables (SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS). Set the subject to
"Concord Crystal City -- Weekly Pulse Report -- Week Ending [date]".
Set the From to "Concord Property Ops <reports@concordcrystalcity.com>".

Add historical trend storage

Add a --save-history flag that stores each week's KPI results as a JSON file
in a history/ directory (one file per week, named kpi-YYYY-MM-DD.json).
When generating a report, automatically load all history files and use them
to populate the 12-week occupancy trend chart and 8-week velocity chart with
real data instead of placeholders. This way the charts get richer every week
as Nick runs the tool.

Add comp set comparison

Add a --comp flag that accepts a CSV file with competitor property data
(property name, unit count, occupancy %, average rent). Add a "Market Position"
section to the report showing the Concord's metrics versus the comp set average.
Show a bar chart comparing occupancy and average rent across properties.
Highlight where the Concord is above or below the comp set average.

Add Yardi API connector

Add a --yardi-api flag that pulls data directly from the Yardi API instead
of a CSV file. Accept the Yardi server URL, database name, and API key from
environment variables (YARDI_URL, YARDI_DB, YARDI_KEY). Call the Yardi
ResidentData and UnitData API endpoints, transform the JSON response into
the same format the CSV parser produces, and feed it into the existing
pipeline. This eliminates the CSV export step entirely.

Try it yourself

  1. Generate the pulse reporter with the prompt above.
  2. Run npm install and generate sample data with --generate-sample.
  3. Run the report with both current and previous week CSVs.
  4. Open the HTML report. Check every KPI card — do the numbers make sense for a 413-unit property?
  5. Read the Risks & Actions narrative. Does it sound like something a property manager would write?
  6. Collapse and expand the vacant unit appendix. Is the detail useful?
  7. If you have Puppeteer installed, generate the PDF with --pdf and verify it looks clean when printed.
  8. Change a value in the sample CSV (set 10 more units to “Vacant”) and re-run. Does the report reflect the change?

Key takeaways

  • Automated reporting eliminates transcription errors and saves hours every week. The data goes from Yardi to finished report without a human copying numbers between systems. The report is reproducible — run it twice with the same data, get the same result.
  • Trend deltas are more valuable than point-in-time snapshots. Occupancy at 96% is fine. Occupancy at 96% and dropping 0.4% per week for three weeks is a problem. The week-over-week calculation surfaces the direction of the business, not just the position.
  • Template-based narrative generation works for recurring reports. You do not need an LLM to write a weekly summary. A set of well-written conditional templates that select sentences based on KPI thresholds produces professional, consistent output. It is deterministic, fast, and works offline.
  • Dual-format export (HTML + PDF) covers both audiences. The regional VP wants a PDF attachment. Nick wants an interactive version he can drill into. Same data, two outputs, one command.
  • Flexible column matching makes the tool resilient to Yardi export variations. Different Yardi reports use different column names. Fuzzy matching on headers means the tool works with whatever Nick exports, not just one specific report format.

KNOWLEDGE CHECK

The Concord has 413 total units. 2 are down as models. Of the remaining 411 leasable units, 395 are occupied, 10 are vacant, 4 are vacant-leased, and 2 are on notice. What is the physical occupancy percentage?


What’s next

In Lesson 6, you will build the Daily Ops Command Center — a unified daily dashboard that integrates the resident communications tool, maintenance triage board, renewal planner, vendor orchestrator, and this pulse reporter into a single operating hub with a “Morning Run” one-click automation. It is the capstone that ties every tool together into Nick’s daily workflow.