Files
doczyai-pipelines/fieldExtraction/src/batch_tracking.py
T
Alex Galarce 47a01e13f0 Merged in chore/contract_effective_date (pull request #307)
New Prompt Method (starting with contract effective date)

* add todos for Siddhant

* moved effective date prompts to prompts.py and added docstring and type hints

* moved effective date prompts to prompts.py and added docstring and type hints

* fixed conflict

Co-authored-by: Alex Galarce <agalarce@aarete.com>

* modified contract effective date logic in existing smart chunking

* Merged main into chore/contract_effective_date

* updated extract_text_from_delimiters function and unit tests for it

* Merge remote-tracking branch 'origin/main' into chore/contract_effective_date

* added comments to preprocessing_funcs

* preprocessing_funcs.py edited online with Bitbucket
* utils.py edited online with Bitbucket
* add debug line

* add NA handling into fix_date_formatting()

* add debug print statements

* worked on review comments

* fixed date checking

* fixed date checking and added unit tests

* Merged main into chore/contract_effective_date

* added logic to handle case when smart chunking returns empty context for effective date

* added comments to file_processing.py

* optimized imports and removed a TODO

* Merged main into chore/contract_effective_date

* made prompt changes for effective date

* keywords.py edited online with Bitbucket
* Removed excess print statements


Approved-by: Katon Minhas
2024-12-05 22:46:41 +00:00

268 lines
10 KiB
Python

import pandas as pd
import boto3
import csv
from datetime import datetime
from pathlib import Path
import subprocess
import config
import os
class BatchTracker:
def __init__(self):
self.s3_client = boto3.client('s3')
self.batch_tracker_bucket = config.S3_OUTPUT_BUCKET
self.batch_tracker_key = 'batch-tracker/doczyai-batch-tracker.csv'
self.current_batch_start = datetime.now()
self.files_processed = 0
self.processed_files = set() # Track unique files
self.processing_events = 0 # Track total processing events
self.total_input_files = 0
self.reimbursement_files = 0
# Define the file log fields in correct order
self.file_log_fields = [
'filename',
'process_type',
'start_time',
'end_time',
'processing_time_seconds',
'delta_time_hours',
'status',
'error',
'memory_usage_mb',
'cpu_percent'
]
def set_input_file_counts(self, total_files, reimbursement_files):
"""Set the input file counts for tracking"""
self.total_input_files = total_files
self.reimbursement_files = reimbursement_files
def get_git_info(self):
"""Get current git branch, commit hash, and metadata"""
try:
# Get current branch
branch = subprocess.check_output(
['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
universal_newlines=True
).strip()
# Get current commit hash
commit = subprocess.check_output(
['git', 'rev-parse', 'HEAD'],
universal_newlines=True
).strip()
# Get commit message
commit_msg = subprocess.check_output(
['git', 'log', '-1', '--pretty=%B'],
universal_newlines=True
).strip()
# Get last commit date
commit_date = subprocess.check_output(
['git', 'log', '-1', '--format=%cd'],
universal_newlines=True
).strip()
return {
'branch': branch,
'commit_hash': commit,
'commit_message': commit_msg,
'commit_date': commit_date
}
except Exception as e:
return {
'branch': 'Unknown',
'commit_hash': 'Unknown',
'commit_message': 'Unknown',
'commit_date': 'Unknown'
}
def get_batch_tracker(self):
"""Retrieve or create the batch tracker DataFrame"""
try:
response = self.s3_client.get_object(
Bucket=self.batch_tracker_bucket,
Key=self.batch_tracker_key
)
return pd.read_csv(response['Body'])
except Exception as e:
print(f"Creating new batch tracker: {str(e)}")
return pd.DataFrame(columns=[
'batch_id', 'state', 'run_mode', 'read_mode', 'input_dir',
'output_dir', 'fields', 'max_workers',
'start_time', 'end_time', 'total_input_files', 'reimbursement_files',
'files_processed', 'contracts_per_hour', 'status', 'machine_info',
'git_branch', 'git_commit', 'git_commit_message',
'git_commit_date'
])
def get_machine_info(self):
"""Get information about the machine running the process"""
import platform
import socket
return {
'hostname': socket.gethostname(),
'platform': platform.platform(),
'processor': platform.processor(),
'python_version': platform.python_version()
}
def add_batch_run(self):
"""Add new batch run to the tracker"""
df = self.get_batch_tracker()
machine_info = self.get_machine_info()
git_info = self.get_git_info()
new_row = {
'batch_id': config.BATCH_ID,
'state': config.STATE,
'run_mode': config.RUN_MODE,
'read_mode': config.READ_MODE,
'input_dir': config.LOCAL_PATH,
'output_dir': config.OUTPUT_DIRECTORY,
'fields': config.FIELDS,
'max_workers': config.MAX_WORKERS,
'start_time': self.current_batch_start.strftime('%Y-%m-%d %H:%M:%S'),
'end_time': None,
'total_input_files': self.total_input_files,
'reimbursement_files': self.reimbursement_files,
'files_processed': 0,
'contracts_per_hour': 0,
'status': 'Running',
'machine_info': str(machine_info),
'git_branch': git_info['branch'],
'git_commit': git_info['commit_hash'],
'git_commit_message': git_info['commit_message'],
'git_commit_date': git_info['commit_date']
}
df = pd.concat([df, pd.DataFrame([new_row])], ignore_index=True)
self.update_tracker(df)
# Initialize the file log with headers if it doesn't exist
if not os.path.exists(config.FILE_LOG_NAME):
with open(config.FILE_LOG_NAME, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=self.file_log_fields)
writer.writeheader()
def update_batch_run(self, end_time=None):
"""Update the current batch run stats"""
df = self.get_batch_tracker()
current_batch_entries = df[df['batch_id'] == config.BATCH_ID]
if current_batch_entries.empty:
self.add_batch_run()
return
latest_idx = current_batch_entries.index[-1]
if end_time:
df.loc[latest_idx, 'end_time'] = end_time.strftime('%Y-%m-%d %H:%M:%S')
df.loc[latest_idx, 'status'] = 'Completed'
df.loc[latest_idx, 'files_processed'] = len(self.processed_files) # Use unique files count
try:
start_time = datetime.strptime(df.loc[latest_idx, 'start_time'], '%Y-%m-%d %H:%M:%S')
current_time = end_time if end_time else datetime.now()
hours_elapsed = (current_time - start_time).total_seconds() / 3600
if hours_elapsed > 0:
contracts_per_hour = len(self.processed_files) / hours_elapsed # Use unique files count
df.loc[latest_idx, 'contracts_per_hour'] = round(contracts_per_hour, 2)
except Exception as e:
print(f"Error calculating processing speed: {str(e)}")
self.update_tracker(df)
if end_time and len(self.processed_files) > 0:
try:
print(f"\nBatch Performance Metrics:")
print(f"Unique files processed: {len(self.processed_files)}")
print(f"Total processing events: {self.processing_events}")
print(f"Total time elapsed: {round(hours_elapsed, 2)} hours")
print(f"Average processing speed: {round(contracts_per_hour, 2)} unique files/hour")
except:
print("Could not calculate final performance metrics")
def update_tracker(self, df):
"""Save tracker to S3"""
try:
csv_buffer = df.to_csv(index=False).encode()
self.s3_client.put_object(
Bucket=self.batch_tracker_bucket,
Key=self.batch_tracker_key,
Body=csv_buffer
)
df.to_csv('doczyai-batch-tracker-local.csv', index=False)
except Exception as e:
print(f"Error updating tracker in S3: {str(e)}")
df.to_csv('doczyai-batch-tracker-local.csv', index=False)
def update_file_progress(self, filename, process_type, start_time, end_time, status='Completed', error=None):
"""Update progress for individual file processing"""
try:
processing_time = (end_time - start_time).total_seconds()
delta_time_hours = processing_time / 3600 # Convert to hours
system_metrics = self.get_system_metrics()
new_row = {
'filename': filename,
'process_type': process_type,
'start_time': start_time.strftime('%Y-%m-%d %H:%M:%S'),
'end_time': end_time.strftime('%Y-%m-%d %H:%M:%S'),
'processing_time_seconds': processing_time,
'delta_time_hours': round(delta_time_hours, 4),
'status': status,
'error': error,
'memory_usage_mb': system_metrics['memory_usage_mb'],
'cpu_percent': system_metrics['cpu_percent']
}
# Write to file log
if not os.path.exists(config.FILE_LOG_NAME):
with open(config.FILE_LOG_NAME, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=self.file_log_fields)
writer.writeheader()
with open(config.FILE_LOG_NAME, 'a', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=self.file_log_fields)
writer.writerow(new_row)
# Track both unique files and processing events
if status == 'Completed':
self.processed_files.add(filename) # Add to set of unique files
self.processing_events += 1 # Increment total events
self.files_processed = len(self.processed_files) # Update files_processed to unique count
self.update_batch_run()
except Exception as e:
print(f"Error updating file progress: {str(e)}")
def get_system_metrics(self):
"""Get current system metrics"""
import psutil
try:
process = psutil.Process()
return {
'memory_usage_mb': process.memory_info().rss / 1024 / 1024,
'cpu_percent': process.cpu_percent()
}
except:
return {
'memory_usage_mb': 0,
'cpu_percent': 0
}