Company Performance Analyzer
Build a Python tool that analyzes cash flow and writes a Gemini-assisted report.
Introduction
30 Second Summary
A spreadsheet full of yearly figures can look convincing while hiding the direction a business is moving. A useful report needs calculations you can trace before it needs polished commentary.
In this project, you will build a command-line Company Performance Analyzer using Python with Pandas. It converts five years of financial data into traceable calculations plus grounded feedback from the Gemini API inside a Markdown report.
What You'll Build
Running analyzer.py reveals a five-year cash flow table with a transparent trend plus a concise business assessment before you open the finished report in Visual Studio Code.
By the end of this project, you'll have:
- A one-command analysis that turns a five-year CSV into a labeled cash flow table. You can compare annual totals with year-to-year changes.
- A traceable trend classification produced by Python rules. You can inspect why the result is volatile but improving overall.
- A shareable Markdown report that identifies whether Gemini or the Python fallback supplied the feedback. You can preview the rendered file in Visual Studio Code.
- Secret Mission: Add an input gate that rejects missing columns plus duplicate or incomplete five-year datasets.
Are there any prerequisites?
You need basic Python syntax. You also need access to the Gemini API Free Tier.
Before We Start
This checkpoint locks in the five-year cash flow analyzer you are about to build. You will keep its arithmetic and trend classification in Python so every result remains traceable before Gemini turns the verified metrics into prose.
Prepare Your Workspace and Gemini Access
Your analyzer needs a controlled Python workspace before it handles financial data. This keeps package behavior reproducible.
A virtual environment isolates the pinned Pandas version from packages used by other projects. The same boundary protects the Google Gen AI SDK installation.
The Gemini API also needs a credential that stays out of your files. Keeping it in the current PowerShell session prevents accidental exposure through your project.
In this step, get ready to:
- Open a Windows project folder in Visual Studio Code and confirm the Python extension is installed.
- Confirm Python 3.11 or newer is available.
- Create .venv and install pandas==3.0.5 plus google-genai==2.20.0.
- Access Google AI Studio and keep your Gemini API key inside the current PowerShell session.
Confirm your Windows Python setup
Visual Studio Code treats an open folder as a workspace. Its integrated terminal starts inside that folder.
- Press the Windows key to open Windows search.
- Type Visual Studio Code into the search field.
- Press Enter to open Visual Studio Code.
You'll see the Visual Studio Code start screen.
- Select File from the top menu.
- Select Open Folder....
- Choose your Desktop in the Windows folder picker.
- Select New Folder.
- Enter company-performance-analyzer as the folder name.
- Select the new company-performance-analyzer folder.
- Select Select Folder.
- Select Yes, I trust the authors if Workspace Trust appears.
You'll see company-performance-analyzer in the Explorer view. This confirms that new project files will land in the correct folder.
- Press Ctrl+Shift+X to open the Extensions view.
- Type python into the extension search field.
- Select the Python extension published by Microsoft.
- Select Install if the extension is not already installed.
You'll see the Python extension in your installed extensions. Visual Studio Code can now discover interpreters for this workspace.
- Select View from the top menu.
- Select Terminal.
- Choose PowerShell from the terminal profile menu if the terminal tab shows a different shell.
- Check the installed Python 3 version by running this command:
py -3 --version
What does this check prove?
The Windows Python launcher reports the Python 3 version it can use. The reported version must be 3.11 or newer because the pinned Pandas release requires that minimum.
✔️ I see the required version
That clears the first compatibility hurdle. Your interpreter can run both pinned packages.
ⓧ I see an older version
Your current interpreter is below the project's minimum version. A newer Python installation brings the workspace into the supported range.
- Open the official Windows releases page.
- Download a stable Python release numbered 3.11 or newer.
- Approve any Windows security prompt only after confirming the installer came from Python.org.
- Complete the official Python installation.
- Restart Visual Studio Code.
- Return to the company-performance-analyzer folder.
- Open a fresh integrated PowerShell terminal.
- Repeat the version check by running:
py -3 --version
Why repeat the check?
Restarting lets Visual Studio Code discover the new installation. The repeated command confirms that the Windows launcher now finds a supported version.
ⓧ Command not found
The Windows Python launcher is unavailable. Installing Python from the official Windows page adds the interpreter required by this project.
- Open the official Windows releases page.
- Download a stable Python release numbered 3.11 or newer.
- Approve any Windows security prompt only after confirming the installer came from Python.org.
- Complete the official Python installation.
- Restart Visual Studio Code.
- Return to the company-performance-analyzer folder.
- Open a fresh integrated PowerShell terminal.
- Repeat the version check by running:
py -3 --version
Why repeat the check?
The repeated command confirms that Windows can now find the installed Python interpreter. A supported version means the environment is ready for the pinned dependencies.
Build the isolated project environment
A virtual environment keeps this analyzer's packages inside .venv. The requirements file records the exact package versions needed to reproduce the workspace.
- Create .venv from the PowerShell terminal used for the version check by running:
python -m venv .venv
What does this command do?
The command creates a local Python environment inside .venv. Packages installed there stay isolated from your other Python projects.
You'll see a new .venv folder in the Explorer view.
- Activate .venv in the current PowerShell terminal by running:
.\.venv\Scripts\Activate.ps1
What does activation change?
Activation directs python and pip to the executables inside .venv. The terminal keeps using that environment until you close the session or deactivate it.
You'll see (.venv) at the start of the PowerShell prompt. That prefix confirms the environment is active.
Activation script blocked?
Confirm that the terminal profile is PowerShell. Confirm that .venv appears in the Explorer view.
If PowerShell blocks the script through a local policy, use the guidance shown for your Windows configuration before trying again.
Help me activate this Windows virtual environment
- Select File from the top menu.
- Select New File.
- Press Ctrl+S to open the save dialog.
- Choose the company-performance-analyzer folder.
- Enter requirements.txt in the file name field.
- Select Save.
- Add the pinned dependencies by pasting this content into requirements.txt:
pandas==3.0.5
google-genai==2.20.0
What does this file control?
- The pandas==3.0.5 entry pins the library used to load the CSV and calculate financial values.
- The google-genai==2.20.0 entry pins the SDK used to request Gemini feedback.
- Exact pins give every installation the same dependency versions.
- Press Ctrl+S to save requirements.txt.
You'll see requirements.txt in the Explorer view with both version pins saved.
✔️ Awesome, I've got everything!
Your requirements file now contains both pinned dependencies. Keep it saved before installing them.
ⓧ I'd like to double check the full code
pandas==3.0.5
google-genai==2.20.0
- Install both pinned dependencies into the active environment by running:
python -m pip install -r requirements.txt
What does this installation use?
Pip reads each exact requirement from requirements.txt. Because (.venv) is active, the packages are installed inside the project environment.
You'll see package download and installation progress before PowerShell returns to the prompt.
Dependency installation failed?
- Confirm the prompt begins with (.venv).
- Confirm requirements.txt contains both exact package pins.
- Retry the command after checking your internet connection.
Help me diagnose this dependency installation
- Press Ctrl+Shift+P to open the Command Palette.
- Enter Python: Select Interpreter.
- Select the interpreter labeled .venv.
You'll see .venv as the active interpreter in the Visual Studio Code status bar. The current PowerShell prompt remains activated.
Set Gemini access for this session
A Gemini API key authorizes the SDK request used later in the analyzer. Process-scoped storage keeps the credential out of requirements.txt and every future Python file.
- Open the official Gemini API key guide in your browser.
- Use the guide to reach the API Keys page in Google AI Studio.
- Sign in to Google AI Studio if prompted.
You'll see the API Keys page for the Google projects available to your account. Choose the path below that matches what you see.
✔️ I already have a suitable key
- Select a key attached to the project you want to use for this analyzer.
- Copy the key without placing it in a project file.
ⓧ I need to create a key
- Select Create API key.
- Choose a Google project that you control.
- Copy the new key without placing it in a project file.
ⓧ My project is not listed
- Select Dashboard.
- Select Projects.
- Select Import projects.
- Import a Google project that you control.
- Return to the API Keys page.
- Select Create API key.
- Copy the new key without placing it in a project file.
Handling a live API key can feel delicate. This method limits it to the active PowerShell process.
- Return to the activated PowerShell terminal from earlier.
- Copy the command below.
- Replace your-api-key-here with your copied key before running it.
$Env:GEMINI_API_KEY = "your-api-key-here"
How is the key protected?
The GEMINI_API_KEY environment variable is available to programs started from this PowerShell session. The Google Gen AI SDK reads it automatically when the analyzer creates its client.
Closing PowerShell removes the process-scoped value. No key is written to a project file.
Gemini cannot find the key?
- Confirm you ran the assignment inside the same activated PowerShell terminal that will run the analyzer.
- Repeat the assignment if you closed the terminal after setting the key.
- Keep the real key out of screenshots when requesting help.
Help me check the API key session scope
Before you run these checks, which two pinned version numbers do you expect the active environment to report?
- Verify both installed packages from the activated PowerShell terminal by running:
python -m pip show pandas
python -m pip show google-genai
What do these checks inspect?
Each command reads the installed package metadata from the active interpreter. The Version field reveals whether the requirements pins were applied correctly.
You'll find Version: 3.0.5 in the Pandas details. You'll find Version: 2.20.0 in the Google Gen AI SDK details.
That's the setup locked down. Your active environment now contains the two pinned packages needed by the analyzer.
Seeing a different version?
- Confirm the selected Visual Studio Code interpreter is .venv.
- Confirm the PowerShell prompt begins with (.venv).
- Rerun the requirements installation inside the activated environment if either package is missing.
Help me fix these package versions
Your Windows workspace can now reproduce the required Python environment without storing a credential on disk. Next up, you'll load the five-year financial dataset into a labeled DataFrame.
Load the Financial Data
Your Windows workspace is ready for the first data task. This step gives the analyzer its first visible result.
Raw cash flow categories are difficult to compare by inspection. A labeled Pandas DataFrame puts all five years in one table before later calculations.
In this step, get ready to:
- Create a five-year financial dataset with signed cash flow values.
- Load the records in ascending year order.
- Print the five-row DataFrame in the terminal.
Create the five-year CSV
A CSV stores each annual record on its own line. Each negative number represents a cash outflow.
- Select the new-file control in the Visual Studio Code file sidebar.
- Name the file financial_data.csv.
The file sidebar now lists financial_data.csv inside your existing project folder.
- Fill financial_data.csv with the five signed annual records by pasting this data:
year,operating_cash_flow,investing_cash_flow,financing_cash_flow
2021,780000,-340000,-120000
2022,850000,-370000,-100000
2023,900000,-460000,-180000
2024,980000,-420000,-90000
2025,1080000,-450000,-80000
How Do Signed Cash Flow Values Work?
- The year column identifies each reporting period.
- The three cash flow columns separate operating activity from investing activity and financing activity.
- Positive entries represent cash inflows.
- Negative entries represent cash outflows.
- Save financial_data.csv.
- Confirm the editor shows one header row followed by five data rows.
CSV Rows Look Misaligned?
Check that every row has four comma-separated values. Remove any extra commas from the data rows.
Confirm that the header contains the same four fields used by every record.
Help me check the CSV structure
✔️ Awesome, I've got everything!
Your CSV now contains all five signed annual cash flow records.
ⓧ I'd like to double check the full code
The complete data file should match this reference.
year,operating_cash_flow,investing_cash_flow,financing_cash_flow
2021,780000,-340000,-120000
2022,850000,-370000,-100000
2023,900000,-460000,-180000
2024,980000,-420000,-90000
2025,1080000,-450000,-80000
What Should Match?
The header names four fields. The next five lines cover 2021 through 2025.
Load the records in year order
Pandas reads the CSV into a DataFrame. Sorting by year makes the result predictable if the source rows ever arrive out of order.
- Select the new-file control in the Visual Studio Code file sidebar.
- Name the file analyzer.py.
The file sidebar now lists analyzer.py beside your CSV.
- Build the CSV-loading function by pasting this code into analyzer.py:
from pathlib import Path
import pandas as pd
DATA_FILE = Path("financial_data.csv")
def load_financial_data():
data = pd.read_csv(DATA_FILE)
return data.sort_values("year").reset_index(drop=True)
What Does This Code Do?
- Path represents the location of financial_data.csv.
- pd.read_csv(DATA_FILE) loads the records into a DataFrame.
- sort_values("year") orders the rows by ascending year.
- reset_index(drop=True) replaces the old row labels with a clean zero-based index.
- Save analyzer.py.
- Confirm the file sidebar lists analyzer.py beside financial_data.csv.
Cannot Find Both Files?
Confirm that both files appear inside the same project folder. Check the spelling of each filename.
Help me check my project files
Print the loaded DataFrame
The main() function provides a clear starting point for the analyzer. The final guard runs that function when Python executes the file directly.
- Append the program entry point below load_financial_data() in analyzer.py by pasting this code:
def main():
data = load_financial_data()
print(data)
if __name__ == "__main__":
main()
What Does This Code Do?
- main() gets the ordered DataFrame from load_financial_data().
- print(data) sends the complete table to the terminal.
- if __name__ == "__main__": starts the program when you run analyzer.py directly.
- Save analyzer.py.
✔️ Awesome, I've got everything!
Your analyzer now contains the complete loading flow and program entry point.
ⓧ I'd like to double check the full code
The complete Python file should match this reference.
from pathlib import Path
import pandas as pd
DATA_FILE = Path("financial_data.csv")
def load_financial_data():
data = pd.read_csv(DATA_FILE)
return data.sort_values("year").reset_index(drop=True)
def main():
data = load_financial_data()
print(data)
if __name__ == "__main__":
main()
What Should Match?
The file should contain the imports and DATA_FILE definition. It should also contain load_financial_data() followed by main().
Before you run this, make a quick prediction about which year appears at the top.
- Execute analyzer.py in the activated PowerShell terminal by running this command:
python analyzer.py
What Should You See?
You should see five rows labeled from 2021 through 2025.
The four headings are year, operating_cash_flow, investing_cash_flow, and financing_cash_flow.
Pandas also prints row labels from 0 through 4.
That is your first visible result. Your analyzer now turns the CSV into an ordered five-row DataFrame.
DataFrame Missing or Incomplete?
Confirm that the activated terminal is inside the folder containing both project files. Check that DATA_FILE uses the exact CSV filename.
If the table has fewer than five rows, compare the saved CSV with the full-file reference above.
Help me debug the DataFrame output
Your five-year dataset is loading in a consistent order. Next, you will calculate annual net cash flow. Those totals reveal the company's trajectory.
Calculate the Cash Flow Trend
You have already loaded five years of signed cash flow records from a CSV file. Your Pandas DataFrame now gives every year a consistent row.
The raw categories do not reveal each year's total or the direction of the five-year trajectory. This step adds traceable calculations before producing an accurate but formulaic fallback paragraph.
In this step, get ready to:
- Calculate annual net cash flow and year-to-year changes.
- Classify the five-year trajectory with transparent Python rules.
- Generate deterministic fallback feedback from the calculated metrics.
Calculate annual net cash flow
Each cash flow category already stores inflows as positive values and outflows as negative values. Adding the three categories produces one comparable total for each year.
The Series.diff() method subtracts each annual total from the next one. The first year has no earlier value for comparison.
- In analyzer.py, add the following function below load_financial_data().
def calculate_cash_flow(data):
data["annual_net_cash_flow"] = (
data["operating_cash_flow"]
+ data["investing_cash_flow"]
+ data["financing_cash_flow"]
)
data["change_from_previous_year"] = data["annual_net_cash_flow"].diff()
return data
What does this code do?
- The annual_net_cash_flow column adds the three signed cash flow categories across every row.
- The change_from_previous_year column measures the movement between consecutive annual totals.
- Returning data passes the expanded DataFrame back to the rest of the script.
- In main(), find the line containing data = load_financial_data().
- Add the following line directly below it:
data = calculate_cash_flow(data)
Why add this line here?
This sends the loaded DataFrame through calculate_cash_flow() before print(data) displays it. The terminal can now show the two calculated columns.
- Save analyzer.py.
- Run the updated analyzer in the activated PowerShell terminal with this command:
python analyzer.py
What should I see?
You should see five rows with the new annual_net_cash_flow and change_from_previous_year columns. The annual totals are $320,000, $380,000, $260,000, $470,000, and $550,000.
The first change appears as a missing value because 2021 is the baseline year. Later rows show movements of $60,000, -$120,000, $210,000, and $80,000.
Missing the calculated columns?
Check that calculate_cash_flow() appears above main(). Confirm that the call to this function appears before print(data).
Compare each column name with the headers in financial_data.csv because every name must match exactly.
Help me debug the calculated cash flow columns.
Classify the five-year trajectory
A deterministic classification applies the same rules every time the data is unchanged. The conditions check for consistently positive, negative, or flat changes before handling mixed patterns.
The overall change resolves the direction of a mixed pattern. Positive overall movement produces an improving classification even when an individual year declines.
- In analyzer.py, add the following function below calculate_cash_flow().
def classify_trend(data):
cash_flows = list(data["annual_net_cash_flow"])
changes = list(data["change_from_previous_year"])[1:]
overall_change = cash_flows[-1] - cash_flows[0]
if all(change > 0 for change in changes):
trend = "steadily improving"
elif all(change < 0 for change in changes):
trend = "steadily declining"
elif all(change == 0 for change in changes):
trend = "stable"
elif overall_change > 0:
trend = "volatile but improving overall"
elif overall_change < 0:
trend = "volatile and declining overall"
else:
trend = "volatile and flat overall"
return trend, overall_change
How does the classification work?
- The slice beginning at index 1 removes the baseline value because it has no prior year.
- The all() checks identify trajectories where every yearly change follows one direction.
- The final conditions classify mixed changes by comparing the first and final annual totals.
- The function returns both the label and the overall numeric movement.
- In main(), find the existing print(data) line.
- Replace that line with the following block:
trend, overall_change = classify_trend(data)
print(data)
print(f"\nTrend: {trend}")
What does this update do?
The first line captures both values returned by classify_trend(). The final line prints the classification beneath the expanded table.
- Save analyzer.py.
- Run the analyzer again with this command:
python analyzer.py
What should I see?
You should see Trend: volatile but improving overall below the table. The 2023 decline makes the path volatile while the final annual total remains higher than the first.
The calculation also holds an overall change of $230,000 from $320,000 in 2021 to $550,000 in 2025.
Seeing the wrong trend?
Confirm that changes starts from index 1. Including the baseline missing value prevents the directional checks from matching correctly.
Check that overall_change subtracts the first cash flow from the final cash flow.
Help me debug the trend classification.
Build deterministic fallback feedback
A fallback keeps the analysis useful when an external narrative service is unavailable. It builds three sentences entirely from Python-computed values.
The paragraph reports the first and final totals before naming the strongest year. Its final sentence adds a cautious observation based on the classification.
- In analyzer.py, add the following helper above load_financial_data().
def format_currency(value):
return f"${value:,.0f}"
What does this helper do?
The format_currency() helper converts numeric values into whole-dollar text with commas. Every sentence can now use one consistent money format.
- Add the following function below classify_trend().
def build_fallback_feedback(data, trend, overall_change):
years = [int(value) for value in data["year"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
strongest_index = cash_flows.index(max(cash_flows))
direction = "increased" if overall_change >= 0 else "decreased"
if "volatile" in trend:
caution = "The year-to-year reversals suggest that management should keep monitoring cash flow consistency."
elif trend == "steadily declining":
caution = "The repeated declines suggest that management should investigate the drivers of weaker cash generation."
else:
caution = "Management should continue monitoring whether this pattern holds in future periods."
return (
f"Annual net cash flow {direction} from {format_currency(cash_flows[0])} "
f"in {years[0]} to {format_currency(cash_flows[-1])} in {years[-1]}, "
f"an overall change of {format_currency(abs(overall_change))}. "
f"The pattern was {trend}, with the strongest result in "
f"{years[strongest_index]} at {format_currency(cash_flows[strongest_index])}. "
f"{caution}"
)
How is the fallback grounded?
- The year and cash flow lists come directly from the calculated DataFrame.
- The maximum annual total determines strongest_index.
- The sign of overall_change selects the direction word.
- The existing trend label selects a matching caution sentence.
- Save analyzer.py.
- Confirm the new function definitions parse successfully by running:
python analyzer.py
What does this check prove?
You should still reach the five-row table and the trend line. Reaching both outputs confirms that Python loaded the new helper definitions without a syntax error.
- Replace the current main() function with this completed version:
def main():
data = load_financial_data()
data = calculate_cash_flow(data)
trend, overall_change = classify_trend(data)
fallback_feedback = build_fallback_feedback(data, trend, overall_change)
print(data)
print(f"\nTrend: {trend}")
print(fallback_feedback)
What does the completed flow do?
The script now loads the source data before calculating totals. It then classifies the trajectory before constructing the fallback feedback.
Printing each result exposes the full path from source rows to narrative output. Every figure remains traceable to Python.
- Save analyzer.py.
Before you run the completed calculation, do you think fixed rules can produce an accurate paragraph that also sounds natural?
- Test the completed cash flow analysis with this command:
python analyzer.py
What should I see?
You should see the two calculated columns followed by Trend: volatile but improving overall. The fallback paragraph reports growth from $320,000 in 2021 to $550,000 in 2025.
The overall increase is $230,000. The strongest year is 2025 at $550,000.
The paragraph is accurate because every detail comes from verified metrics. Its fixed sentence pattern also makes it deliberately formulaic.
Fallback paragraph not printing?
Confirm that build_fallback_feedback() appears above main(). Check that fallback_feedback is assigned before it is printed.
Verify that format_currency() includes the dollar sign and the comma formatting expression shown above.
Help me debug the fallback feedback.
✔️ Awesome, I've got everything!
Great work. Save analyzer.py once more so the complete deterministic analysis is ready for the next step.
ⓧ I'd like to double check the full code
from pathlib import Path
import pandas as pd
DATA_FILE = Path("financial_data.csv")
def format_currency(value):
return f"${value:,.0f}"
def load_financial_data():
data = pd.read_csv(DATA_FILE)
return data.sort_values("year").reset_index(drop=True)
def calculate_cash_flow(data):
data["annual_net_cash_flow"] = (
data["operating_cash_flow"]
+ data["investing_cash_flow"]
+ data["financing_cash_flow"]
)
data["change_from_previous_year"] = data["annual_net_cash_flow"].diff()
return data
def classify_trend(data):
cash_flows = list(data["annual_net_cash_flow"])
changes = list(data["change_from_previous_year"])[1:]
overall_change = cash_flows[-1] - cash_flows[0]
if all(change > 0 for change in changes):
trend = "steadily improving"
elif all(change < 0 for change in changes):
trend = "steadily declining"
elif all(change == 0 for change in changes):
trend = "stable"
elif overall_change > 0:
trend = "volatile but improving overall"
elif overall_change < 0:
trend = "volatile and declining overall"
else:
trend = "volatile and flat overall"
return trend, overall_change
def build_fallback_feedback(data, trend, overall_change):
years = [int(value) for value in data["year"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
strongest_index = cash_flows.index(max(cash_flows))
direction = "increased" if overall_change >= 0 else "decreased"
if "volatile" in trend:
caution = "The year-to-year reversals suggest that management should keep monitoring cash flow consistency."
elif trend == "steadily declining":
caution = "The repeated declines suggest that management should investigate the drivers of weaker cash generation."
else:
caution = "Management should continue monitoring whether this pattern holds in future periods."
return (
f"Annual net cash flow {direction} from {format_currency(cash_flows[0])} "
f"in {years[0]} to {format_currency(cash_flows[-1])} in {years[-1]}, "
f"an overall change of {format_currency(abs(overall_change))}. "
f"The pattern was {trend}, with the strongest result in "
f"{years[strongest_index]} at {format_currency(cash_flows[strongest_index])}. "
f"{caution}"
)
def main():
data = load_financial_data()
data = calculate_cash_flow(data)
trend, overall_change = classify_trend(data)
fallback_feedback = build_fallback_feedback(data, trend, overall_change)
print(data)
print(f"\nTrend: {trend}")
print(fallback_feedback)
if __name__ == "__main__":
main()
Your analyzer now turns signed cash flow records into auditable metrics and dependable fallback feedback. Next, you will give the verified figures a more natural narrative layer without handing over the arithmetic.
Generate Grounded Gemini Feedback
Your Python analyzer now calculates annual cash flow from signed values. Its deterministic feedback also captures the verified trajectory.
Gemini can express those results in more natural business language. A grounded prompt limits that language to metrics Python has already verified.
In this step, get ready to:
- Build a prompt from Python-computed cash flow metrics.
- Add a Gemini request with deterministic fallback behavior.
- Run the analyzer to generate grounded feedback.
Ground the prompt in verified metrics
Prompt grounding keeps the calculated DataFrame as the source of every numerical claim. The model receives annual values plus the classification that Python produced.
- In analyzer.py, scroll to the end of build_fallback_feedback().
- Add the metric preparation section below build_fallback_feedback():
def build_gemini_prompt(data, trend, overall_change):
years = [int(value) for value in data["year"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
changes = list(data["change_from_previous_year"])
strongest_index = cash_flows.index(max(cash_flows))
metric_lines = []
for index in range(len(years)):
if index == 0:
change_text = "baseline year"
else:
change_text = f"change from prior year {format_currency(changes[index])}"
metric_lines.append(
f"- {years[index]}: annual net cash flow "
f"{format_currency(cash_flows[index])}; {change_text}"
)
What does this code prepare?
- The list comprehensions convert the year and cash flow columns into ordinary Python values.
- The strongest result is located from the calculated annual net cash flow values.
- The loop formats one verified metric line for each year.
- The first year is labeled as the baseline because it has no previous year for comparison.
- Continue build_gemini_prompt() by adding this return block immediately after the metric-building loop:
return f"""You are writing a concise summary of a fictional company's cash flow.
Use only the supplied metrics. Do not calculate new figures, invent causes, or give investment advice.
Write exactly one plain-text paragraph of three sentences.
Sentence 1: describe the five-year trajectory.
Sentence 2: mention the strongest year and its annual net cash flow.
Sentence 3: give one cautious operational observation based only on the pattern.
Verified metrics:
{chr(10).join(metric_lines)}
- Python trend classification: {trend}
- Overall change from first to final year: {format_currency(overall_change)}
- Strongest year: {years[strongest_index]}
- Strongest annual net cash flow: {format_currency(cash_flows[strongest_index])}
"""
How does the prompt stay grounded?
The instructions restrict the response to three sentences. They also prohibit new calculations plus invented causes.
Every supplied figure comes from annual_net_cash_flow or change_from_previous_year. Python also supplies the strongest year plus the deterministic classification.
- Save analyzer.py.
- Confirm the expanded script remains runnable by running this command:
python analyzer.py
What should you see?
You should see the five-row cash flow table followed by Trend: volatile but improving overall. The deterministic three-sentence paragraph should follow the trend.
This confirms the new prompt function has valid Python syntax. The existing deterministic path still completes successfully.
Seeing a syntax error?
Check that the return block remains indented inside build_gemini_prompt(). Confirm that the closing triple quotes appear on their own line.
Compare the braces around chr(10).join(metric_lines) with the code above. Help me debug my grounded prompt function
Add a resilient Gemini request
The Google Gen AI SDK sends the grounded prompt to the Gemini API. A single stateless request is enough because this analyzer does not need conversation history.
Your API key stays in the current PowerShell session from earlier. The client reads it automatically without exposing the credential in analyzer.py.
- In analyzer.py, scroll to the opening import section.
- Replace the opening section through the DATA_FILE constant with this code:
from pathlib import Path
import pandas as pd
from google import genai
DATA_FILE = Path("financial_data.csv")
MODEL_ID = "gemini-3.6-flash"
What does this setup add?
- The genai import provides the client used for the Gemini request.
- The MODEL_ID constant keeps the selected model in one visible location.
- The existing DATA_FILE constant continues to point at the five-year CSV.
- Scroll below build_gemini_prompt().
- Add the resilient request function with this code:
def generate_gemini_feedback(prompt, fallback_feedback):
client = None
try:
client = genai.Client()
interaction = client.interactions.create(
model=MODEL_ID,
input=prompt,
store=False,
)
feedback = (interaction.output_text or "").strip()
if feedback:
return feedback, "Gemini"
except Exception as error:
print(f"Gemini request unavailable: {error}")
finally:
if client is not None:
client.close()
return fallback_feedback, "Python fallback"
How does the request stay reliable?
- The try block creates the client. It sends the grounded prompt with server-side interaction storage disabled.
- The response text is stripped before the function checks that it contains usable feedback.
- The except block captures an unavailable request. It allows the analyzer to continue toward its fallback.
- The finally block closes the client whenever one was created. This releases its underlying connection.
- The final return supplies deterministic feedback plus a source label when Gemini produces no usable response.
- Save analyzer.py.
- Check the new import plus request function for syntax errors by running:
python analyzer.py
What does this check prove?
You should see the calculated table plus the deterministic feedback again. This confirms that Python can import the SDK and parse the new request function.
Seeing an import error?
- Confirm the integrated terminal still shows your activated .venv environment.
- Open the Command Palette with Ctrl+Shift+P.
- Select Python: Select Interpreter.
- Choose the interpreter inside .venv.
Help me fix the Google Gen AI SDK import
Run the grounded feedback path
The request function now has both narrative paths. The final connection in main() supplies the prompt plus the deterministic paragraph.
- In analyzer.py, find the entire main() function.
- Replace that function with this integrated version:
def main():
data = load_financial_data()
data = calculate_cash_flow(data)
trend, overall_change = classify_trend(data)
fallback_feedback = build_fallback_feedback(data, trend, overall_change)
prompt = build_gemini_prompt(data, trend, overall_change)
feedback, feedback_source = generate_gemini_feedback(prompt, fallback_feedback)
print(data)
print(f"\nTrend: {trend}")
print(f"Feedback source: {feedback_source}")
print(feedback)
What does the integrated function do?
- The analyzer builds the deterministic feedback before contacting Gemini.
- The grounded prompt receives the calculated data plus the classified trend.
- The request returns both the selected paragraph and its source.
- The terminal prints the source before displaying the final feedback.
- Save analyzer.py.
Before you run the analyzer, which feedback source do you expect to see when the request succeeds?
- Generate the grounded feedback by running:
python analyzer.py
What should you see?
You should see Feedback source: Gemini followed by one three-sentence paragraph. Its numerical claims should match the calculated table.
The paragraph may use different wording on separate runs. Its figures remain bounded by the verified metrics in the prompt.
Seeing Python fallback?
A Feedback source: Python fallback result means the request was unavailable or returned no usable text. Your analyzer still completes with the deterministic paragraph.
- Confirm this terminal is the same PowerShell session that holds the API key from earlier.
- Check that your internet connection can reach the Gemini service.
- Review your Google AI Studio usage if the request mentions a current rate limit.
Help me restore Gemini feedback
✔️ Awesome, I've got everything!
Great. Double-check that you saved analyzer.py before moving on.
ⓧ I'd like to double check the full code
from pathlib import Path
import pandas as pd
from google import genai
DATA_FILE = Path("financial_data.csv")
MODEL_ID = "gemini-3.6-flash"
def format_currency(value):
return f"${value:,.0f}"
def load_financial_data():
data = pd.read_csv(DATA_FILE)
return data.sort_values("year").reset_index(drop=True)
def calculate_cash_flow(data):
data["annual_net_cash_flow"] = (
data["operating_cash_flow"]
+ data["investing_cash_flow"]
+ data["financing_cash_flow"]
)
data["change_from_previous_year"] = data["annual_net_cash_flow"].diff()
return data
def classify_trend(data):
cash_flows = list(data["annual_net_cash_flow"])
changes = list(data["change_from_previous_year"])[1:]
overall_change = cash_flows[-1] - cash_flows[0]
if all(change > 0 for change in changes):
trend = "steadily improving"
elif all(change < 0 for change in changes):
trend = "steadily declining"
elif all(change == 0 for change in changes):
trend = "stable"
elif overall_change > 0:
trend = "volatile but improving overall"
elif overall_change < 0:
trend = "volatile and declining overall"
else:
trend = "volatile and flat overall"
return trend, overall_change
def build_fallback_feedback(data, trend, overall_change):
years = [int(value) for value in data["year"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
strongest_index = cash_flows.index(max(cash_flows))
direction = "increased" if overall_change >= 0 else "decreased"
if "volatile" in trend:
caution = "The year-to-year reversals suggest that management should keep monitoring cash flow consistency."
elif trend == "steadily declining":
caution = "The repeated declines suggest that management should investigate the drivers of weaker cash generation."
else:
caution = "Management should continue monitoring whether this pattern holds in future periods."
return (
f"Annual net cash flow {direction} from {format_currency(cash_flows[0])} "
f"in {years[0]} to {format_currency(cash_flows[-1])} in {years[-1]}, "
f"an overall change of {format_currency(abs(overall_change))}. "
f"The pattern was {trend}, with the strongest result in "
f"{years[strongest_index]} at {format_currency(cash_flows[strongest_index])}. "
f"{caution}"
)
def build_gemini_prompt(data, trend, overall_change):
years = [int(value) for value in data["year"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
changes = list(data["change_from_previous_year"])
strongest_index = cash_flows.index(max(cash_flows))
metric_lines = []
for index in range(len(years)):
if index == 0:
change_text = "baseline year"
else:
change_text = f"change from prior year {format_currency(changes[index])}"
metric_lines.append(
f"- {years[index]}: annual net cash flow "
f"{format_currency(cash_flows[index])}; {change_text}"
)
return f"""You are writing a concise summary of a fictional company's cash flow.
Use only the supplied metrics. Do not calculate new figures, invent causes, or give investment advice.
Write exactly one plain-text paragraph of three sentences.
Sentence 1: describe the five-year trajectory.
Sentence 2: mention the strongest year and its annual net cash flow.
Sentence 3: give one cautious operational observation based only on the pattern.
Verified metrics:
{chr(10).join(metric_lines)}
- Python trend classification: {trend}
- Overall change from first to final year: {format_currency(overall_change)}
- Strongest year: {years[strongest_index]}
- Strongest annual net cash flow: {format_currency(cash_flows[strongest_index])}
"""
def generate_gemini_feedback(prompt, fallback_feedback):
client = None
try:
client = genai.Client()
interaction = client.interactions.create(
model=MODEL_ID,
input=prompt,
store=False,
)
feedback = (interaction.output_text or "").strip()
if feedback:
return feedback, "Gemini"
except Exception as error:
print(f"Gemini request unavailable: {error}")
finally:
if client is not None:
client.close()
return fallback_feedback, "Python fallback"
def main():
data = load_financial_data()
data = calculate_cash_flow(data)
trend, overall_change = classify_trend(data)
fallback_feedback = build_fallback_feedback(data, trend, overall_change)
prompt = build_gemini_prompt(data, trend, overall_change)
feedback, feedback_source = generate_gemini_feedback(prompt, fallback_feedback)
print(data)
print(f"\nTrend: {trend}")
print(f"Feedback source: {feedback_source}")
print(feedback)
if __name__ == "__main__":
main()
You now have a grounded narrative layer that preserves Python's responsibility for every calculation. Next, you'll turn the analysis into a persistent Markdown report.
Write and Preview the Markdown Report
Your analyzer now calculates the cash flow trajectory in Python. It also uses Gemini to turn verified metrics into readable feedback.
Console output is temporary. This final step packages the complete analysis as a Markdown report with UTF-8 encoding so you can preview it in Visual Studio Code.
In this step, get ready to:
- Assemble the financial table and report sections.
- Write the finished report to a Markdown file.
- Preview the rendered report in Visual Studio Code.
Build the report table
The report needs its own output path before the analyzer can save anything. A Path object keeps that destination next to the existing input path.
- In analyzer.py, find this existing line:
DATA_FILE = Path("financial_data.csv")
This constant currently identifies the CSV that supplies the financial records.
- Replace the existing line with this two-line constant block:
DATA_FILE = Path("financial_data.csv")
REPORT_FILE = Path("company_performance_report.md")
What Does This Change Do?
The new REPORT_FILE constant points to company_performance_report.md. Keeping the filename in one constant makes the write operation easy to find.
- Save analyzer.py.
- Confirm the new constant does not interrupt the analysis by running:
python analyzer.py
You'll see the five-row DataFrame followed by the trend and feedback. The completed run confirms that the new output path is valid Python.
A Markdown table is assembled one row at a time. This keeps the table format explicit without adding another package.
- In analyzer.py, add build_markdown_report() below generate_gemini_feedback() by copying this code:
def build_markdown_report(data, trend, overall_change, feedback, feedback_source):
years = [int(value) for value in data["year"]]
operating = [int(value) for value in data["operating_cash_flow"]]
investing = [int(value) for value in data["investing_cash_flow"]]
financing = [int(value) for value in data["financing_cash_flow"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
changes = list(data["change_from_previous_year"])
strongest_index = cash_flows.index(max(cash_flows))
lines = [
"# Company Performance Report",
"",
"## Five-Year Cash Flow Analysis",
"",
"| Year | Operating | Investing | Financing | Annual Net Cash Flow | Change From Prior Year |",
"| ---: | ---: | ---: | ---: | ---: | ---: |",
]
for index in range(len(years)):
change_text = "Baseline" if index == 0 else format_currency(changes[index])
lines.append(
f"| {years[index]} | {format_currency(operating[index])} | "
f"{format_currency(investing[index])} | {format_currency(financing[index])} | "
f"{format_currency(cash_flows[index])} | {change_text} |"
)
return "\n".join(lines)
How Is the Table Built?
- The column lists convert DataFrame values into standard Python values for predictable formatting.
- The lines list begins with the report title and the Markdown table headers.
- The loop adds one formatted row for each year in the dataset.
- The first year displays Baseline because it has no previous year for comparison.
- Save analyzer.py.
- Confirm the table builder is valid by running:
python analyzer.py
You'll see the existing financial table and feedback complete successfully. This confirms that Python can load the new report function.
Does the Script Stop Before the Feedback?
Check that build_markdown_report() starts at the left edge of analyzer.py. Make sure each line inside the function uses consistent indentation.
Compare the closing parentheses around lines.append() if Python reports a syntax problem.
Help me debug the Markdown table function.
Complete the report writer
The table provides the calculated evidence. The remaining sections add the classification, narrative feedback, source label, and calculation method.
- In build_markdown_report(), find the current final line:
return "\n".join(lines)
This line currently joins the title and table rows into one Markdown string.
- Replace that line with this complete ending:
lines.extend(
[
"",
"## Trend Summary",
"",
f"- Pattern: {trend.title()}",
f"- Overall five-year change: {format_currency(overall_change)}",
f"- Strongest year: {years[strongest_index]} at {format_currency(cash_flows[strongest_index])}",
"",
"## Feedback",
"",
feedback,
"",
f"Feedback source: {feedback_source}",
"",
"## Method",
"",
"Annual net cash flow is calculated in this project by adding the signed operating, investing, and financing cash flow values for each year.",
]
)
return "\n".join(lines)
What Completes the Report?
- The trend section records the deterministic classification and strongest year.
- The feedback section stores the generated paragraph with its source.
- The method note explains how annual net cash flow was calculated.
- The final join() call turns every report line into one string.
- Save analyzer.py.
- Confirm the completed report function is valid by running:
python analyzer.py
You'll see the five-year analysis finish without a syntax problem. The completed report builder is ready to be connected to the file writer.
The final version of main() passes every calculated value into the report builder. It then writes the returned string to disk before printing the resolved path.
- In analyzer.py, replace the existing main() function with this complete version:
def main():
data = load_financial_data()
data = calculate_cash_flow(data)
trend, overall_change = classify_trend(data)
fallback_feedback = build_fallback_feedback(data, trend, overall_change)
prompt = build_gemini_prompt(data, trend, overall_change)
feedback, feedback_source = generate_gemini_feedback(prompt, fallback_feedback)
report = build_markdown_report(
data,
trend,
overall_change,
feedback,
feedback_source,
)
REPORT_FILE.write_text(report, encoding="utf-8")
print(data)
print(f"\nTrend: {trend}")
print(f"Feedback source: {feedback_source}")
print(feedback)
print(f"\nReport written to: {REPORT_FILE.resolve()}")
How Does the File Get Written?
- The report builder receives the calculated table, classification, overall change, feedback, and source.
- REPORT_FILE.write_text() creates or replaces company_performance_report.md with UTF-8 text.
- REPORT_FILE.resolve() produces the absolute report path for the terminal confirmation.
- Save analyzer.py.
Before you run the finished analyzer, which new confirmation do you expect beneath the feedback? Hold that prediction.
- Generate the Markdown report by running:
python analyzer.py
You'll see the calculated table, Trend: volatile but improving overall, the feedback source, and the feedback paragraph. The final line begins with Report written to: and shows the absolute path to company_performance_report.md.
Is the Report Path Missing?
Confirm that main() calls build_markdown_report() before REPORT_FILE.write_text(). Check that REPORT_FILE is defined near DATA_FILE.
Help me fix the missing report output.
✔️ Awesome, I've got everything!
Your cumulative analyzer.py now matches the complete Company Performance Analyzer.
ⓧ I'd like to double check the full code
from pathlib import Path
import pandas as pd
from google import genai
DATA_FILE = Path("financial_data.csv")
REPORT_FILE = Path("company_performance_report.md")
MODEL_ID = "gemini-3.6-flash"
def format_currency(value):
return f"${value:,.0f}"
def load_financial_data():
data = pd.read_csv(DATA_FILE)
return data.sort_values("year").reset_index(drop=True)
def calculate_cash_flow(data):
data["annual_net_cash_flow"] = (
data["operating_cash_flow"]
+ data["investing_cash_flow"]
+ data["financing_cash_flow"]
)
data["change_from_previous_year"] = data["annual_net_cash_flow"].diff()
return data
def classify_trend(data):
cash_flows = list(data["annual_net_cash_flow"])
changes = list(data["change_from_previous_year"])[1:]
overall_change = cash_flows[-1] - cash_flows[0]
if all(change > 0 for change in changes):
trend = "steadily improving"
elif all(change < 0 for change in changes):
trend = "steadily declining"
elif all(change == 0 for change in changes):
trend = "stable"
elif overall_change > 0:
trend = "volatile but improving overall"
elif overall_change < 0:
trend = "volatile and declining overall"
else:
trend = "volatile and flat overall"
return trend, overall_change
def build_fallback_feedback(data, trend, overall_change):
years = [int(value) for value in data["year"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
strongest_index = cash_flows.index(max(cash_flows))
direction = "increased" if overall_change >= 0 else "decreased"
if "volatile" in trend:
caution = "The year-to-year reversals suggest that management should keep monitoring cash flow consistency."
elif trend == "steadily declining":
caution = "The repeated declines suggest that management should investigate the drivers of weaker cash generation."
else:
caution = "Management should continue monitoring whether this pattern holds in future periods."
return (
f"Annual net cash flow {direction} from {format_currency(cash_flows[0])} "
f"in {years[0]} to {format_currency(cash_flows[-1])} in {years[-1]}, "
f"an overall change of {format_currency(abs(overall_change))}. "
f"The pattern was {trend}, with the strongest result in "
f"{years[strongest_index]} at {format_currency(cash_flows[strongest_index])}. "
f"{caution}"
)
def build_gemini_prompt(data, trend, overall_change):
years = [int(value) for value in data["year"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
changes = list(data["change_from_previous_year"])
strongest_index = cash_flows.index(max(cash_flows))
metric_lines = []
for index in range(len(years)):
if index == 0:
change_text = "baseline year"
else:
change_text = f"change from prior year {format_currency(changes[index])}"
metric_lines.append(
f"- {years[index]}: annual net cash flow "
f"{format_currency(cash_flows[index])}; {change_text}"
)
return f"""You are writing a concise summary of a fictional company's cash flow.
Use only the supplied metrics. Do not calculate new figures, invent causes, or give investment advice.
Write exactly one plain-text paragraph of three sentences.
Sentence 1: describe the five-year trajectory.
Sentence 2: mention the strongest year and its annual net cash flow.
Sentence 3: give one cautious operational observation based only on the pattern.
Verified metrics:
{chr(10).join(metric_lines)}
- Python trend classification: {trend}
- Overall change from first to final year: {format_currency(overall_change)}
- Strongest year: {years[strongest_index]}
- Strongest annual net cash flow: {format_currency(cash_flows[strongest_index])}
"""
def generate_gemini_feedback(prompt, fallback_feedback):
client = None
try:
client = genai.Client()
interaction = client.interactions.create(
model=MODEL_ID,
input=prompt,
store=False,
)
feedback = (interaction.output_text or "").strip()
if feedback:
return feedback, "Gemini"
except Exception as error:
print(f"Gemini request unavailable: {error}")
finally:
if client is not None:
client.close()
return fallback_feedback, "Python fallback"
def build_markdown_report(data, trend, overall_change, feedback, feedback_source):
years = [int(value) for value in data["year"]]
operating = [int(value) for value in data["operating_cash_flow"]]
investing = [int(value) for value in data["investing_cash_flow"]]
financing = [int(value) for value in data["financing_cash_flow"]]
cash_flows = [int(value) for value in data["annual_net_cash_flow"]]
changes = list(data["change_from_previous_year"])
strongest_index = cash_flows.index(max(cash_flows))
lines = [
"# Company Performance Report",
"",
"## Five-Year Cash Flow Analysis",
"",
"| Year | Operating | Investing | Financing | Annual Net Cash Flow | Change From Prior Year |",
"| ---: | ---: | ---: | ---: | ---: | ---: |",
]
for index in range(len(years)):
change_text = "Baseline" if index == 0 else format_currency(changes[index])
lines.append(
f"| {years[index]} | {format_currency(operating[index])} | "
f"{format_currency(investing[index])} | {format_currency(financing[index])} | "
f"{format_currency(cash_flows[index])} | {change_text} |"
)
lines.extend(
[
"",
"## Trend Summary",
"",
f"- Pattern: {trend.title()}",
f"- Overall five-year change: {format_currency(overall_change)}",
f"- Strongest year: {years[strongest_index]} at {format_currency(cash_flows[strongest_index])}",
"",
"## Feedback",
"",
feedback,
"",
f"Feedback source: {feedback_source}",
"",
"## Method",
"",
"Annual net cash flow is calculated in this project by adding the signed operating, investing, and financing cash flow values for each year.",
]
)
return "\n".join(lines)
def main():
data = load_financial_data()
data = calculate_cash_flow(data)
trend, overall_change = classify_trend(data)
fallback_feedback = build_fallback_feedback(data, trend, overall_change)
prompt = build_gemini_prompt(data, trend, overall_change)
feedback, feedback_source = generate_gemini_feedback(prompt, fallback_feedback)
report = build_markdown_report(
data,
trend,
overall_change,
feedback,
feedback_source,
)
REPORT_FILE.write_text(report, encoding="utf-8")
print(data)
print(f"\nTrend: {trend}")
print(f"Feedback source: {feedback_source}")
print(feedback)
print(f"\nReport written to: {REPORT_FILE.resolve()}")
if __name__ == "__main__":
main()
Preview the generated report
The terminal path confirms that the file exists. The rendered preview confirms that the Markdown structure communicates the analysis clearly.
- In Visual Studio Code, select company_performance_report.md from the workspace file list.
You'll see the plain Markdown title, table syntax, trend bullets, feedback paragraph, source, and method note.
Before you open the preview, predict how the table syntax and headings will look when rendered. Hold that prediction.
- Press Ctrl+Shift+V to open the Markdown preview.
You'll see the report title above a five-row cash flow table. Below it, you'll see the trend summary, feedback paragraph, source, and method note.
You've completed the full loop from signed CSV data to a shareable financial report.
Is the Markdown Still Plain Text?
Make sure company_performance_report.md is the active file before using the preview shortcut. Confirm that the filename ends with .md.
Help me open the rendered Markdown preview.
Secret mission
Reject Incomplete Financial Data
A malformed financial file can produce a polished report built on incomplete data. Add an input gate that rejects missing columns plus incomplete or duplicate year sets before any calculations run.
Clean Up Your Resources
Your local analyzer files have no ongoing cost. You can keep them, pause the current session, or delete the local workspace and Gemini API key.
Keep Real Financial Data Private
The supplied data in financial_data.csv is fictional. Free Tier prompts and responses may be used to improve Google products.
Keep sensitive financial records out of this learning project.
Resources you used:
- Configured project workspace containing the .venv virtual environment, requirements.txt, financial_data.csv, analyzer.py, and company_performance_report.md in Visual Studio Code.
- Current PowerShell session containing the process-scoped GEMINI_API_KEY value.
- Gemini API key attached to your Google AI Studio project.
Keep everything running
No action is needed. Choose this option if you plan to analyze more fictional datasets.
- Keep the configured project workspace so you can rerun analyzer.py.
- Leave the current PowerShell terminal open to preserve the process-scoped GEMINI_API_KEY value.
- Keep the Gemini API key active for future report runs.
- Review your Free Tier usage in Google AI Studio before running repeated analyses.
Pause - I'll come back to this later
Pause the current session while preserving the analyzer files for later.
- Close the current PowerShell terminal.
- Close Visual Studio Code to free its local resources.
The process-scoped GEMINI_API_KEY value disappears when PowerShell closes. Your project workspace and cloud key remain available.
Delete - I don't want to use this again
Deleting these resources is permanent. Use this option only when you no longer need the analyzer files or Gemini API key.
- Close the current PowerShell terminal to remove the process-scoped GEMINI_API_KEY value.
- Close Visual Studio Code.
- Use Windows File Explorer to locate the configured project workspace folder from this project.
- Delete that project workspace folder to remove .venv, requirements.txt, financial_data.csv, analyzer.py, and company_performance_report.md.
- Return to the API Keys page in Google AI Studio.
- Select the Gemini API key created for this project.
- Revoke the selected key.
- Confirm that the key no longer appears as active.
That clears the analyzer files, the current session value, and the project key.
Nice Work!
Nice Work!
You did it! Your Company Performance Analyzer now turns five years of financial data into a traceable cash flow assessment and a shareable report.
You've learned how to:
- Built a five-year cash flow analysis in a Pandas DataFrame with annual totals plus year-to-year changes.
- Separated deterministic Python calculations from a grounded Gemini narrative with an automatic fallback.
- Generated a shareable Markdown report with the complete analysis plus a clear feedback source.
- Secret Mission: Added input validation that rejects missing columns plus incomplete or duplicate year sets before report generation.
Ready to quiz yourself?