Chapter 07 Working with Files and External Data - Bryantad/Sona GitHub Wiki
Chapter 7: Working with Files and External Data
š Welcome to External Data Mastery!
Ages 12-55+ | All Learning Styles Welcome | Neurodivergent-Friendly
šÆ Chapter Overview
Real-world applications don't exist in isolation - they need to read configuration files, save user data, communicate with web services, and integrate with other systems. This chapter will teach you how to make your Sona programs interact with the outside world effectively and safely.
š§ This Chapter Supports:
- š ADHD: Clear progress tracking, bite-sized sections, frequent checkpoints
- šØ Autism: Predictable patterns, detailed explanations, consistent structure
- š Learning Disabilities: Plain language, visual aids, step-by-step guidance
- šÆ Executive Function: Organized workflows, clear objectives, built-in review
- šļø Visual Processing: Clean layout, logical flow, accessibility features
- š§ Auditory Processing: Screen reader friendly, clear headings, descriptive text
š Learning Objectives
By the end of this chapter, you will:
- ā Read and write files confidently
- ā Parse JSON and work with structured data formats
- ā Handle errors gracefully when working with external resources
- ā Build a weather application that saves data locally
- ā Implement best practices for data persistence
- ā Create a personal expense tracker with CSV export and data visualization
šÆ Progress Tracking
Chapter 7 Progress: [āāāāāāāāāā] 0% Complete
āāā š Files and Reading (0/3 sections)
āāā š¾ Data Formats (0/3 sections)
āāā ā ļø Error Handling (0/3 sections)
āāā š API Integration (0/3 sections)
āāā š Real-World Projects (0/2 projects)
šŗļø Visual Chapter Map
š External Data Journey
āāā š File Operations
ā āāā š Reading text files
ā āāā āļø Writing and appending
ā āāā š Directory operations
āāā š Data Formats
ā āāā š JSON parsing and generation
ā āāā š CSV handling
ā āāā āļø Configuration files
āāā ā ļø Error Handling
ā āāā š File not found errors
ā āāā š Permission issues
ā āāā š Data format errors
āāā š API Integration
ā āāā š HTTP requests
ā āāā šØ Response handling
ā āāā ā±ļø Rate limiting
āāā š Real-World Projects
āāā š¤ļø Weather Application
āāā š° Expense Tracker
š¤ Why External Data Matters
š” Real-World Connection
When I started programming, I thought applications were self-contained worlds. But the real power comes when your programs can save important information, load user preferences, and communicate with other services. Your Sona programs become truly useful when they can persist data and connect to the wider digital ecosystem.
š Everyday Examples
Consider these scenarios you encounter daily:
- š® Gaming: Saving your game progress
- šµ Music: Loading your playlist
- š± Apps: Syncing your notes across devices
- š³ Banking: Importing your transactions
- š· Photos: Backing up your memories
All of these require working with external data sources!
š§ Learning Path Options
Choose your learning style:
- šÆ Quick Learner (Ages 16-55): Jump to practical examples
- š Detailed Learner (Ages 12-55): Follow step-by-step explanations
- š ADHD-Friendly (All ages): Use progress checkpoints and breaks
- šØ Autism-Friendly (All ages): Focus on patterns and consistency
- š§ Audio Learner (All ages): Read explanations aloud or use screen reader
š File Operations: The Foundation
šÆ Section Goal
Learn to read and write files safely and efficiently.
š Reading Files: Your First Step
think "Let's start with the basics - reading text files"
think "Basic file reading - simple and safe"
function read_simple_text_file(filename):
try:
content = read_file(filename)
show "File contents:"
show content
return content
except error:
show "Error reading file: " + error.message
return None
think "Reading line by line for large files"
function read_file_by_lines(filename):
try:
lines = read_file_lines(filename)
show "File has " + str(len(lines)) + " lines"
for i, line in enumerate(lines):
show "Line " + str(i + 1) + ": " + line
return lines
except error:
show "Error reading file: " + error.message
return []
think "Processing configuration files"
function load_config(config_file):
try:
lines = read_file_lines(config_file)
config = {}
for line in lines:
think "Skip empty lines and comments"
when len(line) == 0 or line[0] == "#":
continue
think "Process key=value pairs"
when "=" in line:
key, value = line.split("=", 1)
config[key.strip()] = value.strip()
show "Configuration loaded successfully!"
return config
except error:
show "Error loading configuration: " + error.message
return {}
think "Let's test our file reading functions"
config = load_config("app_settings.txt")
show "App name: " + config.get("app_name", "Unknown")
show "Version: " + config.get("version", "1.0")
š” Learning Checkpoint 1
šÆ What did we learn?
- How to read entire files safely
- How to process files line by line
- How to handle configuration files
- How to use try/except for error handling
āļø Writing Files: Saving Your Data
think "Writing files - preserving your work"
function write_simple_file(filename, content):
try:
write_file(filename, content)
show "Successfully saved " + filename
return True
except error:
show "Error writing file: " + error.message
return False
think "Appending to existing files"
function append_to_file(filename, new_content):
try:
append_file(filename, new_content)
show "Successfully appended to " + filename
return True
except error:
show "Error appending to file: " + error.message
return False
think "Creating a simple log system"
function log_message(message, log_file):
timestamp = get_current_timestamp()
log_entry = timestamp + " - " + message + "\n"
return append_to_file(log_file, log_entry)
function get_current_timestamp():
think "In real implementation, this would be dynamic"
return "2025-07-14 15:30:00"
think "Creating backup files"
function create_backup(original_file):
try:
content = read_file(original_file)
backup_name = original_file + ".backup"
write_file(backup_name, content)
show "Backup created: " + backup_name
return True
except error:
show "Error creating backup: " + error.message
return False
}
}
// Safe file writing (with temporary file)
func safe_write_file(filename, content) {
let temp_file = filename + ".tmp"
try {
// Write to temporary file first
write_file(temp_file, content)
// If successful, replace original
move_file(temp_file, filename)
print("Safely saved " + filename)
return true
} catch (error) {
// Clean up temporary file if it exists
try {
delete_file(temp_file)
} catch (cleanup_error) {
// Ignore cleanup errors
}
print("Error in safe write: " + error.message)
return false
}
}
Directory Operations
// Working with directories
class FileManager {
func init() {
self.current_directory = get_current_directory()
}
func list_directory(path) {
try {
let items = list_directory_contents(path)
let files = []
let directories = []
for item in items {
if item.is_directory {
directories = directories + [item.name]
} else {
files = files + [item.name]
}
}
return {
"files": files,
"directories": directories,
"total_items": items.length
}
} catch (error) {
print("Error listing directory: " + error.message)
return {"files": [], "directories": [], "total_items": 0}
}
}
func create_directory_structure(base_path, structure) {
try {
for folder in structure {
let full_path = base_path + "/" + folder
create_directory(full_path)
print("Created directory: " + full_path)
}
return true
} catch (error) {
print("Error creating directories: " + error.message)
return false
}
}
func find_files_by_extension(directory, extension) {
try {
let contents = list_directory_contents(directory)
let matching_files = []
for item in contents {
if !item.is_directory && item.name.ends_with(extension) {
matching_files = matching_files + [item.name]
}
}
return matching_files
} catch (error) {
print("Error searching files: " + error.message)
return []
}
}
func get_file_info(file_path) {
try {
let info = get_file_stats(file_path)
return {
"name": info.name,
"size": info.size,
"created": info.created_date,
"modified": info.modified_date,
"is_readable": info.permissions.read,
"is_writable": info.permissions.write
}
} catch (error) {
print("Error getting file info: " + error.message)
return null
}
}
}
// Usage
let file_manager = FileManager()
let current_files = file_manager.list_directory(".")
print("Current directory contains:")
print(" Files: " + current_files.files.length)
print(" Directories: " + current_files.directories.length)
let sona_files = file_manager.find_files_by_extension(".", ".sona")
print("Sona files found: " + sona_files.length)
Working with JSON Data
JSON (JavaScript Object Notation) is the universal language for data exchange. Even though it originated from JavaScript, it's used everywhere - web APIs, configuration files, data storage.
// JSON parsing and generation
class JSONHandler {
func parse_json_string(json_string) {
try {
let data = parse_json(json_string)
return {"success": true, "data": data}
} catch (error) {
return {"success": false, "error": error.message}
}
}
func load_json_file(filename) {
try {
let content = read_file(filename)
let result = self.parse_json_string(content)
if result.success {
print("Successfully loaded JSON from " + filename)
return result.data
} else {
print("JSON parse error: " + result.error)
return null
}
} catch (error) {
print("File error: " + error.message)
return null
}
}
func save_json_file(filename, data) {
try {
let json_string = stringify_json(data)
write_file(filename, json_string)
print("Successfully saved JSON to " + filename)
return true
} catch (error) {
print("Error saving JSON: " + error.message)
return false
}
}
func pretty_print_json(data) {
try {
let pretty_json = stringify_json_pretty(data)
print(pretty_json)
} catch (error) {
print("Error formatting JSON: " + error.message)
}
}
}
// User profile management example
class UserProfileManager {
func init() {
self.json_handler = JSONHandler()
self.profiles_file = "user_profiles.json"
self.profiles = self.load_profiles()
}
func load_profiles() {
let data = self.json_handler.load_json_file(self.profiles_file)
if data != null {
return data
} else {
// Create default structure
return {"profiles": [], "version": "1.0"}
}
}
func save_profiles() {
return self.json_handler.save_json_file(self.profiles_file, self.profiles)
}
func create_profile(username, email, preferences) {
let profile = {
"id": self.generate_user_id(),
"username": username,
"email": email,
"preferences": preferences,
"created_date": get_current_timestamp(),
"last_active": get_current_timestamp()
}
self.profiles.profiles = self.profiles.profiles + [profile]
if self.save_profiles() {
print("Profile created for " + username)
return profile
} else {
print("Failed to save profile")
return null
}
}
func find_profile(username) {
for profile in self.profiles.profiles {
if profile.username == username {
return profile
}
}
return null
}
func update_preferences(username, new_preferences) {
let profile = self.find_profile(username)
if profile != null {
profile.preferences = new_preferences
profile.last_active = get_current_timestamp()
if self.save_profiles() {
print("Updated preferences for " + username)
return true
}
}
print("Failed to update preferences for " + username)
return false
}
func generate_user_id() {
// Simple ID generation - in practice, use UUIDs or database IDs
return "user_" + (self.profiles.profiles.length + 1)
}
func export_profile(username, export_file) {
let profile = self.find_profile(username)
if profile != null {
return self.json_handler.save_json_file(export_file, profile)
}
return false
}
func import_profile(import_file) {
let profile_data = self.json_handler.load_json_file(import_file)
if profile_data != null {
// Check if profile already exists
let existing = self.find_profile(profile_data.username)
if existing != null {
print("Profile already exists: " + profile_data.username)
return false
}
self.profiles.profiles = self.profiles.profiles + [profile_data]
return self.save_profiles()
}
return false
}
}
// Usage demonstration
let profile_manager = UserProfileManager()
// Create some profiles
let alice_prefs = {
"theme": "dark",
"language": "en",
"notifications": true,
"auto_save": true
}
let bob_prefs = {
"theme": "light",
"language": "es",
"notifications": false,
"auto_save": false
}
profile_manager.create_profile("alice", "[email protected]", alice_prefs)
profile_manager.create_profile("bob", "[email protected]", bob_prefs)
// Update preferences
let new_alice_prefs = alice_prefs
new_alice_prefs.theme = "blue"
profile_manager.update_preferences("alice", new_alice_prefs)
// Export a profile
profile_manager.export_profile("alice", "alice_profile_backup.json")
Error Handling for External Resources
Andre's Safety Philosophy: External resources are unreliable by nature. Files might not exist, networks might be down, APIs might change. Good error handling isn't just about preventing crashes - it's about creating robust applications that gracefully handle the unexpected.
// Comprehensive error handling patterns
class RobustFileHandler {
func init() {
self.max_retries = 3
self.retry_delay = 1000 // milliseconds
}
func read_file_with_retry(filename) {
let attempts = 0
let last_error = null
while attempts < self.max_retries {
try {
let content = read_file(filename)
print("Successfully read " + filename + " on attempt " + (attempts + 1))
return {"success": true, "content": content}
} catch (error) {
attempts = attempts + 1
last_error = error
print("Attempt " + attempts + " failed: " + error.message)
if attempts < self.max_retries {
print("Retrying in " + self.retry_delay + "ms...")
sleep(self.retry_delay)
}
}
}
return {
"success": false,
"error": "Failed after " + self.max_retries + " attempts",
"last_error": last_error.message
}
}
func safe_write_with_validation(filename, content, validation_func) {
// Validate content first
if validation_func != null {
try {
let is_valid = validation_func(content)
if !is_valid {
return {
"success": false,
"error": "Content validation failed"
}
}
} catch (error) {
return {
"success": false,
"error": "Validation error: " + error.message
}
}
}
// Create backup if file exists
if file_exists(filename) {
let backup_result = self.create_timestamped_backup(filename)
if !backup_result.success {
return backup_result
}
}
// Write the file
try {
write_file(filename, content)
return {"success": true, "message": "File written successfully"}
} catch (error) {
return {
"success": false,
"error": "Write failed: " + error.message
}
}
}
func create_timestamped_backup(filename) {
try {
let timestamp = get_current_timestamp().replace(" ", "_").replace(":", "-")
let backup_name = filename + ".backup_" + timestamp
let content = read_file(filename)
write_file(backup_name, content)
return {
"success": true,
"backup_file": backup_name
}
} catch (error) {
return {
"success": false,
"error": "Backup failed: " + error.message
}
}
}
func batch_process_files(file_list, process_func) {
let results = []
let successful = 0
let failed = 0
for filename in file_list {
try {
let result = process_func(filename)
results = results + [{
"file": filename,
"success": true,
"result": result
}]
successful = successful + 1
} catch (error) {
results = results + [{
"file": filename,
"success": false,
"error": error.message
}]
failed = failed + 1
}
}
print("Batch processing complete:")
print(" Successful: " + successful)
print(" Failed: " + failed)
return {
"results": results,
"summary": {
"total": file_list.length,
"successful": successful,
"failed": failed
}
}
}
}
// Validation functions
func validate_json_content(content) {
try {
parse_json(content)
return true
} catch (error) {
return false
}
}
func validate_csv_content(content) {
let lines = content.split("\n")
if lines.length < 2 {
return false // Need at least header and one data row
}
let header_columns = lines[0].split(",").length
for i in range(1, lines.length) {
if lines[i].trim().length > 0 { // Skip empty lines
let row_columns = lines[i].split(",").length
if row_columns != header_columns {
return false // Inconsistent column count
}
}
}
return true
}
// Usage
let robust_handler = RobustFileHandler()
// Safe file operations with validation
let json_data = '{"name": "test", "value": 42}'
let result = robust_handler.safe_write_with_validation(
"test_data.json",
json_data,
validate_json_content
)
if result.success {
print("File saved successfully!")
} else {
print("Save failed: " + result.error)
}
Building a Weather Application
Let's create a practical application that demonstrates all these concepts:
class WeatherApp {
func init() {
self.data_file = "weather_data.json"
self.config_file = "weather_config.txt"
self.json_handler = JSONHandler()
self.weather_data = self.load_weather_data()
self.config = self.load_config()
}
func load_weather_data() {
let data = self.json_handler.load_json_file(self.data_file)
if data != null {
return data
} else {
return {
"locations": {},
"last_updated": null,
"version": "1.0"
}
}
}
func load_config() {
try {
let lines = read_file_lines(self.config_file)
let config = {
"api_key": "",
"default_units": "fahrenheit",
"update_interval": 3600, // seconds
"max_locations": 10
}
for line in lines {
if line.length > 0 && line[0] != "#" {
let parts = line.split("=")
if parts.length == 2 {
let key = parts[0].trim()
let value = parts[1].trim()
config[key] = value
}
}
}
return config
} catch (error) {
print("Using default configuration")
return {
"api_key": "demo_key",
"default_units": "fahrenheit",
"update_interval": 3600,
"max_locations": 10
}
}
}
func save_weather_data() {
self.weather_data.last_updated = get_current_timestamp()
return self.json_handler.save_json_file(self.data_file, self.weather_data)
}
func add_location(location_name, latitude, longitude) {
if self.weather_data.locations.keys().length >= self.config.max_locations {
print("Maximum number of locations reached")
return false
}
let location_data = {
"name": location_name,
"latitude": latitude,
"longitude": longitude,
"weather_history": [],
"alerts": [],
"added_date": get_current_timestamp()
}
self.weather_data.locations[location_name] = location_data
if self.save_weather_data() {
print("Added location: " + location_name)
return true
} else {
print("Failed to save location data")
return false
}
}
func simulate_weather_fetch(location_name) {
// Simulate API call (in real app, this would be an HTTP request)
let weather_data = {
"location": location_name,
"temperature": 72 + (random() * 40 - 20), // 52-92°F
"humidity": 30 + (random() * 40), // 30-70%
"condition": self.get_random_condition(),
"wind_speed": random() * 15, // 0-15 mph
"timestamp": get_current_timestamp(),
"forecast": self.generate_forecast()
}
return weather_data
}
func get_random_condition() {
let conditions = ["sunny", "cloudy", "partly cloudy", "rainy", "stormy", "foggy"]
let index = (random() * conditions.length)
return conditions[index]
}
func generate_forecast() {
let forecast = []
for i in range(1, 6) { // 5-day forecast
let day_forecast = {
"day": i,
"high": 70 + (random() * 30),
"low": 50 + (random() * 20),
"condition": self.get_random_condition()
}
forecast = forecast + [day_forecast]
}
return forecast
}
func update_weather(location_name) {
if self.weather_data.locations[location_name] == null {
print("Location not found: " + location_name)
return false
}
try {
print("Fetching weather for " + location_name + "...")
let current_weather = self.simulate_weather_fetch(location_name)
// Add to history
let location = self.weather_data.locations[location_name]
location.weather_history = location.weather_history + [current_weather]
// Keep only last 30 entries
if location.weather_history.length > 30 {
location.weather_history = location.weather_history.slice(-30)
}
// Check for weather alerts
self.check_weather_alerts(location_name, current_weather)
if self.save_weather_data() {
print("Weather updated for " + location_name)
return current_weather
} else {
print("Failed to save weather data")
return null
}
} catch (error) {
print("Error updating weather: " + error.message)
return null
}
}
func check_weather_alerts(location_name, weather_data) {
let location = self.weather_data.locations[location_name]
let alerts = []
// Temperature alerts
if weather_data.temperature > 90 {
alerts = alerts + [{
"type": "heat_warning",
"message": "High temperature warning: " + weather_data.temperature + "°F",
"timestamp": weather_data.timestamp
}]
}
if weather_data.temperature < 32 {
alerts = alerts + [{
"type": "freeze_warning",
"message": "Freezing temperature warning: " + weather_data.temperature + "°F",
"timestamp": weather_data.timestamp
}]
}
// Wind alerts
if weather_data.wind_speed > 25 {
alerts = alerts + [{
"type": "wind_warning",
"message": "High wind warning: " + weather_data.wind_speed + " mph",
"timestamp": weather_data.timestamp
}]
}
// Storm alerts
if weather_data.condition == "stormy" {
alerts = alerts + [{
"type": "storm_warning",
"message": "Storm conditions detected",
"timestamp": weather_data.timestamp
}]
}
if alerts.length > 0 {
location.alerts = location.alerts + alerts
print("ā ļø Weather alerts for " + location_name + ":")
for alert in alerts {
print(" " + alert.message)
}
}
}
func get_current_weather(location_name) {
let location = self.weather_data.locations[location_name]
if location == null {
print("Location not found: " + location_name)
return null
}
if location.weather_history.length == 0 {
print("No weather data available for " + location_name)
return null
}
return location.weather_history[-1] // Last entry
}
func display_weather(location_name) {
let weather = self.get_current_weather(location_name)
if weather == null {
return
}
print("\nš¤ļø Weather for " + location_name + " š¤ļø")
print("Temperature: " + weather.temperature + "°F")
print("Condition: " + weather.condition)
print("Humidity: " + weather.humidity + "%")
print("Wind Speed: " + weather.wind_speed + " mph")
print("Last Updated: " + weather.timestamp)
print("\nš
5-Day Forecast:")
for day in weather.forecast {
print("Day " + day.day + ": " + day.condition +
" (High: " + day.high + "°F, Low: " + day.low + "°F)")
}
print("=" * 40 + "\n")
}
func generate_weather_report(location_name, output_file) {
let location = self.weather_data.locations[location_name]
if location == null {
print("Location not found: " + location_name)
return false
}
let report_lines = []
report_lines = report_lines + ["Weather Report for " + location_name]
report_lines = report_lines + ["Generated: " + get_current_timestamp()]
report_lines = report_lines + ["=" * 50]
report_lines = report_lines + [""]
// Current weather
if location.weather_history.length > 0 {
let current = location.weather_history[-1]
report_lines = report_lines + ["Current Weather:"]
report_lines = report_lines + [" Temperature: " + current.temperature + "°F"]
report_lines = report_lines + [" Condition: " + current.condition]
report_lines = report_lines + [" Humidity: " + current.humidity + "%"]
report_lines = report_lines + [" Wind Speed: " + current.wind_speed + " mph"]
report_lines = report_lines + [""]
}
// Weather history
if location.weather_history.length > 1 {
report_lines = report_lines + ["Recent Weather History:"]
let recent_history = location.weather_history.slice(-7) // Last 7 entries
for entry in recent_history {
report_lines = report_lines + [
" " + entry.timestamp + " - " + entry.temperature + "°F, " + entry.condition
]
}
report_lines = report_lines + [""]
}
// Alerts
if location.alerts.length > 0 {
report_lines = report_lines + ["Recent Alerts:"]
let recent_alerts = location.alerts.slice(-10) // Last 10 alerts
for alert in recent_alerts {
report_lines = report_lines + [" " + alert.timestamp + " - " + alert.message]
}
report_lines = report_lines + [""]
}
// Statistics
if location.weather_history.length > 0 {
let stats = self.calculate_weather_stats(location.weather_history)
report_lines = report_lines + ["Weather Statistics:"]
report_lines = report_lines + [" Average Temperature: " + stats.avg_temp + "°F"]
report_lines = report_lines + [" Highest Temperature: " + stats.max_temp + "°F"]
report_lines = report_lines + [" Lowest Temperature: " + stats.min_temp + "°F"]
report_lines = report_lines + [" Average Humidity: " + stats.avg_humidity + "%"]
report_lines = report_lines + [" Most Common Condition: " + stats.most_common_condition]
}
let report_content = report_lines.join("\n")
try {
write_file(output_file, report_content)
print("Weather report saved to " + output_file)
return true
} catch (error) {
print("Error saving report: " + error.message)
return false
}
}
func calculate_weather_stats(weather_history) {
if weather_history.length == 0 {
return {}
}
let total_temp = 0
let total_humidity = 0
let max_temp = weather_history[0].temperature
let min_temp = weather_history[0].temperature
let condition_counts = {}
for entry in weather_history {
total_temp = total_temp + entry.temperature
total_humidity = total_humidity + entry.humidity
if entry.temperature > max_temp {
max_temp = entry.temperature
}
if entry.temperature < min_temp {
min_temp = entry.temperature
}
// Count conditions
if condition_counts[entry.condition] == null {
condition_counts[entry.condition] = 1
} else {
condition_counts[entry.condition] = condition_counts[entry.condition] + 1
}
}
// Find most common condition
let most_common = ""
let max_count = 0
for condition in condition_counts.keys() {
if condition_counts[condition] > max_count {
max_count = condition_counts[condition]
most_common = condition
}
}
return {
"avg_temp": (total_temp / weather_history.length).toFixed(1),
"avg_humidity": (total_humidity / weather_history.length).toFixed(1),
"max_temp": max_temp,
"min_temp": min_temp,
"most_common_condition": most_common
}
}
func export_data(export_file) {
return self.json_handler.save_json_file(export_file, self.weather_data)
}
func import_data(import_file) {
let imported_data = self.json_handler.load_json_file(import_file)
if imported_data != null {
self.weather_data = imported_data
return self.save_weather_data()
}
return false
}
}
### šÆ Project Goals
Build two complete applications that demonstrate all the concepts we've learned in this chapter.
---
## š¤ļø Project 1: Weather Application
### š Project Overview
Create a comprehensive weather application that:
- Fetches weather data from APIs
- Saves data locally with caching
- Provides multiple display formats
- Handles errors gracefully
- Exports data for analysis
```sona
think "Complete Weather Application - Real-world external data project"
class WeatherApp:
function __init__():
self.config = self.load_config()
self.cache_file = "weather_cache.json"
self.data_file = "weather_data.json"
self.locations = {}
self.api_key = self.config.get("api_key", "demo_key")
self.base_url = "https://api.openweathermap.org/data/2.5/weather"
self.rate_limiter = RateLimiter(60, 60) # 60 requests per minute
function load_config():
try:
config = load_data_from_json("weather_config.json")
return config if config else self.create_default_config()
except error:
show "ā ļø Config not found, creating default"
return self.create_default_config()
function create_default_config():
default_config = {
"api_key": "your_api_key_here",
"default_units": "metric",
"cache_duration": 300, # 5 minutes
"max_locations": 10,
"auto_refresh": True,
"display_format": "detailed"
}
save_data_as_json(default_config, "weather_config.json")
return default_config
function add_location(name, latitude, longitude):
location_data = {
"name": name,
"latitude": latitude,
"longitude": longitude,
"added_at": get_current_timestamp(),
"last_updated": None,
"weather_data": None
}
self.locations[name] = location_data
show "š Added location: " + name
return True
function get_weather_data(location_name):
when location_name not in self.locations:
show "ā Location not found: " + location_name
return None
think "Check cache first"
cached_data = self.get_cached_weather(location_name)
when cached_data:
show "š¾ Using cached weather data for: " + location_name
return cached_data
think "Fetch from API"
location = self.locations[location_name]
url = (self.base_url + "?q=" + location_name +
"&appid=" + self.api_key + "&units=" + self.config["default_units"])
try:
when not self.rate_limiter.can_make_request():
show "ā±ļø Rate limit reached, waiting..."
sleep(60)
response = make_http_request(url, "GET", {}, None)
when response and response.status_code == 200:
weather_data = parse_json(response.body)
processed_data = self.process_weather_data(weather_data)
think "Cache the data"
self.cache_weather_data(location_name, processed_data)
think "Update location"
self.locations[location_name]["weather_data"] = processed_data
self.locations[location_name]["last_updated"] = get_current_timestamp()
return processed_data
else:
show "ā Failed to fetch weather data"
return None
except error:
show "ā Error fetching weather: " + error.message
return None
function process_weather_data(raw_data):
try:
processed = {
"city": raw_data["name"],
"country": raw_data["sys"]["country"],
"temperature": raw_data["main"]["temp"],
"feels_like": raw_data["main"]["feels_like"],
"humidity": raw_data["main"]["humidity"],
"pressure": raw_data["main"]["pressure"],
"description": raw_data["weather"][0]["description"],
"wind_speed": raw_data["wind"]["speed"],
"visibility": raw_data.get("visibility", 0),
"timestamp": get_current_timestamp(),
"sunrise": raw_data["sys"]["sunrise"],
"sunset": raw_data["sys"]["sunset"]
}
return processed
except KeyError as e:
show "ā Missing field in weather data: " + str(e)
return None
function cache_weather_data(location_name, weather_data):
try:
cache = load_data_from_json(self.cache_file)
when not cache:
cache = {}
cache[location_name] = {
"data": weather_data,
"cached_at": get_current_timestamp()
}
save_data_as_json(cache, self.cache_file)
return True
except error:
show "ā Failed to cache data: " + error.message
return False
function get_cached_weather(location_name):
try:
cache = load_data_from_json(self.cache_file)
when not cache or location_name not in cache:
return None
cached_entry = cache[location_name]
think "Check if cache is still valid (5 minutes)"
cached_time = cached_entry["cached_at"]
current_time = get_current_timestamp()
think "In real app, calculate time difference"
think "For demo, assume cache is valid"
return cached_entry["data"]
except error:
show "ā Error reading cache: " + error.message
return None
function display_weather(location_name):
weather_data = self.get_weather_data(location_name)
when not weather_data:
show "ā No weather data available for: " + location_name
return
when self.config["display_format"] == "detailed":
self.display_detailed_weather(weather_data)
else:
self.display_simple_weather(weather_data)
function display_detailed_weather(weather_data):
show "š¤ļø ================================"
show "š " + weather_data["city"] + ", " + weather_data["country"]
show "š”ļø Temperature: " + str(weather_data["temperature"]) + "°C"
show "š¤ Feels like: " + str(weather_data["feels_like"]) + "°C"
show "āļø Conditions: " + weather_data["description"].title()
show "š§ Humidity: " + str(weather_data["humidity"]) + "%"
show "š Pressure: " + str(weather_data["pressure"]) + " hPa"
show "šØ Wind: " + str(weather_data["wind_speed"]) + " m/s"
show "šļø Visibility: " + str(weather_data["visibility"]) + " meters"
show "ā° Updated: " + weather_data["timestamp"]
show "================================"
function display_simple_weather(weather_data):
show (weather_data["city"] + ": " + str(weather_data["temperature"]) +
"°C, " + weather_data["description"])
function generate_weather_report(location_name, report_file):
weather_data = self.get_weather_data(location_name)
when not weather_data:
show "ā Cannot generate report - no weather data"
return False
report_content = []
report_content.append("Weather Report for " + location_name)
report_content.append("=" * 50)
report_content.append("")
report_content.append("Generated: " + get_current_timestamp())
report_content.append("")
report_content.append("Location: " + weather_data["city"] + ", " + weather_data["country"])
report_content.append("Temperature: " + str(weather_data["temperature"]) + "°C")
report_content.append("Feels like: " + str(weather_data["feels_like"]) + "°C")
report_content.append("Conditions: " + weather_data["description"].title())
report_content.append("Humidity: " + str(weather_data["humidity"]) + "%")
report_content.append("Pressure: " + str(weather_data["pressure"]) + " hPa")
report_content.append("Wind Speed: " + str(weather_data["wind_speed"]) + " m/s")
report_content.append("Visibility: " + str(weather_data["visibility"]) + " meters")
report_content.append("")
report_content.append("Data updated: " + weather_data["timestamp"])
report_text = "\n".join(report_content)
when write_file(report_file, report_text):
show "š Weather report saved: " + report_file
return True
else:
show "ā Failed to save report"
return False
function export_all_data(export_file):
try:
export_data = {
"config": self.config,
"locations": self.locations,
"export_timestamp": get_current_timestamp(),
"version": "1.0"
}
save_data_as_json(export_data, export_file)
show "š¾ All weather data exported to: " + export_file
return True
except error:
show "ā Export failed: " + error.message
return False
class RateLimiter:
function __init__(max_requests, time_window):
self.max_requests = max_requests
self.time_window = time_window
self.requests = []
function can_make_request():
current_time = get_current_timestamp()
think "Remove old requests"
self.requests = [req_time for req_time in self.requests
if time_diff(current_time, req_time) < self.time_window]
return len(self.requests) < self.max_requests
function record_request():
self.requests.append(get_current_timestamp())
think "Let's test our weather application!"
think "Create weather app instance"
weather_app = WeatherApp()
think "Add some locations"
weather_app.add_location("London", 51.5074, -0.1278)
weather_app.add_location("Tokyo", 35.6762, 139.6503)
weather_app.add_location("New York", 40.7128, -74.0060)
think "Display weather for all locations"
locations = ["London", "Tokyo", "New York"]
for location in locations:
weather_app.display_weather(location)
think "Generate detailed report"
weather_app.generate_weather_report("London", "london_weather_report.txt")
think "Export all data"
weather_app.export_all_data("weather_backup.json")
show "š Weather application demo complete!"
š° Project 2: Personal Expense Tracker
š Project Overview
Create a comprehensive expense tracking application that:
- Tracks expenses with categories
- Manages budgets and alerts
- Exports data to CSV
- Provides spending analysis
- Handles data validation
think "Personal Expense Tracker - Complete financial management tool"
class ExpenseTracker:
function __init__():
self.data_file = "expenses.json"
self.expenses = self.load_expenses()
self.categories = ["Food", "Transportation", "Entertainment", "Utilities",
"Healthcare", "Shopping", "Education", "Other"]
self.budgets = {}
self.load_budgets()
function load_expenses():
try:
data = load_data_from_json(self.data_file)
when data:
return data
else:
return self.create_default_data()
except error:
show "ā ļø Creating new expense file"
return self.create_default_data()
function create_default_data():
default_data = {
"expenses": [],
"budgets": {},
"version": "1.0",
"created": get_current_timestamp(),
"last_modified": get_current_timestamp()
}
return default_data
function save_expenses():
try:
self.expenses["last_modified"] = get_current_timestamp()
save_data_as_json(self.expenses, self.data_file)
return True
except error:
show "ā Failed to save expenses: " + error.message
return False
function add_expense(amount, category, description, date):
think "Validate input"
when not self.is_valid_category(category):
show "ā Invalid category. Available: " + ", ".join(self.categories)
return False
when amount <= 0:
show "ā Amount must be positive"
return False
when not description.strip():
show "ā Description cannot be empty"
return False
expense = {
"id": self.generate_expense_id(),
"amount": float(amount),
"category": category,
"description": description.strip(),
"date": date,
"created": get_current_timestamp()
}
self.expenses["expenses"].append(expense)
when self.save_expenses():
show "ā
Added expense: $" + str(amount) + " for " + description
self.check_budget_alert(category, amount)
return expense
else:
show "ā Failed to save expense"
return None
function is_valid_category(category):
return category in self.categories
function generate_expense_id():
expense_count = len(self.expenses["expenses"])
timestamp = get_current_timestamp().replace(" ", "").replace(":", "")
return "exp_" + str(expense_count + 1) + "_" + timestamp
function set_budget(category, monthly_amount):
when not self.is_valid_category(category):
show "ā Invalid category"
return False
when monthly_amount <= 0:
show "ā Budget amount must be positive"
return False
self.expenses["budgets"][category] = {
"monthly_limit": float(monthly_amount),
"set_date": get_current_timestamp()
}
when self.save_expenses():
show "š° Budget set for " + category + ": $" + str(monthly_amount)
return True
else:
show "ā Failed to save budget"
return False
function check_budget_alert(category, new_expense_amount):
when category not in self.expenses["budgets"]:
return
current_month_spending = self.get_monthly_spending(category)
budget_limit = self.expenses["budgets"][category]["monthly_limit"]
percentage_used = (current_month_spending / budget_limit) * 100
when percentage_used >= 100:
show "šØ BUDGET EXCEEDED! " + category + ": $" + str(current_month_spending) + " / $" + str(budget_limit)
when percentage_used >= 80:
show "ā ļø Budget warning: " + category + " at " + str(int(percentage_used)) + "% ($" + str(current_month_spending) + " / $" + str(budget_limit) + ")"
function get_monthly_spending(category):
current_month = get_current_timestamp()[:7] # YYYY-MM format
total = 0
for expense in self.expenses["expenses"]:
when expense["category"] == category and expense["date"].startswith(current_month):
total += expense["amount"]
return total
function get_spending_by_category():
category_totals = {}
for expense in self.expenses["expenses"]:
category = expense["category"]
when category not in category_totals:
category_totals[category] = 0
category_totals[category] += expense["amount"]
return category_totals
function get_spending_by_month():
monthly_totals = {}
for expense in self.expenses["expenses"]:
month = expense["date"][:7] # YYYY-MM
when month not in monthly_totals:
monthly_totals[month] = 0
monthly_totals[month] += expense["amount"]
return monthly_totals
function display_spending_summary():
show "š° ================================"
show "š Expense Summary"
show "================================"
total_expenses = sum(expense["amount"] for expense in self.expenses["expenses"])
show "šø Total Expenses: $" + str(total_expenses)
category_totals = self.get_spending_by_category()
show "\nš Spending by Category:"
for category, amount in category_totals.items():
percentage = (amount / total_expenses) * 100 if total_expenses > 0 else 0
show " " + category + ": $" + str(amount) + " (" + str(int(percentage)) + "%)"
monthly_totals = self.get_spending_by_month()
show "\nš
Monthly Spending:"
for month, amount in sorted(monthly_totals.items()):
show " " + month + ": $" + str(amount)
show "\nš° Budget Status:"
for category, budget_info in self.expenses["budgets"].items():
monthly_spending = self.get_monthly_spending(category)
budget_limit = budget_info["monthly_limit"]
percentage = (monthly_spending / budget_limit) * 100
status = "ā
" if percentage < 80 else "ā ļø" if percentage < 100 else "šØ"
show " " + status + " " + category + ": $" + str(monthly_spending) + " / $" + str(budget_limit) + " (" + str(int(percentage)) + "%)"
show "================================"
function export_to_csv(filename):
try:
headers = ["Date", "Description", "Category", "Amount", "ID"]
data = []
for expense in self.expenses["expenses"]:
row = [
expense["date"],
expense["description"],
expense["category"],
str(expense["amount"]),
expense["id"]
]
data.append(row)
save_data_as_csv(data, filename, headers)
show "š Expenses exported to CSV: " + filename
return True
except error:
show "ā Export failed: " + error.message
return False
function import_from_csv(filename):
try:
headers, data = load_data_from_csv(filename)
when not headers or "Amount" not in headers:
show "ā Invalid CSV format"
return False
imported_count = 0
for row in data:
when len(row) >= 4:
try:
amount = float(row[3])
date = row[0]
description = row[1]
category = row[2]
when self.add_expense(amount, category, description, date):
imported_count += 1
except error:
show "ā ļø Skipped invalid row: " + str(row)
show "ā
Imported " + str(imported_count) + " expenses from CSV"
return True
except error:
show "ā Import failed: " + error.message
return False
function search_expenses(query):
results = []
query_lower = query.lower()
for expense in self.expenses["expenses"]:
when (query_lower in expense["description"].lower() or
query_lower in expense["category"].lower()):
results.append(expense)
return results
function delete_expense(expense_id):
for i, expense in enumerate(self.expenses["expenses"]):
when expense["id"] == expense_id:
deleted_expense = self.expenses["expenses"].pop(i)
when self.save_expenses():
show "ā
Deleted expense: " + deleted_expense["description"]
return True
else:
show "ā Failed to delete expense"
return False
show "ā Expense not found: " + expense_id
return False
think "Let's test our expense tracker!"
think "Create expense tracker instance"
tracker = ExpenseTracker()
think "Set up some budgets"
tracker.set_budget("Food", 500)
tracker.set_budget("Transportation", 200)
tracker.set_budget("Entertainment", 150)
think "Add some expenses"
tracker.add_expense(45.50, "Food", "Weekly groceries", "2025-07-01")
tracker.add_expense(32.00, "Transportation", "Gas for car", "2025-07-02")
tracker.add_expense(4.75, "Food", "Coffee", "2025-07-03")
tracker.add_expense(15.00, "Entertainment", "Movie tickets", "2025-07-04")
tracker.add_expense(12.50, "Food", "Lunch", "2025-07-05")
tracker.add_expense(85.00, "Shopping", "New shoes", "2025-07-06")
tracker.add_expense(25.00, "Transportation", "Bus pass", "2025-07-07")
think "Display spending summary"
tracker.display_spending_summary()
think "Export to CSV"
tracker.export_to_csv("my_expenses.csv")
think "Search expenses"
coffee_expenses = tracker.search_expenses("coffee")
show "ā Coffee expenses found: " + str(len(coffee_expenses))
think "Test budget alerts by adding a large expense"
tracker.add_expense(400, "Food", "Large grocery order", "2025-07-08")
show "š Expense tracker demo complete!"
š” Learning Checkpoint 7
šÆ What did we learn?
- How to build complete applications with external data
- How to implement caching and rate limiting
- How to create data export/import systems
- How to handle complex data validation
- How to create user-friendly error messages
š Final Progress Update
Chapter 7 Progress: [āāāāāāāāāā] 100% Complete! š
āāā ā
Files and Reading (3/3 sections) - COMPLETE!
āāā ā
Data Formats (3/3 sections) - COMPLETE!
āāā ā
Error Handling (3/3 sections) - COMPLETE!
āāā ā
API Integration (3/3 sections) - COMPLETE!
āāā ā
Real-World Projects (2/2 projects) - COMPLETE!
šÆ Chapter Summary
š What You've Accomplished
Congratulations! You've mastered working with external data in Sona. You can now:
-
š File Operations:
- Read and write files safely
- Handle directory operations
- Create backup systems
- Implement logging
-
š Data Formats:
- Work with JSON data
- Process CSV files
- Manage configuration files
- Transform data between formats
-
ā ļø Error Handling:
- Handle file system errors gracefully
- Implement retry logic
- Use defensive programming
- Provide helpful error messages
-
š API Integration:
- Make HTTP requests safely
- Handle API responses
- Implement caching strategies
- Respect rate limits
-
š Real-World Applications:
- Built a complete weather application
- Created a personal expense tracker
- Implemented data export/import
- Added search and analysis features
š§ Accessibility Learning Review
For ADHD learners: You used progress tracking and bite-sized sections For Autism learners: You followed consistent patterns and detailed explanations For Learning Disabilities: You used plain language and visual aids For Executive Function: You worked with organized workflows and clear objectives
š Next Steps
- Practice: Build your own data-driven applications
- Experiment: Try different APIs and data sources
- Expand: Add more features to your projects
- Share: Show your applications to others
- Learn: Explore advanced topics in the next chapters
š” Pro Tips for External Data
- Always validate data before processing
- Use caching to improve performance
- Handle errors gracefully with helpful messages
- Respect rate limits when using APIs
- Back up important data regularly
- Keep user data secure and private
Remember: Working with external data is about building bridges between your application and the wider world. Every successful app needs to save, load, and share data effectively! }
self.expenses.budgets[category] = {
"monthly_amount": monthly_amount,
"set_date": get_current_timestamp()
}
if self.save_expenses() {
print("Set budget for " + category + ": $" + monthly_amount + "/month")
return true
}
return false
}
func check_budget_alert(category, new_expense_amount) {
if self.expenses.budgets[category] == null {
return // No budget set
}
let current_month = self.get_current_month()
let monthly_spending = self.get_monthly_spending(category, current_month)
let budget_amount = self.expenses.budgets[category].monthly_amount
let new_total = monthly_spending + new_expense_amount
let percentage_used = (new_total / budget_amount) * 100
if percentage_used >= 100 {
print("šØ BUDGET ALERT: You've exceeded your " + category + " budget!")
print(" Budget: $" + budget_amount + " | Spent: $" + new_total)
} else if percentage_used >= 80 {
print("ā ļø BUDGET WARNING: You've used " + percentage_used.toFixed(1) + "% of your " + category + " budget")
print(" Budget: $" + budget_amount + " | Spent: $" + new_total)
}
}
func get_current_month() {
// Simplified - in real implementation, extract from current date
return "2025-06"
}
func get_monthly_spending(category, month) {
let total = 0
for expense in self.expenses.expenses {
if expense.category == category && expense.date.starts_with(month) {
total = total + expense.amount
}
}
return total
}
func get_spending_summary(month) {
let summary = {}
let total_spending = 0
for category in self.categories {
let category_spending = self.get_monthly_spending(category, month)
summary[category] = category_spending
total_spending = total_spending + category_spending
}
summary["total"] = total_spending
return summary
}
func generate_monthly_report(month, output_file) {
let summary = self.get_spending_summary(month)
let report_lines = []
report_lines = report_lines + ["Monthly Expense Report - " + month]
report_lines = report_lines + ["Generated: " + get_current_timestamp()]
report_lines = report_lines + ["=" * 50]
report_lines = report_lines + [""]
// Category breakdown
report_lines = report_lines + ["Spending by Category:"]
for category in self.categories {
let amount = summary[category]
let budget = self.expenses.budgets[category]
if budget != null {
let percentage = (amount / budget.monthly_amount) * 100
report_lines = report_lines + [
" " + category + ": $" + amount + " (Budget: $" +
budget.monthly_amount + ", " + percentage.toFixed(1) + "% used)"
]
} else {
report_lines = report_lines + [" " + category + ": $" + amount + " (No budget set)"]
}
}
report_lines = report_lines + [""]
report_lines = report_lines + ["Total Spending: $" + summary.total]
report_lines = report_lines + [""]
// Individual transactions
report_lines = report_lines + ["Detailed Transactions:"]
let monthly_expenses = self.get_expenses_for_month(month)
for expense in monthly_expenses {
report_lines = report_lines + [
" " + expense.date + " | " + expense.category + " | $" +
expense.amount + " | " + expense.description
]
}
// Insights
report_lines = report_lines + [""]
report_lines = report_lines + ["Insights:"]
let insights = self.generate_insights(summary)
for insight in insights {
report_lines = report_lines + [" ⢠" + insight]
}
let report_content = report_lines.join("\n")
try {
write_file(output_file, report_content)
print("Monthly report saved to " + output_file)
return true
} catch (error) {
print("Error saving report: " + error.message)
return false
}
}
func get_expenses_for_month(month) {
let monthly_expenses = []
for expense in self.expenses.expenses {
if expense.date.starts_with(month) {
monthly_expenses = monthly_expenses + [expense]
}
}
return monthly_expenses
}
func generate_insights(summary) {
let insights = []
let highest_category = ""
let highest_amount = 0
// Find highest spending category
for category in self.categories {
if summary[category] > highest_amount {
highest_amount = summary[category]
highest_category = category
}
}
if highest_category != "" {
insights = insights + ["Highest spending category: " + highest_category + " ($" + highest_amount + ")"]
}
// Budget analysis
let over_budget_categories = []
for category in self.categories {
let budget = self.expenses.budgets[category]
if budget != null && summary[category] > budget.monthly_amount {
over_budget_categories = over_budget_categories + [category]
}
}
if over_budget_categories.length > 0 {
insights = insights + ["Categories over budget: " + over_budget_categories.join(", ")]
}
// Spending patterns
if summary.total > 0 {
let avg_daily_spending = summary.total / 30 // Approximate
insights = insights + ["Average daily spending: $" + avg_daily_spending.toFixed(2)]
}
return insights
}
func export_to_csv(output_file, month) {
let csv_lines = []
// Header
csv_lines = csv_lines + ["Date,Category,Amount,Description,ID"]
// Data rows
let expenses_to_export = month != null ?
self.get_expenses_for_month(month) :
self.expenses.expenses
for expense in expenses_to_export {
let csv_line = expense.date + "," +
expense.category + "," +
expense.amount + "," +
'"' + expense.description + '"' + "," +
expense.id
csv_lines = csv_lines + [csv_line]
}
let csv_content = csv_lines.join("\n")
try {
write_file(output_file, csv_content)
print("Expenses exported to CSV: " + output_file)
return true
} catch (error) {
print("Error exporting CSV: " + error.message)
return false
}
}
func import_from_csv(input_file) {
try {
let content = read_file(input_file)
let lines = content.split("\n")
if lines.length < 2 {
print("CSV file is empty or invalid")
return false
}
let imported_count = 0
// Skip header row
for i in range(1, lines.length) {
let line = lines[i].trim()
if line.length == 0 {
continue
}
let parts = self.parse_csv_line(line)
if parts.length >= 4 {
let date = parts[0]
let category = parts[1]
let amount = parseFloat(parts[2])
let description = parts[3]
if self.add_expense(amount, category, description, date) != null {
imported_count = imported_count + 1
}
}
}
print("Imported " + imported_count + " expenses from CSV")
return true
} catch (error) {
print("Error importing CSV: " + error.message)
return false
}
}
func parse_csv_line(line) {
// Simple CSV parser - handles quoted fields
let parts = []
let current_part = ""
let in_quotes = false
for i in range(0, line.length) {
let char = line[i]
if char == '"' {
in_quotes = !in_quotes
} else if char == ',' && !in_quotes {
parts = parts + [current_part]
current_part = ""
} else {
current_part = current_part + char
}
}
// Add the last part
parts = parts + [current_part]
return parts
}
func display_dashboard() {
let current_month = self.get_current_month()
let summary = self.get_spending_summary(current_month)
print("\nš° Expense Dashboard - " + current_month + " š°")
print("=" * 40)
for category in self.categories {
let spent = summary[category]
let budget = self.expenses.budgets[category]
if budget != null {
let percentage = (spent / budget.monthly_amount) * 100
let bar = self.create_progress_bar(percentage)
print(category + ": $" + spent + " / $" + budget.monthly_amount + " " + bar)
} else {
print(category + ": $" + spent + " (No budget)")
}
}
print("=" * 40)
print("Total Spending: $" + summary.total)
print("=" * 40 + "\n")
}
func create_progress_bar(percentage) {
let bar_length = 20
let filled_length = (percentage / 100) * bar_length
let bar = ""
for i in range(0, bar_length) {
if i < filled_length {
bar = bar + "ā"
} else {
bar = bar + "ā"
}
}
let color = percentage >= 100 ? "š“" : (percentage >= 80 ? "š”" : "š¢")
return "[" + bar + "] " + percentage.toFixed(1) + "% " + color
}
}
// Expense tracker demonstration print("=== Personal Expense Tracker Demo ===\n")
let expense_tracker = ExpenseTracker()
// Set some budgets expense_tracker.set_budget("Food", 500) expense_tracker.set_budget("Transportation", 200) expense_tracker.set_budget("Entertainment", 150) expense_tracker.set_budget("Shopping", 300)
// Add some sample expenses expense_tracker.add_expense(45.50, "Food", "Grocery shopping at Safeway", "2025-06-01") expense_tracker.add_expense(12.75, "Transportation", "Bus fare", "2025-06-02") expense_tracker.add_expense(25.00, "Entertainment", "Movie tickets", "2025-06-03") expense_tracker.add_expense(85.20, "Food", "Dinner with friends", "2025-06-05") expense_tracker.add_expense(120.00, "Shopping", "New shoes", "2025-06-07") expense_tracker.add_expense(35.40, "Transportation", "Uber ride", "2025-06-10") expense_tracker.add_expense(15.99, "Entertainment", "Netflix subscription", "2025-06-15") expense_tracker.add_expense(200.00, "Shopping", "Electronics", "2025-06-20")
// Display dashboard expense_tracker.display_dashboard()
// Generate reports expense_tracker.generate_monthly_report("2025-06", "june_2025_expense_report.txt") expense_tracker.export_to_csv("june_2025_expenses.csv", "2025-06")
// Export all data for backup expense_tracker.save_expenses()
print("Expense tracker demo complete!")
## Chapter Summary
### Key Concepts Mastered
- **File Operations**: Reading, writing, and managing files safely
- **JSON Handling**: Parsing, generating, and working with structured data
- **Error Handling**: Robust error management for external resources
- **Data Validation**: Ensuring data integrity and format compliance
- **CSV Processing**: Import/export functionality for spreadsheet compatibility
- **Configuration Management**: Loading and saving application settings
- **Data Persistence**: Maintaining application state across sessions
### Skills Acquired
- [ ] Read and write files with proper error handling
- [ ] Parse and generate JSON data structures
- [ ] Implement data validation and format checking
- [ ] Create backup and recovery systems
- [ ] Build applications that save and load user data
- [ ] Generate reports and export data in multiple formats
- [ ] Handle configuration files and application settings
### Real-World Applications Built
1. **Weather Application**: Data fetching, storage, alerts, and reporting
2. **Expense Tracker**: Personal finance management with budgets and insights
3. **File Management System**: Robust file operations with error handling
4. **User Profile Manager**: JSON-based data persistence and migration
### Professional Context
Working with external data is fundamental to modern applications:
- **Web Applications**: API integration, user data persistence
- **Mobile Apps**: Local storage, cloud synchronization
- **Desktop Applications**: File management, settings persistence
- **Data Analysis**: Import/export, data transformation
- **System Integration**: Configuration management, logging
### Next Steps
In Chapter 8, we'll focus on Testing and Debugging your Sona code. You'll learn how to write comprehensive tests for the file operations and data handling code you've built, ensuring your applications are reliable and maintainable.
---
_Andre's Data Philosophy: External data makes your applications truly useful. The difference between a toy program and a professional application is often how well it handles real-world data - messy, inconsistent, and unpredictable as it may be. Master these patterns, and you'll build applications that users can depend on for their important information._