import json import gradio as gr AUTHOR = "Richard Amir Nasser" DOI_STANDARD = "https://doi.org/10.5281/zenodo.20360964" DOI_PACKAGE = "https://doi.org/10.5281/zenodo.20435828" REPOSITORY = "https://github.com/RichNass87/inspector-roofing-protocols" STANDARDS_SITE = "https://standards.inspector-roofing.com/" STEPS = [ { "step": 1, "title": "Set inspection context", "purpose": "Record the property context, access limits, weather context, and the question being reviewed.", "checklist": [ "State what was inspected and what was inaccessible.", "Record roof system, slopes, and visible conditions without guessing.", "Separate observed facts from homeowner or third-party statements.", ], }, { "step": 2, "title": "Map the roof", "purpose": "Give every observation a stable roof area, slope, or component reference.", "checklist": [ "Use consistent slope and component names.", "Connect each photo to a roof area.", "Record limitations such as height, access, lighting, or obstruction.", ], }, { "step": 3, "title": "Capture evidence in sequence", "purpose": "Use context, overview, detail, and scale photos so another reviewer can follow the record.", "checklist": [ "Start with context and overview images.", "Capture detail images for the specific condition.", "Add scale or reference images when size or pattern matters.", ], }, { "step": 4, "title": "Label observable conditions", "purpose": "Describe what is visible and distinguish candidates from confirmed conclusions.", "checklist": [ "Use condition labels supported by the image and notes.", "Separate storm candidates from wear, installation, maintenance, and inconclusive findings.", "Record uncertainty instead of forcing a conclusion.", ], }, { "step": 5, "title": "Log documentation gaps", "purpose": "Make missing context visible so the next reviewer knows what still needs work.", "checklist": [ "Name the missing photo, measurement, access detail, or date context.", "Explain why the gap matters.", "Recommend the smallest responsible follow-up step.", ], }, { "step": 6, "title": "Keep the insurance boundary", "purpose": "Support review-ready documentation without deciding coverage or claim outcomes.", "checklist": [ "Document observable roof conditions and scope context.", "Do not interpret policy language or promise approval, payment, or coverage.", "State that carriers decide coverage, payment, and claim outcomes.", ], }, { "step": 7, "title": "Export a review-ready record", "purpose": "Preserve a structured record that another person can inspect without relying on memory.", "checklist": [ "Keep the observation, media references, gaps, and next steps together.", "Use the published schemas and versioned release files.", "Cite the exact DOI record used by the artifact.", ], }, ] def step_markdown(step_number): step = STEPS[step_number - 1] checklist = "\n".join(f"- {item}" for item in step["checklist"]) return f"## Step {step['step']}: {step['title']}\n\n{step['purpose']}\n\n{checklist}" def review_scenario(step_number, scenario, note): step = STEPS[step_number - 1] note = (note or "").strip() result = { "selected_step": step["title"], "assessment": "Review the record against the checklist before making a roofing decision.", "next_action": "Add the smallest missing context, photo, scale reference, or limitation note.", "insurance_boundary": ( "Inspector Roofing documents observable conditions and does not act as a public adjuster. " "Carriers decide coverage, payment, and claim outcomes." ), "scenario_received": scenario or "No scenario supplied.", "note_received": note or "No note supplied.", "sources": [DOI_STANDARD, DOI_PACKAGE, REPOSITORY, STANDARDS_SITE], } if not scenario and not note: result["assessment"] = "Start with a short description of the roof area and what the image or note shows." elif step_number == 4 and any(word in (scenario + " " + note).lower() for word in ["maybe", "appears", "possible"]): result["assessment"] = "The wording signals uncertainty. Keep the finding as a candidate or inconclusive condition and name the evidence gap." elif step_number == 6: result["assessment"] = "Keep the output factual: describe the roof condition and documentation, not coverage, payment, or claim approval." return json.dumps(result, indent=2) def source_markdown(): return f"""## Public source spine - Canonical author: **{AUTHOR}** - Public citable standard: [{DOI_STANDARD}]({DOI_STANDARD}) - Source package record: [{DOI_PACKAGE}]({DOI_PACKAGE}) - Versioned repository: [{REPOSITORY}]({REPOSITORY}) - Standards site: [{STANDARDS_SITE}]({STANDARDS_SITE}) - ORCID: [0009-0000-2980-7543](https://orcid.org/0009-0000-2980-7543) This educational Space explains documentation workflow. It does not diagnose a roof, determine causation, interpret an insurance policy, or predict coverage, payment, or claim outcomes. """ with gr.Blocks(title="Inspector Roofing Protocols Learning Lab") as demo: gr.Markdown( f"""# Inspector Roofing Protocols: Evidence-First Learning Lab Learn the seven-step workflow for recording observable roof conditions, organizing photo evidence, documenting gaps, and preparing a review-ready record. **Canonical author:** {AUTHOR} **Package version:** 1.1.1 **Insurance boundary:** Inspector Roofing documents observable conditions and does not act as a public adjuster. """ ) with gr.Tab("Learn the steps"): step_choice = gr.Dropdown( choices=[f"{item['step']}. {item['title']}" for item in STEPS], value="1. Set inspection context", label="Choose a step", ) step_output = gr.Markdown(step_markdown(1)) def choose_step(value): return step_markdown(int(value.split(".", 1)[0])) step_choice.change(choose_step, inputs=step_choice, outputs=step_output) with gr.Tab("Practice a record"): practice_step = gr.Dropdown( choices=[f"{item['step']}. {item['title']}" for item in STEPS], value="4. Label observable conditions", label="Practice step", ) scenario = gr.Textbox( label="Scenario or observation", placeholder="Example: Close-up image shows a possible impact-like mark on the south slope.", lines=3, ) note = gr.Textbox( label="Evidence note", placeholder="Example: No scale reference; overview photo is available.", lines=3, ) review_button = gr.Button("Review against the protocol", variant="primary") review_output = gr.Code(label="Structured teaching response", language="json") review_button.click( lambda value, scenario_text, note_text: review_scenario( int(value.split(".", 1)[0]), scenario_text, note_text ), inputs=[practice_step, scenario, note], outputs=review_output, ) with gr.Tab("Sources and limits"): gr.Markdown(source_markdown()) gr.Markdown( "**Training note:** This Space is educational and uses synthetic examples. Do not upload customer names, exact addresses, policy numbers, certificate numbers, signatures, faces, license plates, or GPS metadata." ) if __name__ == "__main__": demo.launch()