Ashutoshbk commited on
Commit
c252293
·
1 Parent(s): 229b166

Initial commit: Add Flask app for deployment

Browse files
.env ADDED
@@ -0,0 +1 @@
 
 
1
+ OPENAI_API_KEY=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
Dockerfile ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use the official Python image as the base image
2
+ FROM python:3.9
3
+
4
+ # Set the working directory inside the container
5
+ WORKDIR /app
6
+
7
+ # Copy all project files into the container
8
+ COPY . /app
9
+
10
+ # Install required Python packages
11
+ RUN pip install --no-cache-dir -r requirements.txt
12
+
13
+ # Expose the port Flask runs on
14
+ EXPOSE 7860
15
+
16
+ # Set environment variables (API key will be passed securely)
17
+ ENV OPENAI_API_KEY=""
18
+
19
+ # Command to run the Flask app using Gunicorn
20
+ CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:7860", "app:app"]
Space.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: "Manhole Infrastructure Analysis"
3
+ sdk: "docker"
4
+ docker:
5
+ build: .
6
+ run: gunicorn -w 4 -b 0.0.0.0:7860 app:app
7
+ ---
app.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import base64
3
+ import re
4
+ from flask import Flask, render_template, request, redirect, url_for, flash
5
+ from openai import OpenAI
6
+ from PIL import Image
7
+ from io import BytesIO
8
+
9
+ # Initialize Flask app
10
+ app = Flask(__name__)
11
+ app.secret_key = 'supersecretkey'
12
+ app.config['UPLOAD_FOLDER'] = 'static/uploads'
13
+ os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)
14
+
15
+ # Initialize OpenAI client
16
+ # client = OpenAI()
17
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
18
+
19
+
20
+ # Function to convert image to Base64 PNG string
21
+ def convert_to_png_base64(image_path):
22
+ try:
23
+ with Image.open(image_path) as img:
24
+ img = img.convert("RGBA")
25
+ buffer = BytesIO()
26
+ img.save(buffer, format="PNG")
27
+ buffer.seek(0)
28
+ return base64.b64encode(buffer.read()).decode("utf-8")
29
+ except Exception as e:
30
+ print(f"Error: {e}")
31
+ return None
32
+
33
+ # Function to format response into clean HTML
34
+ def format_response(message_content):
35
+ # Replace ### Heading with <h3>Heading</h3>
36
+ formatted = re.sub(r'^### (.*?)$', r'<h3>\1</h3>', message_content, flags=re.M)
37
+ # Replace **bold** with <strong>bold</strong>
38
+ formatted = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', formatted)
39
+ # Replace "- " with list items
40
+ formatted = re.sub(r'^\- (.*?)$', r'<li>\1</li>', formatted, flags=re.M)
41
+ # Wrap lists with <ul>
42
+ if '<li>' in formatted:
43
+ formatted = f"<ul>{formatted}</ul>"
44
+ return formatted
45
+ @app.route('/', methods=['GET', 'POST'])
46
+ def index():
47
+ if request.method == 'POST':
48
+ # Check if an image file was uploaded
49
+ if 'image' in request.files:
50
+ file = request.files['image']
51
+ if file.filename != '':
52
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
53
+ file.save(filepath)
54
+ return render_template('index.html', uploaded_image=file.filename, response=None)
55
+
56
+ # Analyze button pressed
57
+ if 'analyze' in request.form:
58
+ image_name = request.form.get('uploaded_image')
59
+ filepath = os.path.join(app.config['UPLOAD_FOLDER'], image_name)
60
+
61
+ # Convert image to Base64
62
+ base64_image = convert_to_png_base64(filepath)
63
+ if not base64_image:
64
+ flash('Error processing image.')
65
+ return redirect(request.url)
66
+
67
+ # Send to OpenAI API
68
+ response = client.chat.completions.create(
69
+ model="gpt-4o-mini",
70
+ messages=[
71
+ {
72
+ "role": "user",
73
+ "content": [
74
+ {
75
+ "type": "text",
76
+ "text": '''You are an expert in analyzing infrastructure issues related to manholes. Your task is to analyze an image of a manhole(specially inside of red rectangle) and generate a detailed report with the following fields:
77
+ Ticket Number: Assign a unique identifier for the issue.
78
+ GPS Location: Provide a fake but realistic GPS coordinate.
79
+ Severity: Categorize the issue as Low, Medium, or High based on the visible damage.
80
+ Comment: Provide a brief explanation of the problem visible in the image.
81
+ Defect: Identify the specific defect (e.g., cracked cover, open manhole, debris obstruction, etc.).
82
+ Department: Always set as "Road/Manhole Management."
83
+ Action: Suggest an appropriate action based on the severity and type of defect.''',
84
+ },
85
+ {
86
+ "type": "image_url",
87
+ "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"},
88
+ },
89
+ ],
90
+ }
91
+ ],
92
+ )
93
+
94
+ # Extract response and format it
95
+ message_content = response.choices[0].message.content
96
+ formatted_content = format_response(message_content)
97
+ print(formatted_content)
98
+ return render_template('index.html', uploaded_image=image_name, response=formatted_content)
99
+
100
+ return render_template('index.html', uploaded_image=None, response=None)
101
+
102
+ if __name__ == '__main__':
103
+ app.run(debug=True)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Flask==2.3.2
2
+ openai==0.27.0
3
+ Pillow==10.0.0
4
+ gunicorn
5
+ python-dotenv
static/style.css ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* General styles */
2
+ body {
3
+ font-family: Arial, sans-serif;
4
+ margin: 0;
5
+ padding: 0;
6
+ background-color: #f4f4f4;
7
+ display: flex;
8
+ justify-content: center;
9
+ align-items: center;
10
+ min-height: 100vh;
11
+ }
12
+
13
+ .container {
14
+ background: #ffffff;
15
+ border-radius: 10px;
16
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
17
+ padding: 20px;
18
+ max-width: 1200px;
19
+ width: 100%;
20
+ }
21
+
22
+ /* Header */
23
+ h1 {
24
+ color: #333;
25
+ font-size: 24px;
26
+ margin-bottom: 20px;
27
+ text-align: center;
28
+ }
29
+
30
+ /* Form styles */
31
+ form {
32
+ margin: 20px 0;
33
+ text-align: center;
34
+ }
35
+
36
+ input[type="file"], button {
37
+ margin: 10px 0;
38
+ padding: 10px;
39
+ border: 1px solid #ccc;
40
+ border-radius: 5px;
41
+ display: inline-block;
42
+ width: auto;
43
+ }
44
+
45
+ button {
46
+ background-color: #007bff;
47
+ color: white;
48
+ border: none;
49
+ cursor: pointer;
50
+ font-size: 16px;
51
+ }
52
+
53
+ button:hover {
54
+ background-color: #0056b3;
55
+ }
56
+
57
+ /* Layout for result container */
58
+ .result-container {
59
+ display: flex;
60
+ flex-direction: row; /* Ensures elements are side by side */
61
+ justify-content: flex-start; /* Align items to the left */
62
+ align-items: flex-start; /* Align items at the top */
63
+ gap: 20px; /* Space between elements */
64
+ margin-top: 20px;
65
+ }
66
+
67
+ .image-container, .analysis-container {
68
+ flex: 1;
69
+ }
70
+
71
+ /* Image styling */
72
+ .image-container img {
73
+ max-width: 100%;
74
+ height: auto;
75
+ border: 1px solid #ddd;
76
+ border-radius: 5px;
77
+ }
78
+
79
+ /* Analysis section styling */
80
+ .analysis-container {
81
+ text-align: left;
82
+ background: #f9f9f9;
83
+ padding: 15px;
84
+ border-radius: 5px;
85
+ border: 1px solid #ddd;
86
+ }
87
+
88
+ .analysis-result {
89
+ white-space: pre-wrap;
90
+ font-family: 'Courier New', Courier, monospace;
91
+ font-size: 14px;
92
+ color: #555;
93
+ }
static/uploads/image007.png ADDED
static/uploads/image007_result.png ADDED
static/uploads/image009_result.png ADDED
static/uploads/image011_result.png ADDED
templates/index.html ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Upload Image for Analysis</title>
7
+ <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
8
+ </head>
9
+ <body>
10
+ <div class="container">
11
+ <h1>Upload Image for Analysis</h1>
12
+ {% with messages = get_flashed_messages() %}
13
+ {% if messages %}
14
+ <div class="messages">
15
+ {% for message in messages %}
16
+ <p class="alert">{{ message }}</p>
17
+ {% endfor %}
18
+ </div>
19
+ {% endif %}
20
+ {% endwith %}
21
+
22
+ {% if not uploaded_image %}
23
+ <form action="/" method="post" enctype="multipart/form-data">
24
+ <input type="file" name="image" accept="image/*" required>
25
+ <button type="submit">Upload</button>
26
+ </form>
27
+ {% else %}
28
+ <div class="result-container">
29
+ <div class="image-container">
30
+ <h2>Uploaded Image:</h2>
31
+ <img src="{{ url_for('static', filename='uploads/' ~ uploaded_image) }}" alt="Uploaded Image">
32
+ </div>
33
+ <div class="analysis-container">
34
+ {% if not response %}
35
+ <form action="/" method="post">
36
+ <input type="hidden" name="uploaded_image" value="{{ uploaded_image }}">
37
+ <button type="submit" name="analyze">Analyze</button>
38
+ </form>
39
+ {% else %}
40
+ <h2>Analysis Result:</h2>
41
+ <div class="analysis-result">
42
+ {{ response | safe }}
43
+ </div>
44
+ {% endif %}
45
+ </div>
46
+ </div>
47
+ {% endif %}
48
+ </div>
49
+ </body>
50
+ </html>