AgenticBA / app.py
MrInvincible96's picture
Update app.py
0086807 verified
import re
import gradio as gr
CUSTOM_CSS = """
body {
background: linear-gradient(180deg, #f4f6f8 0%, #edf1f5 100%);
}
.gradio-container {
max-width: 1280px !important;
padding-top: 20px !important;
padding-bottom: 36px !important;
font-family: "Aptos", "Segoe UI", "Helvetica Neue", sans-serif !important;
color: #1c2733;
}
.hero-shell {
background: linear-gradient(135deg, #11283f 0%, #1a3d5f 100%);
border-radius: 10px;
overflow: hidden;
box-shadow: 0 18px 40px rgba(18, 36, 53, 0.16);
margin-bottom: 18px;
border: 1px solid rgba(255, 255, 255, 0.08);
}
.hero-inner {
padding: 30px 34px;
color: #ffffff;
}
.eyebrow {
display: inline-block;
font-size: 0.78rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.08em;
color: #ffffff !important;
margin-bottom: 12px;
}
.hero-title {
margin: 0 0 12px 0;
font-size: 2.5rem;
line-height: 1.08;
font-weight: 800;
letter-spacing: -0.02em;
color: #ffffff !important;
opacity: 1 !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
}
.hero-subtitle {
max-width: 860px;
margin: 0;
font-size: 1.02rem;
line-height: 1.75;
color: #ffffff !important;
}
.hero-bar {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 1px;
margin-top: 22px;
background: rgba(255, 255, 255, 0.12);
border-radius: 8px;
overflow: hidden;
}
.hero-stat {
background: rgba(255, 255, 255, 0.08);
padding: 14px 16px;
}
.hero-stat strong {
display: block;
font-size: 0.92rem;
margin-bottom: 4px;
color: #ffffff !important;
}
.hero-stat span {
font-size: 0.84rem;
color: #ffffff !important;
line-height: 1.45;
}
.panel {
background: #ffffff;
border: 1px solid #d7dde5;
border-radius: 10px;
box-shadow: 0 8px 20px rgba(31, 47, 61, 0.06);
overflow: hidden;
}
.panel-head {
padding: 20px 22px 14px 22px;
border-bottom: 1px solid #e3e8ee;
background: linear-gradient(180deg, #fbfcfd 0%, #f5f8fb 100%);
}
.panel-title {
margin: 0 0 6px 0;
font-size: 1.2rem;
font-weight: 800;
color: #17324a;
}
.panel-copy {
margin: 0;
color: #56697a;
font-size: 0.95rem;
line-height: 1.6;
}
.panel-body {
padding: 20px 22px 22px 22px;
}
.metric-strip {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
margin-bottom: 16px;
}
.metric-card {
background: #f8fafc;
border: 1px solid #dfe5eb;
border-left: 4px solid #244e73;
border-radius: 8px;
padding: 14px 14px 12px 14px;
}
.metric-card strong {
display: block;
color: #173954;
font-size: 1rem;
margin-bottom: 4px;
}
.metric-card span {
color: #607180;
font-size: 0.88rem;
line-height: 1.45;
}
/* Base Textbox & Textarea styling */
textarea, [data-testid="textbox"] textarea {
background: #ffffff !important;
border: 1px solid #ccd5de !important;
border-radius: 8px !important;
font-size: 0.98rem !important;
color: #27272A !important;
box-shadow: none !important;
}
textarea::placeholder, [data-testid="textbox"] textarea::placeholder {
color: #7a8896 !important;
}
textarea:focus, [data-testid="textbox"] textarea:focus {
border: 1px solid #244e73 !important;
box-shadow: 0 0 0 3px rgba(36, 78, 115, 0.12) !important;
}
/* Button styling */
button.primary {
background: #173a59 !important;
color: #ffffff !important;
border: 1px solid #173a59 !important;
border-radius: 8px !important;
min-height: 46px !important;
font-weight: 700 !important;
transition: all 0.2s ease !important;
}
button.primary:hover {
background: #214d75 !important;
border-color: #214d75 !important;
color: #ffffff !important;
}
button.secondary {
background: #ffffff !important;
color: #173954 !important;
border: 1px solid #ccd5de !important;
border-radius: 8px !important;
min-height: 46px !important;
font-weight: 700 !important;
transition: all 0.2s ease !important;
}
button.secondary:hover {
background: #eef4f8 !important;
color: #16324a !important;
border-color: #b8c7d4 !important;
}
/* Output Prose styling */
.prose, .prose * {
color: #1e2f3d !important;
line-height: 1.72 !important;
}
.prose h1, .prose h2, .prose h3 {
color: #16324a !important;
font-weight: 800 !important;
}
/* Footer Banner */
.footer-banner {
margin-top: 16px;
background: #ffffff;
border: 1px solid #d8dfe6;
border-radius: 10px;
padding: 16px 20px;
color: #4d6272;
font-size: 0.94rem;
line-height: 1.65;
box-shadow: 0 8px 18px rgba(31, 47, 61, 0.05);
}
.footer-banner strong {
color: #16324a;
}
/* Keep hero stats white no matter what */
.hero-shell *,
.hero-inner *,
.hero-subtitle,
.eyebrow,
.hero-stat strong,
.hero-stat span {
color: #ffffff !important;
}
@media (max-width: 900px) {
.hero-title {
font-size: 2rem;
}
.hero-bar,
.metric-strip {
grid-template-columns: 1fr;
}
}
/* =========================================
SLEDGEHAMMER OVERRIDES FOR GRADIO BUGS
========================================= */
/* 1. HERO TITLE FIX (Bypasses Gradio's internal h1 color reset) */
.gradio-container .hero-shell h1.hero-title,
.gradio-container .hero-shell h1,
.hero-shell .hero-title {
color: #ffffff !important;
-webkit-text-fill-color: #ffffff !important; /* Forces white even if webkit overrides it */
opacity: 1 !important;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.18) !important;
margin: 0 0 12px 0 !important;
font-size: 2.5rem !important;
font-weight: 800 !important;
}
/* 2. TABS VISIBILITY FIX */
div.tab-nav {
gap: 8px !important;
padding: 14px 22px 0 22px !important;
background: #ffffff !important;
}
button[role="tab"] {
color: #41586b !important; /* Forces unselected text to be dark grey */
border-radius: 999px !important;
border: 1px solid #d5dce4 !important;
background: #f7f9fb !important;
font-weight: 700 !important;
transition: all 0.2s ease !important;
}
button[role="tab"]:hover {
background: #edf3f8 !important;
color: #173954 !important;
border-color: #bfd0dd !important;
}
button[role="tab"].selected {
background: #173a59 !important; /* Force background on active */
color: #ffffff !important; /* Force white text on active */
border-color: #173a59 !important;
}
/* 3. EXAMPLES LABEL ("Sample Business Scenarios") FIX */
.examples > div:first-child,
.examples > div:first-child *,
.examples .label-wrap,
.examples .label-wrap *,
div.examples label,
div.examples p,
div.examples span {
color: #000000 !important;
font-weight: 700 !important;
opacity: 1 !important;
}
/* 4. EXAMPLES BUTTONS & HOVER STATE FIX */
.examples button,
.examples [role="button"],
.examples .gallery-item {
background-color: #ffffff !important;
color: #000000 !important;
border: 1px solid #ccd5de !important;
}
/* Force text inside the un-hovered buttons to stay black */
.examples button *,
.examples [role="button"] *,
.examples .gallery-item * {
color: #000000 !important;
}
/* Hover and Selected states for Examples */
.examples button:hover,
.examples [role="button"]:hover,
.examples .gallery-item:hover,
.examples button.selected,
.examples [aria-selected="true"] {
background-color: #173a59 !important; /* Dark blue background so white text pops */
color: #ffffff !important; /* White text */
border-color: #173a59 !important;
}
/* Force text inside the hovered/selected buttons to turn white */
.examples button:hover *,
.examples [role="button"]:hover *,
.examples .gallery-item:hover *,
.examples button.selected *,
.examples [aria-selected="true"] * {
color: #ffffff !important;
}
"""
def guess_title(notes):
text = notes.lower()
if "checkout" in text or "cart" in text or "payment" in text:
return "Checkout Experience Improvement"
if "hospital" in text or "patient" in text or "reception" in text:
return "Patient Registration Process Improvement"
if "login" in text or "signup" in text or "authentication" in text:
return "Login and Access Experience Improvement"
if "dashboard" in text:
return "Operational Dashboard Enhancement"
return "Business Process Improvement Initiative"
def extract_payment_methods(text):
methods = []
options = ["upi", "cards", "wallets", "net banking", "cod", "cash on delivery"]
lowered = text.lower()
for item in options:
if item in lowered:
methods.append(item.upper() if item == "upi" else item.title())
return methods
def build_objectives(text):
objectives = []
lowered = text.lower()
if "cart" in lowered or "checkout" in lowered:
objectives.append("Reduce checkout drop-off and improve completion rate")
if "mobile" in lowered:
objectives.append("Improve the experience for mobile users")
if "support" in lowered:
objectives.append("Reduce support tickets related to the process")
if "faster" in lowered or "slow" in lowered:
objectives.append("Reduce time required to complete the process")
if "payment" in lowered:
objectives.append("Make payment completion simpler and more flexible")
if "dashboard" in lowered:
objectives.append("Provide clearer visibility for business users")
if not objectives:
objectives = [
"Improve process efficiency",
"Increase consistency in user experience",
"Reduce manual effort"
]
return objectives[:4]
def build_scope(text):
scope = []
lowered = text.lower()
if "checkout" in lowered or "cart" in lowered:
scope.append("Covers the end-to-end checkout journey")
if "payment" in lowered:
scope.append("Includes payment option selection and payment completion flow")
if "guest checkout" in lowered or "guest" in lowered:
scope.append("Includes guest user purchase flow")
if "mobile" in lowered or "tablet" in lowered:
scope.append("Includes responsive experience on mobile and tablet devices")
if "dashboard" in lowered:
scope.append("Includes dashboard access for internal users")
if not scope:
scope = [
"Covers the target business process described in the stakeholder notes",
"Includes the main user interactions and supporting business workflow"
]
return scope
def build_functional_requirements(text):
reqs = []
lowered = text.lower()
payments = extract_payment_methods(text)
if "guest checkout" in lowered or "guest" in lowered:
reqs.append("The system should allow users to continue without mandatory account login")
if payments:
reqs.append(f"The system should support the following payment methods: {', '.join(payments)}")
if "mobile" in lowered:
reqs.append("The system should provide a mobile-friendly experience")
if "tablet" in lowered:
reqs.append("The system should work effectively on tablet devices")
if "otp" in lowered:
reqs.append("The system should support OTP-based verification")
if "dashboard" in lowered:
reqs.append("The system should provide a dashboard for business users")
if "login" in lowered and "optional" in lowered:
reqs.append("The system should make login optional where appropriate")
if "faster" in lowered or "slow" in lowered:
reqs.append("The system should reduce unnecessary user steps in the process")
if not reqs:
reqs = [
"The system should support the core business process described by stakeholders",
"The system should simplify the current user journey",
"The system should improve consistency across the workflow"
]
return reqs[:6]
def build_non_functional_requirements(text):
reqs = [
"The solution should be easy to use for target users",
"The solution should be reliable during normal business usage"
]
lowered = text.lower()
if "mobile" in lowered or "tablet" in lowered:
reqs.append("The interface should be responsive across supported screen sizes")
if "payment" in lowered or "otp" in lowered or "login" in lowered:
reqs.append("The solution should protect user and transaction data")
if "faster" in lowered or "slow" in lowered:
reqs.append("The process should complete within an acceptable response time")
return reqs[:4]
def build_assumptions(text):
assumptions = [
"Stakeholder notes represent the primary business need for the first version",
"Existing systems can be updated without a full platform replacement"
]
lowered = text.lower()
if "launch" in lowered or "month" in lowered:
assumptions.append("Delivery timeline expectations are realistic and approved by stakeholders")
if "payment" in lowered:
assumptions.append("Payment integrations can be configured within the current technical landscape")
return assumptions[:4]
def build_risks(text):
risks = [
"Incomplete stakeholder input may lead to missing requirements",
"Tight delivery timelines may reduce testing time"
]
lowered = text.lower()
if "payment" in lowered:
risks.append("Payment integration complexity may delay release")
if "guest" in lowered:
risks.append("Guest user journeys may create reporting or tracking challenges")
if "mobile" in lowered or "tablet" in lowered:
risks.append("Cross-device usability issues may affect adoption")
return risks[:4]
def build_success_metrics(text):
metrics = [
"Reduced time spent preparing business documentation",
"Improved consistency of requirement outputs"
]
lowered = text.lower()
if "cart" in lowered or "checkout" in lowered:
metrics.append("Higher checkout completion rate")
if "support" in lowered:
metrics.append("Lower support ticket volume")
if "mobile" in lowered:
metrics.append("Improved mobile task completion rate")
return metrics[:4]
def build_user_stories(text):
lowered = text.lower()
stories = []
if "guest checkout" in lowered or "guest" in lowered:
stories.append({
"role": "customer",
"goal": "complete checkout without creating an account",
"benefit": "I can purchase faster",
"given": "I have items in my cart",
"when": "I proceed to checkout",
"then": "I should be able to continue as a guest"
})
if "payment" in lowered or extract_payment_methods(text):
methods = extract_payment_methods(text)
method_text = ", ".join(methods) if methods else "multiple payment options"
stories.append({
"role": "customer",
"goal": f"choose from {method_text}",
"benefit": "I can use my preferred payment method",
"given": "I am on the payment step",
"when": "I review the payment options",
"then": f"I should see {method_text}"
})
if "mobile" in lowered or "tablet" in lowered:
device = "mobile device" if "mobile" in lowered else "tablet"
stories.append({
"role": "user",
"goal": f"complete the journey smoothly on my {device}",
"benefit": "I can finish the task without frustration",
"given": f"I am using a {device}",
"when": "I open the process flow",
"then": "the experience should be clear and easy to use"
})
if "dashboard" in lowered:
stories.append({
"role": "business user",
"goal": "view information in a dashboard",
"benefit": "I can manage work more efficiently",
"given": "I log into the dashboard",
"when": "I review current records or updates",
"then": "I should be able to access the information I need clearly"
})
if not stories:
stories.append({
"role": "end user",
"goal": "complete the main process with fewer steps",
"benefit": "I can save time and effort",
"given": "I start the process",
"when": "I move through the required steps",
"then": "the journey should feel simple and efficient"
})
stories.append({
"role": "business stakeholder",
"goal": "receive clearer requirement documentation",
"benefit": "delivery can be more consistent",
"given": "stakeholder notes are captured",
"when": "documentation is generated",
"then": "the output should be structured and easy to review"
})
return stories[:5]
def format_brd(notes):
title = guess_title(notes)
objectives = build_objectives(notes)
scope = build_scope(notes)
functional = build_functional_requirements(notes)
non_functional = build_non_functional_requirements(notes)
assumptions = build_assumptions(notes)
risks = build_risks(notes)
metrics = build_success_metrics(notes)
brd = f"""# Business Requirements Document
## Project Title
{title}
## Executive Summary
This document translates unstructured stakeholder notes into a cleaner, review-ready business requirements format. It is designed to help Business Analysts move faster from conversation capture to formal documentation.
## Business Problem
Stakeholder feedback suggests that the current process contains friction, inconsistency, or avoidable manual effort. A more structured solution is needed to improve user outcomes and business performance.
## Objectives
""" + "\n".join([f"- {item}" for item in objectives]) + """
## Scope
""" + "\n".join([f"- {item}" for item in scope]) + """
## Functional Requirements
""" + "\n".join([f"- {item}" for item in functional]) + """
## Non-Functional Requirements
""" + "\n".join([f"- {item}" for item in non_functional]) + """
## Assumptions
""" + "\n".join([f"- {item}" for item in assumptions]) + """
## Risks
""" + "\n".join([f"- {item}" for item in risks]) + """
## Success Metrics
""" + "\n".join([f"- {item}" for item in metrics])
return brd
def format_stories(notes):
stories = build_user_stories(notes)
sections = ["# BDD-Style User Stories"]
for index, story in enumerate(stories, start=1):
sections.append(
f"""
## User Story {index}
As a {story['role']},
I want to {story['goal']},
So that {story['benefit']}.
### Acceptance Criteria
- Given {story['given']}
- When {story['when']}
- Then {story['then']}
""".strip()
)
return "\n\n".join(sections)
def format_before_after(notes):
cleaned = notes.strip()
bullet_lines = [line.strip(" -•\t") for line in cleaned.splitlines() if line.strip()]
if not bullet_lines:
bullet_lines = [part.strip() for part in re.split(r"[.;]\s*", cleaned) if part.strip()]
before = "\n".join([f"- {line}" for line in bullet_lines[:8]])
return f"""# Before vs After
## Before: Raw Stakeholder Notes
{before}
## After: Business-Ready Output
- Raw notes were grouped into a formal BRD structure
- Requirements were separated into functional and non-functional areas
- User needs were rewritten as BDD-style stories
- The output is now easier to review, share, and present
## Portfolio Value
This demonstrates BA lifecycle thinking, requirement structuring, and workflow automation in a recruiter-friendly format.
"""
def generate_documents(notes):
notes = notes.strip()
if not notes:
msg = "Please paste stakeholder notes first."
return msg, msg, msg
brd = format_brd(notes)
stories = format_stories(notes)
before_after = format_before_after(notes)
return brd, stories, before_after
with gr.Blocks(css=CUSTOM_CSS, theme=gr.themes.Base()) as demo:
gr.HTML("""
<div class="hero-shell">
<div class="hero-inner">
<div class="eyebrow">Strategy • Requirements • Workflow Automation</div>
<h1 class="hero-title">Agentic BA</h1>
<p class="hero-subtitle">
A Business Analyst portfolio solution that converts unstructured stakeholder notes into
structured BRDs, BDD-style user stories, and a clear before-versus-after documentation view.
</p>
<div class="hero-bar">
<div class="hero-stat">
<strong>Structured Documentation</strong>
<span>Converts rough notes into formal requirement outputs.</span>
</div>
<div class="hero-stat">
<strong>Public Portfolio Demo</strong>
<span>Built to share with recruiters and hiring teams.</span>
</div>
<div class="hero-stat">
<strong>100% Free Stack</strong>
<span>No paid APIs, subscriptions, or hidden usage costs.</span>
</div>
<div class="hero-stat">
<strong>BA Lifecycle Focus</strong>
<span>Highlights analysis, structuring, and requirement thinking.</span>
</div>
</div>
</div>
</div>
""")
with gr.Row(equal_height=True):
with gr.Column(scale=5):
gr.HTML("""
<div class="panel">
<div class="panel-head">
<h2 class="panel-title">Input Workspace</h2>
<p class="panel-copy">
Provide stakeholder notes, workshop summaries, or interview observations.
The application reorganizes them into clearer business deliverables.
</p>
</div>
<div class="panel-body">
<div class="metric-strip">
<div class="metric-card">
<strong>BRD Output</strong>
<span>Formal business requirement structure</span>
</div>
<div class="metric-card">
<strong>BDD Stories</strong>
<span>User-centered requirement framing</span>
</div>
<div class="metric-card">
<strong>Showcase View</strong>
<span>Before-and-after transformation for interviews</span>
</div>
</div>
""")
notes_input = gr.Textbox(
lines=16,
label="Raw Stakeholder Notes",
placeholder="Example: Customers are abandoning carts before payment. Stakeholders want easier checkout, UPI, cards, wallets, guest checkout, mobile-friendly flow, and fewer support issues."
)
with gr.Row():
generate_btn = gr.Button("Generate Deliverables", variant="primary")
clear_btn = gr.Button("Clear Input", variant="secondary")
gr.Examples(
examples=[
["Customers are abandoning carts before payment. They want easier checkout, UPI, cards, wallets, mobile-friendly pages, guest checkout, and fewer support issues."],
["Hospital reception staff say patient registration is slow. They want OTP verification, fewer manual steps, tablet support, and a simple dashboard for receptionists."],
["Users find login confusing. Stakeholders want optional sign-in where possible, a simpler user flow, faster completion, and fewer helpdesk calls."]
],
inputs=notes_input,
label="Sample Business Scenarios"
)
gr.HTML("</div></div>")
with gr.Column(scale=7):
gr.HTML("""
<div class="panel">
<div class="panel-head">
<h2 class="panel-title">Output Review</h2>
<p class="panel-copy">
Review the generated documentation in a format designed for portfolio presentation
and rapid recruiter understanding.
</p>
</div>
""")
with gr.Tabs():
with gr.Tab("BRD"):
brd_output = gr.Markdown()
with gr.Tab("BDD User Stories"):
stories_output = gr.Markdown()
with gr.Tab("Before vs After"):
before_after_output = gr.Markdown()
gr.HTML("</div>")
gr.HTML("""
<div class="footer-banner">
<strong>Recruiter takeaway:</strong> This project demonstrates how a Business Analyst can apply
structured automation thinking to convert unclear stakeholder input into clearer, more consistent,
and more presentation-ready requirement documentation.
</div>
""")
generate_btn.click(
fn=generate_documents,
inputs=notes_input,
outputs=[brd_output, stories_output, before_after_output]
)
clear_btn.click(
fn=lambda: ("", "", "", ""),
inputs=[],
outputs=[notes_input, brd_output, stories_output, before_after_output]
)
demo.launch()