mousom's picture
Upload folder using huggingface_hub
9847bda verified
raw
history blame
16.4 kB
import gradio as gr
import requests
import pandas as pd
from typing import Dict, List, Optional, Tuple
import json
# Base URL for the API
BASE_URL = "https://gljx3devrf.execute-api.us-west-1.amazonaws.com/prod/"
class ContractPaymentViewer:
def __init__(self):
self.current_contract_data = {}
self.current_monthly_data = {}
def fetch_payment_history(self, contract_number: str) -> Tuple[gr.Dropdown, str, pd.DataFrame]:
"""
Fetch payment history for a given contract number
"""
if not contract_number:
return gr.Dropdown(choices=[], value=None), "Please enter a contract number", pd.DataFrame()
try:
# Clean the contract number
contract_number = contract_number.strip()
# Call the API
url = f"{BASE_URL}/contracts/{contract_number}/payments"
response = requests.get(url, timeout=10)
response.raise_for_status()
data = response.json()
self.current_contract_data = data
# Extract monthly breakdown for dropdown
monthly_breakdown = data.get('monthly_breakdown', {})
if not monthly_breakdown:
return gr.Dropdown(choices=[], value=None), "No payment history found", pd.DataFrame()
# Create choices for dropdown (sorted by date, newest first)
choices = sorted(list(monthly_breakdown.keys()), reverse=True)
# Create summary info
contract_details = data.get('contract_details', {})
summary = data.get('summary', {})
# Clean job description by removing carriage returns
job_description = contract_details.get('job_description', 'N/A').replace('\r', ' ')
summary_info = f"""
### Contract Summary
- **Contract Number:** {contract_details.get('contract_number', contract_number)}
- **Contractor:** {contract_details.get('contractor_name', 'N/A')}
- **Job Description:** {job_description}
- **Total Payments:** {summary.get('total_payments', 0)}
- **Total Disbursed:** ${summary.get('total_disbursed', 0):,.2f}
- **Total Held:** ${summary.get('total_held', 0):,.2f}
- **Estimated Completion:** {contract_details.get('estimated_completion', 'N/A')}
- **Last Updated:** {contract_details.get('last_updated', 'N/A')}
"""
# Create payment history DataFrame
payment_history = data.get('payment_history', [])
if payment_history:
history_df = pd.DataFrame(payment_history)
history_df = history_df[['estimate_number', 'type', 'payment_type', 'release_date', 'disbursed_amount', 'held_amount']]
history_df.columns = ['Estimate #', 'Type', 'Payment Type', 'Release Date', 'Disbursed Amount', 'Held Amount']
history_df['Disbursed Amount'] = history_df['Disbursed Amount'].apply(lambda x: f"${x:,.2f}")
history_df['Held Amount'] = history_df['Held Amount'].apply(lambda x: f"${x:,.2f}")
else:
history_df = pd.DataFrame()
return (
gr.Dropdown(choices=choices, value=choices[0] if choices else None, visible=True, interactive=True),
summary_info,
history_df
)
except requests.exceptions.RequestException as e:
return gr.Dropdown(choices=[], value=None), f"Error fetching data: {str(e)}", pd.DataFrame()
except Exception as e:
return gr.Dropdown(choices=[], value=None), f"Unexpected error: {str(e)}", pd.DataFrame()
def fetch_monthly_details(self, contract_number: str, selected_month: str) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame, pd.DataFrame, str]:
"""
Fetch detailed monthly payment information
"""
if not contract_number or not selected_month:
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), "Please select a month"
try:
# Clean inputs
contract_number = contract_number.strip()
selected_month = selected_month.strip()
# Call the API for monthly details
url = f"{BASE_URL}/contracts/{contract_number}/payments/monthly/{selected_month}"
response = requests.get(url, timeout=10)
response.raise_for_status()
monthly_data = response.json()
self.current_monthly_data = monthly_data
# Process the payment data
payment_info = monthly_data.get('payments', [{}])[0] if monthly_data.get('payments') else {}
# Create monthly summary DataFrame
summary_data = {
'Metric': [
'Month',
'Payment Count',
'Total Disbursed',
'Total Held',
'Estimate Number',
'Payment Type',
'Release Date'
],
'Value': [
monthly_data.get('month', selected_month),
monthly_data.get('payment_count', 0),
f"${monthly_data.get('total_disbursed', 0):,.2f}",
f"${monthly_data.get('total_held', 0):,.2f}",
payment_info.get('estimate_number', 'N/A'),
payment_info.get('payment_type', 'N/A'),
payment_info.get('release_date', 'N/A')
]
}
summary_df = pd.DataFrame(summary_data)
# Create detailed items DataFrame
items_df = pd.DataFrame()
if payment_info and 'detailed_estimate' in payment_info:
detailed_estimate = payment_info['detailed_estimate']
prime_items = detailed_estimate.get('prime_contract', {}).get('items', [])
if prime_items:
# Process items for display
items_data = []
for item in prime_items:
if item.get('amounts', {}).get('this_period', 0) != 0: # Only show items with activity
items_data.append({
'Item #': item.get('item_number', ''),
'Description': item.get('description', '')[:50] + '...' if len(item.get('description', '')) > 50 else item.get('description', ''),
'Unit': item.get('units', ''),
'Unit Price': f"${item.get('unit_price', 0):,.2f}",
'Qty This Period': f"{item.get('quantities', {}).get('this_period', 0):,.2f}",
'Amount This Period': f"${item.get('amounts', {}).get('this_period', 0):,.2f}",
'Job to Date': f"${item.get('amounts', {}).get('job_to_date', 0):,.2f}",
'% Complete': f"{item.get('quantities', {}).get('percent_jtd', 0):.1f}%"
})
if items_data:
items_df = pd.DataFrame(items_data)
# Sort by amount this period (descending)
items_df['sort_value'] = items_df['Amount This Period'].str.replace('$', '').str.replace(',', '').astype(float)
items_df = items_df.sort_values('sort_value', ascending=False).drop('sort_value', axis=1)
else:
# Create empty dataframe with message
items_df = pd.DataFrame({'Message': ['No line items with activity for this period']})
else:
items_df = pd.DataFrame({'Message': ['No line items available']})
else:
items_df = pd.DataFrame({'Message': ['No line items available']})
# Create deductions DataFrame
deductions_df = pd.DataFrame()
if payment_info and 'detailed_estimate' in payment_info:
deductions = payment_info['detailed_estimate'].get('deductions', [])
if deductions:
deductions_data = []
for deduction in deductions:
deductions_data.append({
'Estimate #': deduction.get('estimate_no', ''),
'Description': deduction.get('description', ''),
'This Period': f"${deduction.get('this_period', 0):,.2f}",
'Job to Date': f"${deduction.get('job_to_date', 0):,.2f}"
})
deductions_df = pd.DataFrame(deductions_data)
else:
deductions_df = pd.DataFrame({'Message': ['No deductions for this period']})
else:
deductions_df = pd.DataFrame({'Message': ['No deductions available']})
# Create change orders DataFrame
change_orders_df = pd.DataFrame()
if payment_info and 'detailed_estimate' in payment_info:
change_orders = payment_info['detailed_estimate'].get('change_orders', {}).get('items', [])
if change_orders:
change_orders_data = []
for co in change_orders:
change_orders_data.append({
'Change Order #': co.get('change_order_no', ''),
'Description': co.get('description', '')[:50] + '...' if len(co.get('description', '')) > 50 else co.get('description', ''),
'This Period': f"${co.get('this_period_amt', 0):,.2f}",
'Job to Date': f"${co.get('job_to_date_amt', 0):,.2f}",
'Status': co.get('status', 'N/A')
})
if change_orders_data:
change_orders_df = pd.DataFrame(change_orders_data)
# Sort by amount this period (descending)
change_orders_df['sort_value'] = change_orders_df['This Period'].str.replace('$', '').str.replace(',', '').astype(float)
change_orders_df = change_orders_df.sort_values('sort_value', ascending=False).drop('sort_value', axis=1)
else:
change_orders_df = pd.DataFrame({'Message': ['No change orders for this period']})
else:
change_orders_df = pd.DataFrame({'Message': ['No change orders available']})
else:
change_orders_df = pd.DataFrame({'Message': ['No change orders available']})
# Create totals summary
if payment_info and 'detailed_estimate' in payment_info:
totals = payment_info['detailed_estimate'].get('overall_totals', {})
totals_info = f"""
### Payment Totals for {selected_month}
- **Original Contract Amount:** ${totals.get('original', 0):,.2f}
- **This Period:** ${totals.get('this_period', 0):,.2f}
- **Job to Date:** ${totals.get('job_to_date', 0):,.2f}
- **Extra Work This Estimate:** ${totals.get('extra_work_this_estimate', 0):,.2f}
- **Extra Work to Date:** ${totals.get('extra_work_to_date', 0):,.2f}
"""
else:
totals_info = "No detailed totals available"
return summary_df, items_df, deductions_df, change_orders_df, totals_info
except requests.exceptions.RequestException as e:
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), f"Error fetching monthly details: {str(e)}"
except Exception as e:
return pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), pd.DataFrame(), f"Unexpected error: {str(e)}"
# Create instance of the viewer
viewer = ContractPaymentViewer()
# Create Gradio interface
with gr.Blocks(title="Contract Payment Viewer", theme=gr.themes.Soft()) as app:
gr.Markdown("# Contract Payment Viewer")
gr.Markdown("Enter a contract number to view payment history and detailed monthly breakdowns")
with gr.Row():
with gr.Column(scale=1):
contract_input = gr.Textbox(
label="Contract Number",
placeholder="e.g., 03-3F0704",
value="03-3F0704"
)
fetch_button = gr.Button("Fetch Payment History", variant="primary")
with gr.Row():
with gr.Column():
summary_output = gr.Markdown(label="Contract Summary")
with gr.Row():
with gr.Column(scale=1):
month_dropdown = gr.Dropdown(
label="Select Month",
choices=[],
visible=False
)
fetch_monthly_button = gr.Button("Get Monthly Details", variant="secondary", visible=False)
with gr.Row():
with gr.Column():
with gr.Accordion("Payment History", open=False):
history_table = gr.Dataframe(
label="Payment History",
wrap=True
)
# Monthly details section
with gr.Row(visible=False) as monthly_section:
with gr.Column():
gr.Markdown("## Monthly Payment Details")
with gr.Tabs():
with gr.TabItem("Summary"):
monthly_summary_table = gr.Dataframe(
label="Monthly Summary",
wrap=True
)
totals_output = gr.Markdown(label="Payment Totals")
with gr.TabItem("Line Items"):
items_table = gr.Dataframe(
label="Contract Items (This Period Activity)",
wrap=True
)
with gr.TabItem("Deductions"):
deductions_table = gr.Dataframe(
label="Deductions",
wrap=True
)
with gr.TabItem("Change Orders"):
change_orders_table = gr.Dataframe(
label="Change Orders",
wrap=True
)
# Event handlers
def handle_fetch_history(contract_number):
dropdown, summary, history = viewer.fetch_payment_history(contract_number)
return {
month_dropdown: dropdown,
summary_output: summary,
history_table: history,
fetch_monthly_button: gr.Button(visible=True if dropdown.choices else False),
monthly_section: gr.Row(visible=False)
}
def handle_fetch_monthly(contract_number, selected_month):
summary_df, items_df, deductions_df, change_orders_df, totals = viewer.fetch_monthly_details(contract_number, selected_month)
return {
monthly_summary_table: summary_df,
items_table: items_df,
deductions_table: deductions_df,
change_orders_table: change_orders_df,
totals_output: totals,
monthly_section: gr.Row(visible=True)
}
fetch_button.click(
fn=handle_fetch_history,
inputs=[contract_input],
outputs=[month_dropdown, summary_output, history_table, fetch_monthly_button, monthly_section]
)
fetch_monthly_button.click(
fn=handle_fetch_monthly,
inputs=[contract_input, month_dropdown],
outputs=[monthly_summary_table, items_table, deductions_table, change_orders_table, totals_output, monthly_section]
)
# Auto-fetch monthly details when dropdown changes
month_dropdown.change(
fn=handle_fetch_monthly,
inputs=[contract_input, month_dropdown],
outputs=[monthly_summary_table, items_table, deductions_table, change_orders_table, totals_output, monthly_section]
)
# Launch the app
if __name__ == "__main__":
app.launch(
share=True,
server_name="0.0.0.0",
server_port=7860,
show_error=True,
debug=False,
max_threads=10
)