| 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() |
|
|