| import gradio as gr |
| import pandas as pd |
| import re |
| from datetime import datetime |
| from typing import Dict, List, Optional |
| import json |
|
|
| class ACHTransactionParser: |
| def __init__(self): |
| |
| self.patterns = { |
| 'orig_co_name': r'ORIG CO NAME:\s*([^:]+?)(?=\s+ORIG ID:|$)', |
| 'orig_id': r'ORIG ID:\s*([^\s]+)', |
| 'desc_date': r'DESC DATE:\s*([^\s]+)', |
| 'co_entry_descr': r'CO ENTRY DESCR:\s*([^\s]+)', |
| 'sec_code': r'SEC:\s*([^\s]+)', |
| 'trace_number': r'TRACE#:\s*([^\s]+)', |
| 'effective_date': r'EED:\s*([^\s]+)', |
| 'ind_id': r'IND ID:\s*([^:]*?)(?=\s+IND NAME:|$)', |
| 'ind_name': r'IND NAME:\s*([^:]+?)(?=\s+TRN:|$)', |
| 'transaction_ref': r'TRN:\s*([^\s]+)' |
| } |
| |
| def parse_transaction(self, transaction_text: str) -> Dict[str, str]: |
| """Parse a single transaction string and extract structured data""" |
| transaction_data = {} |
| |
| for field, pattern in self.patterns.items(): |
| match = re.search(pattern, transaction_text, re.IGNORECASE) |
| if match: |
| transaction_data[field] = match.group(1).strip() |
| else: |
| transaction_data[field] = None |
| |
| |
| transaction_data = self._clean_data(transaction_data) |
| |
| return transaction_data |
| |
| def _clean_data(self, data: Dict[str, str]) -> Dict[str, str]: |
| """Clean and format the extracted data""" |
| |
| if data.get('effective_date'): |
| try: |
| date_str = data['effective_date'] |
| if len(date_str) == 6: |
| year = '20' + date_str[:2] |
| month = date_str[2:4] |
| day = date_str[4:6] |
| formatted_date = f"{year}-{month}-{day}" |
| data['effective_date_formatted'] = formatted_date |
| except: |
| data['effective_date_formatted'] = data['effective_date'] |
| |
| |
| for field in ['orig_co_name', 'ind_name']: |
| if data.get(field): |
| data[field] = data[field].strip() |
| |
| return data |
| |
| def parse_multiple_transactions(self, text: str) -> List[Dict[str, str]]: |
| """Parse multiple transactions from a text block""" |
| transactions = [] |
| |
| |
| lines = text.strip().split('\n') |
| |
| for line in lines: |
| line = line.strip() |
| if line and ('ORIG CO NAME' in line or 'TRACE#' in line): |
| parsed = self.parse_transaction(line) |
| if any(parsed.values()): |
| transactions.append(parsed) |
| |
| return transactions |
|
|
| |
| parser = ACHTransactionParser() |
|
|
| def process_transactions(input_text): |
| """Process the input text and return formatted results""" |
| if not input_text.strip(): |
| return "Please paste your ACH transaction data above.", "", "" |
| |
| try: |
| |
| transactions = parser.parse_multiple_transactions(input_text) |
| |
| if not transactions: |
| return "No valid transactions found. Please check your input format.", "", "" |
| |
| |
| formatted_output = [] |
| csv_data = [] |
| |
| for i, trans in enumerate(transactions): |
| formatted_output.append(f"=== Transaction {i+1} ===") |
| for key, value in trans.items(): |
| if value: |
| display_key = key.replace('_', ' ').title() |
| formatted_output.append(f"{display_key}: {value}") |
| formatted_output.append("") |
| |
| |
| csv_row = [ |
| trans.get('effective_date_formatted', ''), |
| trans.get('orig_co_name', ''), |
| trans.get('ind_name', ''), |
| trans.get('co_entry_descr', ''), |
| trans.get('trace_number', ''), |
| trans.get('sec_code', ''), |
| trans.get('orig_id', ''), |
| trans.get('transaction_ref', '') |
| ] |
| csv_data.append(csv_row) |
| |
| |
| df = pd.DataFrame(csv_data, columns=[ |
| 'Date', 'Originating Company', 'Individual Name', |
| 'Description', 'Trace Number', 'SEC Code', |
| 'Originating ID', 'Transaction Ref' |
| ]) |
| |
| |
| summary = f""" |
| π PARSING SUMMARY |
| ================== |
| Total Transactions Processed: {len(transactions)} |
| Date Range: {df['Date'].min() if not df['Date'].empty else 'N/A'} to {df['Date'].max() if not df['Date'].empty else 'N/A'} |
| SEC Codes Found: {', '.join(df['SEC Code'].dropna().unique()) if not df['SEC Code'].empty else 'N/A'} |
| """ |
| |
| return "\n".join(formatted_output), df, summary |
| |
| except Exception as e: |
| return f"Error processing transactions: {str(e)}", "", "" |
|
|
| def clear_inputs(): |
| """Clear all inputs and outputs""" |
| return "", "Ready to process new transactions...", "", "" |
|
|
| |
| sample_data = """ORIG CO NAME:SUNDAY ADE CHAND ORIG ID:BNIAIDJA DESC DATE: CO ENTRY DESCR:WIRE2ACH SEC:PPD TRACE#:021000029427563 EED:250804 IND ID: IND NAME:INTELLIGENCE SCIENCE A TRN: 2169427563TC |
| ORIG CO NAME:TECH SOLUTIONS INC ORIG ID:TECHSOL1 DESC DATE: CO ENTRY DESCR:PAYROLL SEC:PPD TRACE#:021000029427564 EED:250805 IND ID:EMP001 IND NAME:JOHN DOE TRN: 2169427564TC""" |
|
|
| |
| with gr.Blocks( |
| title="ACH Transaction Parser", |
| theme=gr.themes.Soft(), |
| css=""" |
| .main-header { |
| text-align: center; |
| background: linear-gradient(90deg, #1f2937, #3b82f6); |
| color: white; |
| padding: 20px; |
| border-radius: 10px; |
| margin-bottom: 20px; |
| } |
| .info-box { |
| background-color: #f0f9ff; |
| border: 1px solid #0ea5e9; |
| border-radius: 8px; |
| padding: 15px; |
| margin: 10px 0; |
| } |
| """ |
| ) as demo: |
| |
| |
| gr.HTML(""" |
| <div class="main-header"> |
| <h1>π¦ ACH Transaction Parser Dashboard</h1> |
| <p>Parse and analyze ACH transaction data with ease</p> |
| </div> |
| """) |
| |
| |
| gr.HTML(""" |
| <div class="info-box"> |
| <h3>π How to Use:</h3> |
| <ol> |
| <li>Copy your ACH transaction data from your bank or payment system</li> |
| <li>Paste it in the input box below</li> |
| <li>Click "Parse Transactions" to process the data</li> |
| <li>View the formatted results in the output sections</li> |
| </ol> |
| <p><strong>Supported Format:</strong> ORIG CO NAME:... ORIG ID:... DESC DATE:... etc.</p> |
| </div> |
| """) |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| |
| gr.HTML("<h3>π₯ Input Section</h3>") |
| |
| input_text = gr.Textbox( |
| label="Paste ACH Transaction Data Here", |
| placeholder="Paste your transaction data here...\n\nExample:\nORIG CO NAME:SUNDAY ADE CHAND ORIG ID:BNIAIDJA...", |
| lines=8, |
| max_lines=15 |
| ) |
| |
| with gr.Row(): |
| parse_btn = gr.Button("π Parse Transactions", variant="primary", size="lg") |
| clear_btn = gr.Button("ποΈ Clear All", variant="secondary") |
| sample_btn = gr.Button("π Load Sample Data", variant="secondary") |
| |
| |
| gr.HTML("<h3>π€ Output Sections</h3>") |
| |
| with gr.Row(): |
| with gr.Column(scale=1): |
| summary_output = gr.Textbox( |
| label="π Summary", |
| lines=6, |
| interactive=False |
| ) |
| |
| with gr.Column(scale=1): |
| formatted_output = gr.Textbox( |
| label="π Parsed Transaction Details", |
| lines=15, |
| max_lines=25, |
| interactive=False |
| ) |
| |
| |
| with gr.Row(): |
| data_table = gr.Dataframe( |
| label="π Transaction Table", |
| interactive=False, |
| wrap=True |
| ) |
| |
| |
| parse_btn.click( |
| fn=process_transactions, |
| inputs=[input_text], |
| outputs=[formatted_output, data_table, summary_output] |
| ) |
| |
| clear_btn.click( |
| fn=clear_inputs, |
| outputs=[input_text, formatted_output, data_table, summary_output] |
| ) |
| |
| sample_btn.click( |
| fn=lambda: sample_data, |
| outputs=[input_text] |
| ) |
| |
| |
| gr.HTML(""" |
| <div style="text-align: center; margin-top: 30px; padding: 20px; background-color: #f8fafc; border-radius: 10px;"> |
| <h4>π Features</h4> |
| <p>β
Parse multiple transactions at once | β
Clean and format data | β
Export-ready table view | β
Summary statistics</p> |
| <p><em>Built with Gradio for easy deployment and sharing</em></p> |
| </div> |
| """) |
|
|
| |
| if __name__ == "__main__": |
| demo.launch( |
| server_name="0.0.0.0", |
| server_port=7860, |
| share=True, |
| debug=True |
| ) |