Spaces:
Sleeping
Sleeping
| # 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) |