Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Code Quality

on:
push:
branches: [main]

pull_request:
branches: [main]

jobs:
lint:
runs-on: ubuntu-latest

defaults:
run:
working-directory: code

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install Development Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt

- name: Run Black
run: black --check .

- name: Run Ruff
run: ruff check . --exclude notebooks

301 changes: 229 additions & 72 deletions code/app.py

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion code/evaluation/main.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# evaluation/main.py


def run_evaluation():
"""
Future model evaluation module.
"""
pass
pass
78 changes: 29 additions & 49 deletions code/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,75 +13,55 @@
results = []

for _, row in claims.head(5).iterrows():

history_row = history_df[
history_df["user_id"] == row["user_id"]
]

history_row = history_df[history_df["user_id"] == row["user_id"]]

risk_flags = history_row.iloc[0]["history_flags"]

claim_info = extract_claim(
row["user_claim"]
)
claim_info = extract_claim(row["user_claim"])

image_path = row["image_paths"].split(";")[0]

image_id = os.path.splitext(
os.path.basename(image_path)
)[0]

image_id = os.path.splitext(os.path.basename(image_path))[0]

full_path = "../dataset/" + image_path

if not os.path.exists(full_path):
continue

image_result = analyze_image(
full_path,
row["claim_object"]
)
image_result = analyze_image(full_path, row["claim_object"])
time.sleep(15)

evidence_met, evidence_reason = check_evidence(
image_result["damage_visible"],
image_result["valid_image"]
image_result["damage_visible"], image_result["valid_image"]
)

claim_status, justification = decide_claim(
evidence_met,
image_result["damage_visible"]
evidence_met, image_result["damage_visible"]
)

results.append({
"user_id": row["user_id"],
"image_paths": row["image_paths"],

"user_claim": row["user_claim"],
"claim_object": row["claim_object"],

"evidence_standard_met": evidence_met,
"evidence_standard_met_reason": evidence_reason,

"risk_flags": risk_flags,

"issue_type": image_result["issue_type"],
"object_part": image_result["object_part"],

"claim_status": claim_status,
"claim_status_justification": justification,

"supporting_image_ids": image_id,

"valid_image": image_result["valid_image"],

"severity": image_result["severity"]
})
results.append(
{
"user_id": row["user_id"],
"image_paths": row["image_paths"],
"user_claim": row["user_claim"],
"claim_object": row["claim_object"],
"evidence_standard_met": evidence_met,
"evidence_standard_met_reason": evidence_reason,
"risk_flags": risk_flags,
"issue_type": image_result["issue_type"],
"object_part": image_result["object_part"],
"claim_status": claim_status,
"claim_status_justification": justification,
"supporting_image_ids": image_id,
"valid_image": image_result["valid_image"],
"severity": image_result["severity"],
}
)

output_df = pd.DataFrame(results)

output_df.to_csv(
"output.csv",
index=False
)
output_df.to_csv("output.csv", index=False)

