11import os
22import boto3
3- from flask import Flask , render_template , request , redirect , url_for
43import time
4+ import csv
5+ import io
6+ from flask import Flask , render_template , request , redirect , url_for , jsonify
7+ from dotenv import load_dotenv
8+ from botocore .client import Config
9+
10+ load_dotenv ()
511
612app = Flask (__name__ )
713
8- # AWS Configuration
9- AWS_ACCESS_KEY_ID = os .environ .get ('AWS_ACCESS_KEY_ID' )
10- AWS_SECRET_ACCESS_KEY = os .environ .get ('AWS_SECRET_ACCESS_KEY' )
11- AWS_REGION = os .environ .get ('AWS_REGION' )
14+ # --- Configuration and AWS Clients ---
1215S3_BUCKET = os .environ .get ('S3_BUCKET' )
16+ AWS_REGION = os .environ .get ('AWS_REGION' )
17+ if not all ([S3_BUCKET , AWS_REGION , os .environ .get ('AWS_ACCESS_KEY_ID' ), os .environ .get ('AWS_SECRET_ACCESS_KEY' )]):
18+ raise ValueError ("One or more essential environment variables are missing." )
1319
14- s3 = boto3 .client (
15- 's3' ,
16- aws_access_key_id = AWS_ACCESS_KEY_ID ,
17- aws_secret_access_key = AWS_SECRET_ACCESS_KEY ,
18- region_name = AWS_REGION
20+ # The robust S3 client configuration
21+ s3_config = Config (
22+ signature_version = 's3v4' ,
23+ s3 = {'addressing_style' : 'path' }
1924)
2025
21- textract = boto3 .client (
22- 'textract' ,
23- aws_access_key_id = AWS_ACCESS_KEY_ID ,
24- aws_secret_access_key = AWS_SECRET_ACCESS_KEY ,
25- region_name = AWS_REGION
26+ s3 = boto3 .client (
27+ 's3' ,
28+ region_name = AWS_REGION ,
29+ config = s3_config
2630)
31+ textract = boto3 .client ('textract' , region_name = AWS_REGION )
2732
33+ # --- Routes ---
34+ # ... (all other routes remain the same)
2835@app .route ('/' )
2936def index ():
3037 return render_template ('index.html' )
3138
3239@app .route ('/upload' , methods = ['POST' ])
3340def upload ():
3441 if 'file' not in request .files :
35- return redirect ( request .url )
36-
42+ return "No file part in the request." , 400
43+
3744 file = request .files ['file' ]
38-
3945 if file .filename == '' :
40- return redirect ( request . url )
46+ return "No file selected." , 400
4147
4248 if file :
43- s3 .upload_fileobj (
44- file ,
45- S3_BUCKET ,
46- file .filename ,
47- ExtraArgs = {'ContentType' : file .content_type }
48- )
49-
50- response = textract .start_document_text_detection (
51- DocumentLocation = {
52- 'S3Object' : {
53- 'Bucket' : S3_BUCKET ,
54- 'Name' : file .filename
55- }
56- }
57- )
58-
59- job_id = response ['JobId' ]
60- return redirect (url_for ('result' , job_id = job_id ))
61-
62- @app .route ('/result/<job_id>' )
63- def result (job_id ):
64- response = textract .get_document_text_detection (JobId = job_id )
65-
66- while response ['JobStatus' ] == 'IN_PROGRESS' :
67- time .sleep (5 )
49+ try :
50+ s3 .upload_fileobj (
51+ file ,
52+ S3_BUCKET ,
53+ file .filename ,
54+ ExtraArgs = {'ContentType' : file .content_type }
55+ )
56+ response = textract .start_document_text_detection (
57+ DocumentLocation = {'S3Object' : {'Bucket' : S3_BUCKET , 'Name' : file .filename }}
58+ )
59+ return redirect (url_for ('status' , job_id = response ['JobId' ], original_filename = file .filename ))
60+ except Exception as e :
61+ return f"An error occurred: { str (e )} " , 500
62+
63+ return redirect (url_for ('index' ))
64+
65+ @app .route ('/status/<job_id>/<original_filename>' )
66+ def status (job_id , original_filename ):
67+ return render_template ('status.html' , job_id = job_id , original_filename = original_filename )
68+
69+ @app .route ('/api/check_status/<job_id>' )
70+ def check_status (job_id ):
71+ try :
6872 response = textract .get_document_text_detection (JobId = job_id )
69-
70- if response ['JobStatus' ] == 'SUCCEEDED' :
71- blocks = []
72- pages = [response ]
73-
74- while 'NextToken' in pages [- 1 ]:
75- pages .append (textract .get_document_text_detection (JobId = job_id , NextToken = pages [- 1 ]['NextToken' ]))
76-
77- for page in pages :
78- blocks .extend (page ['Blocks' ])
79-
80- extracted_text = ''
81- for block in blocks :
82- if block ['BlockType' ] == 'LINE' :
83- extracted_text += block ['Text' ] + '\n '
84- return render_template ('result.html' , text = extracted_text )
85-
86- return "Error processing document."
73+ status = response .get ('JobStatus' )
74+ return jsonify ({'status' : status })
75+ except Exception as e :
76+ return jsonify ({'status' : 'FAILED' , 'error' : str (e )})
77+
78+ @app .route ('/process_result/<job_id>/<original_filename>' )
79+ def process_result (job_id , original_filename ):
80+ try :
81+ response = textract .get_document_text_detection (JobId = job_id )
82+ if response .get ('JobStatus' ) == 'SUCCEEDED' :
83+ blocks = get_all_textract_blocks (job_id , response )
84+ csv_filename = create_and_upload_csv (blocks , original_filename )
85+ return redirect (url_for ('success' , csv_filename = csv_filename ))
86+ else :
87+ return "Job did not succeed. Status: " + response .get ('JobStatus' ), 500
88+ except Exception as e :
89+ return f"An error occurred during final processing: { str (e )} " , 500
90+
91+ # --- UPDATED SUCCESS FUNCTION ---
92+ @app .route ('/success/<csv_filename>' )
93+ def success (csv_filename ):
94+ try :
95+ download_url = s3 .generate_presigned_url (
96+ 'get_object' ,
97+ Params = {'Bucket' : S3_BUCKET , 'Key' : csv_filename },
98+ ExpiresIn = 300
99+ )
100+ # --- THIS IS THE NEW LINE ---
101+ print ("--- Generated Download URL ---\n " , download_url , "\n --------------------------" )
102+ # -----------------------------
103+
104+ except Exception as e :
105+ print (f"Error generating presigned URL: { e } " )
106+ download_url = None
107+
108+ return render_template (
109+ 'result.html' ,
110+ csv_filename = csv_filename ,
111+ bucket_name = S3_BUCKET ,
112+ download_url = download_url
113+ )
114+
115+ # ... (all helper functions remain the same)
116+ def get_all_textract_blocks (job_id , initial_response ):
117+ blocks = initial_response ['Blocks' ]
118+ next_token = initial_response .get ('NextToken' )
119+ while next_token :
120+ response = textract .get_document_text_detection (JobId = job_id , NextToken = next_token )
121+ blocks .extend (response ['Blocks' ])
122+ next_token = response .get ('NextToken' )
123+ return blocks
124+
125+ def create_and_upload_csv (blocks , original_filename ):
126+ string_buffer = io .StringIO ()
127+ writer = csv .writer (string_buffer )
128+ writer .writerow (['DetectedText' ])
129+ for block in blocks :
130+ if block ['BlockType' ] == 'LINE' :
131+ writer .writerow ([block ['Text' ]])
132+ csv_string = string_buffer .getvalue ()
133+ csv_bytes = csv_string .encode ('utf-8' )
134+ bytes_buffer = io .BytesIO (csv_bytes )
135+ base_filename = os .path .splitext (original_filename )[0 ]
136+ csv_filename = f"{ base_filename } _result.csv"
137+ s3 .upload_fileobj (
138+ bytes_buffer ,
139+ S3_BUCKET ,
140+ csv_filename ,
141+ ExtraArgs = {'ContentType' : 'text/csv' }
142+ )
143+ return csv_filename
87144
88145if __name__ == '__main__' :
89- app .run ()
146+ app .run (debug = True )
0 commit comments