lighthouse / lighthouse.py
alxcspr's picture
Upload folder using huggingface_hub
b3d58ff verified
# To run this app:
# 1. Install Gradio: !pip install gradio
# 2. Run from your terminal: python lighthouse.py
import gradio as gr
import json
from datetime import date
# ===================================================================================
# PROJECT CODE STARTS HERE
# Students will modify the code below this line.
# ===================================================================================
# ### WEEK 1: The Blueprint - Modeling a Lighthouse ###
# Focus: Abstraction, Automation, Analysis
# -----------------------------------------------------------------------------------
# TODO: Implement the Lighthouse data model.
# - Use a class to represent a lighthouse.
# - Add validation logic inside its __init__ method.
class Lighthouse:
"""Represents a single lighthouse with validation."""
def __init__(self, name: str, location: str, is_operational: bool, last_inspected_date: str):
# Validate name: must be a non-empty string
if not isinstance(name, str) or not name.strip():
raise ValueError("Name must be a non-empty string.")
# Validate location: must be a non-empty string
if not isinstance(location, str) or not location.strip():
raise ValueError("Location must be a non-empty string.")
# Validate is_operational: must be a boolean
if not isinstance(is_operational, bool):
raise TypeError("Is Operational must be a boolean (True/False).")
# Validate date: must be in 'YYYY-MM-DD' format
try:
parsed_date = date.fromisoformat(last_inspected_date)
except (ValueError, TypeError):
raise ValueError("Last Inspected Date must be in YYYY-MM-DD format.")
self.name = name.strip()
self.location = location.strip()
self.is_operational = is_operational
self.last_inspected_date = parsed_date
def to_dict(self):
"""Converts the object to a dictionary for JSON serialization."""
return {
"name": self.name,
"location": self.location,
"is_operational": self.is_operational,
"last_inspected_date": self.last_inspected_date.isoformat()
}
def __repr__(self):
"""Provides a developer-friendly string representation of the object."""
status = "Operational" if self.is_operational else "Non-Operational"
return f"βœ… {self.name} ({self.location}) - Status: {status}, Last Inspected: {self.last_inspected_date.isoformat()}"
# ### WEEK 5: Scaling Up - The Lighthouse Authority ###
# Focus: Abstraction, Automation, Analysis
# -----------------------------------------------------------------------------------
# TODO: Refactor all functions into this LighthouseRegistry class.
class LighthouseRegistry:
"""Manages the collection of all lighthouses."""
def __init__(self, filepath="lighthouses.json"):
self.lighthouses = []
self.filepath = filepath
# ### WEEK 2: First Steps - The Lighthouse Logbook ###
# TODO: Implement the add_lighthouse method.
def add_lighthouse(self, name: str, location: str, is_operational: bool, last_inspected_date: str):
"""Creates and adds a new Lighthouse to the registry after validation."""
try:
new_lighthouse = Lighthouse(name, location, is_operational, last_inspected_date)
self.lighthouses.append(new_lighthouse)
return f"Success: Added '{name}' to the registry."
except (ValueError, TypeError) as e:
return f"❌ Error: Could not add lighthouse. {e}"
# ### WEEK 3: Making Sense of the Data - Fleet Status Reports ###
# TODO: Implement the generate_status_report method for ANALYSIS.
def generate_status_report(self):
"""Analyzes the current list of lighthouses and generates a summary report."""
if not self.lighthouses:
return "Cannot generate report: No lighthouses in the registry."
total = len(self.lighthouses)
operational_count = sum(1 for lh in self.lighthouses if lh.is_operational)
non_operational_count = total - operational_count
most_recent_lighthouse = max(self.lighthouses, key=lambda lh: lh.last_inspected_date)
report = (
"πŸ“Š ===== Lighthouse Fleet Status Report ===== πŸ“Š\n"
f"Total Lighthouses: {total}\n"
f" - Operational: {operational_count}\n"
f" - Non-Operational: {non_operational_count}\n\n"
f"Most Recently Inspected:\n"
f" - Name: {most_recent_lighthouse.name}\n"
f" - Date: {most_recent_lighthouse.last_inspected_date.isoformat()}\n"
"=========================================="
)
return report
def list_lighthouses(self):
"""Returns a formatted string listing all lighthouses in the registry."""
if not self.lighthouses:
return "No lighthouses in the registry."
header = "--- List of All Lighthouses ---"
# Use the __repr__ from the Lighthouse class for a clean format
report_lines = [repr(lh) for lh in self.lighthouses]
return f"{header}\n" + "\n".join(report_lines)
# ### WEEK 4: Making It Real - The Official Record ###
# TODO: Implement save_to_file and load_from_file to AUTOMATE persistence.
def save_to_file(self):
"""Saves the current list of lighthouses to the JSON file."""
try:
# Convert each Lighthouse object to its dictionary representation
data_to_save = [lh.to_dict() for lh in self.lighthouses]
with open(self.filepath, 'w') as f:
json.dump(data_to_save, f, indent=4)
return f"πŸ’Ύ Success: Saved {len(self.lighthouses)} lighthouses to {self.filepath}."
except (IOError, TypeError) as e:
return f"❌ Error: Could not save to file. {e}"
def load_from_file(self):
"""Loads lighthouses from the JSON file, recreating the objects."""
try:
with open(self.filepath, 'r') as f:
data = json.load(f)
self.lighthouses = [] # Clear the current list before loading
for item in data:
# The Lighthouse constructor handles validation of the loaded data
lh = Lighthouse(**item)
self.lighthouses.append(lh)
return f"πŸ“‚ Success: Loaded {len(self.lighthouses)} lighthouses from {self.filepath}."
except FileNotFoundError:
return f"ℹ️ Info: No save file found at '{self.filepath}'. Starting with an empty registry."
except (json.JSONDecodeError, ValueError, TypeError) as e:
# Catches corrupted JSON, or data that fails Lighthouse validation
return f"❌ Error: Could not load from file. File may be corrupt or malformed. {e}"
# ===================================================================================
# GRADIO UI CODE
# Students should not need to modify below this line initially,
# but can extend it in Week 5.
# ===================================================================================
def create_ui(registry: LighthouseRegistry):
"""Creates the Gradio web interface."""
# Helper functions to interact with the registry and update the UI
def add_and_update(name, location, is_op, date_str, current_log):
result = registry.add_lighthouse(name, location, is_op, date_str)
new_log = f"{current_log}\n> {result}"
return new_log
def report_and_update(current_log):
result = registry.generate_status_report()
new_log = f"{current_log}\n\n{result}\n"
return new_log
def list_and_update(current_log):
result = registry.list_lighthouses()
new_log = f"{current_log}\n\n{result}\n"
return new_log
def save_and_update(current_log):
result = registry.save_to_file()
new_log = f"{current_log}\n> {result}"
return new_log
def load_and_update(current_log):
result = registry.load_from_file()
new_log = f"{current_log}\n> {result}"
# After loading, also list the lighthouses to show what was loaded
list_result = registry.list_lighthouses()
return f"{new_log}\n\n{list_result}\n"
# Define the Gradio interface layout
with gr.Blocks(theme=gr.themes.Monochrome(), title="Lighthouse Lookout") as app:
gr.Markdown("# 🚒 Lighthouse Lookout")
gr.Markdown("A tool for monitoring and managing a fleet of lighthouses.")
log_textbox = gr.Textbox(
label="Log & Reports",
value="Welcome! Your command results will appear here.",
lines=15,
interactive=False,
elem_classes="terminal" # Just for potential styling
)
with gr.Row():
report_btn = gr.Button("πŸ“Š Generate Status Report")
list_btn = gr.Button("πŸ“‹ List All Lighthouses")
with gr.Accordion("βž• Add a New Lighthouse", open=False):
with gr.Row():
name_input = gr.Textbox(label="Name", placeholder="e.g., Trinity Lighthouse")
location_input = gr.Textbox(label="Location", placeholder="e.g., 50.18, -0.16")
with gr.Row():
is_operational_input = gr.Checkbox(label="Is Operational?", value=True)
# Using a string for the date to align with the Lighthouse class constructor
date_input = gr.Textbox(label="Last Inspected Date", placeholder="YYYY-MM-DD")
add_btn = gr.Button("Add Lighthouse")
with gr.Row():
save_btn = gr.Button("πŸ’Ύ Save Registry")
load_btn = gr.Button("πŸ“‚ Load Registry")
# Connect UI components to the helper functions
add_btn.click(
fn=add_and_update,
inputs=[name_input, location_input, is_operational_input, date_input, log_textbox],
outputs=[log_textbox]
)
report_btn.click(fn=report_and_update, inputs=[log_textbox], outputs=[log_textbox])
list_btn.click(fn=list_and_update, inputs=[log_textbox], outputs=[log_textbox])
save_btn.click(fn=save_and_update, inputs=[log_textbox], outputs=[log_textbox])
load_btn.click(fn=load_and_update, inputs=[log_textbox], outputs=[log_textbox])
return app
# --- Main execution block ---
if __name__ == "__main__":
# Create a single instance of our registry that the app will use.
lighthouse_registry = LighthouseRegistry()
# Load any existing data on startup
initial_load_message = lighthouse_registry.load_from_file()
print(initial_load_message) # Print to terminal for the developer
# Create and launch the Gradio app
web_app = create_ui(lighthouse_registry)
web_app.launch(share=True)