print("output.csv generated successfully")
print("Rows:", len(output_df))
print("Rows:", len(output_df))
40 changes: 34 additions & 6 deletions code/report_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,10 @@ def generate_pdf_report(
REPORTS_DIR.mkdir(parents=True, exist_ok=True)

timestamp = datetime.now()
pdf_path = REPORTS_DIR / f"claim_report_{timestamp.strftime('%Y%m%d_%H%M%S')}_{uuid4().hex[:8]}.pdf"
pdf_path = (
REPORTS_DIR
/ f"claim_report_{timestamp.strftime('%Y%m%d_%H%M%S')}_{uuid4().hex[:8]}.pdf"
)
styles = _build_styles()

doc = SimpleDocTemplate(
Expand Down Expand Up @@ -203,10 +206,27 @@ def generate_pdf_report(
_key_value_table(
[
("Claim Object", claim_object),
("Claim Description", claim_description or "No claim description provided."),
(
"Claim Description",
claim_description or "No claim description provided.",
),
("Claim Status", claim_status or "Not assigned"),
("AI Confidence", f"{confidence_score}%" if confidence_score is not None else "Not available"),
("Fraud Risk Score", fraud_risk_score if fraud_risk_score is not None else "Not available"),
(
"AI Confidence",
(
f"{confidence_score}%"
if confidence_score is not None
else "Not available"
),
),
(
"Fraud Risk Score",
(
fraud_risk_score
if fraud_risk_score is not None
else "Not available"
),
),
("Risk Level", risk_level or "Not available"),
("Estimated Repair Cost", estimated_repair_cost or "Not available"),
],
Expand All @@ -219,7 +239,12 @@ def generate_pdf_report(
if embedded_image:
story.extend([embedded_image, Spacer(1, 8)])
else:
story.append(Paragraph("Uploaded image could not be embedded in the PDF.", styles["BodyTextWrapped"]))
story.append(
Paragraph(
"Uploaded image could not be embedded in the PDF.",
styles["BodyTextWrapped"],
)
)

story.extend(
[
Expand All @@ -237,7 +262,10 @@ def generate_pdf_report(
styles,
),
Paragraph("AI Explanation", styles["SectionHeading"]),
Paragraph(ai_explanation or "No additional explanation provided.", styles["BodyTextWrapped"]),
Paragraph(
ai_explanation or "No additional explanation provided.",
styles["BodyTextWrapped"],
),
Paragraph("Final AI Assessment", styles["SectionHeading"]),
Paragraph(final_assessment, styles["BodyTextWrapped"]),
]
Expand Down
38 changes: 9 additions & 29 deletions code/src/claim_extractor.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,22 @@

ISSUE_MAP = {
"dent": "dent",
"dented": "dent",

"scratch": "scratch",
"scratched": "scratch",

"crack": "crack",
"cracked": "crack",

"shatter": "glass_shatter",
"shattered": "glass_shatter",

"broken": "broken_part",
"damage": "broken_part",
"damaged": "broken_part",
"affected": "broken_part",

"missing": "missing_part",

"water": "water_damage",
"wet": "water_damage",

"stain": "stain",

"torn": "torn_packaging",
"crushed": "crushed_packaging"
"crushed": "crushed_packaging",
}

PARTS = [
Expand All @@ -47,27 +38,19 @@
"corner",
"seal",
"label",
"contents"
"contents",
]


def extract_claim(claim_text):
text = claim_text.lower()

# Normalize common compound words
text = text.replace(
"frontbumper",
"front bumper"
)

text = text.replace(
"rearbumper",
"rear bumper"
)
# Normalize common compound words
text = text.replace("frontbumper", "front bumper")

text = text.replace("rearbumper", "rear bumper")

text = text.replace(
"sidemirror",
"side mirror"
)
text = text.replace("sidemirror", "side mirror")

issue_type = "unknown"
object_part = "unknown"
Expand All @@ -82,7 +65,4 @@ def extract_claim(claim_text):
object_part = part.replace(" ", "_")
break

return {
"issue_type": issue_type,
"object_part": object_part
}
return {"issue_type": issue_type, "object_part": object_part}
20 changes: 4 additions & 16 deletions code/src/decision_engine.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
def decide_claim(
evidence_met,
damage_visible
):
def decide_claim(evidence_met, damage_visible):
if not evidence_met:
return (
"not_enough_information",
"Insufficient visual evidence"
)
return ("not_enough_information", "Insufficient visual evidence")

if damage_visible:
return (
"supported",
"Damage visible in image"
)
return ("supported", "Damage visible in image")

return (
"contradicted",
"Claimed damage not observed"
)
return ("contradicted", "Claimed damage not observed")
20 changes: 4 additions & 16 deletions code/src/evidence_checker.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
def check_evidence(
damage_visible,
valid_image
):
def check_evidence(damage_visible, valid_image):
if not valid_image:
return (
False,
"Image not usable for review"
)
return (False, "Image not usable for review")

if not damage_visible:
return (
False,
"Claimed damage not visible"
)
return (False, "Claimed damage not visible")

return (
True,
"Sufficient visual evidence"
)
return (True, "Sufficient visual evidence")
9 changes: 2 additions & 7 deletions code/src/history_checker.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
def get_history_flags(history_row):
history_flags = str(
history_row["history_flags"]
).strip()
history_flags = str(history_row["history_flags"]).strip()

if history_flags.lower() == "none":
return ["none"]

return [
flag.strip()
for flag in history_flags.split(";")
]
return [flag.strip() for flag in history_flags.split(";")]
Loading
Loading