Wassymk commited on
Commit
0f369cb
·
1 Parent(s): 8acfb53

store images

Browse files
Files changed (2) hide show
  1. app.py +46 -46
  2. storage.py +212 -0
app.py CHANGED
@@ -6,49 +6,35 @@ import io
6
  import base64
7
  import logging
8
  import os
9
- import json
10
- import threading
11
- import time
12
- import os
 
 
13
 
14
  # Configure logging
15
  logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
16
  logger = logging.getLogger(__name__)
17
 
18
- # Configure Gemini
19
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
20
  MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY")
21
 
 
 
 
 
 
 
 
 
 
 
 
22
  genai.configure(api_key=GEMINI_API_KEY)
23
  gemini_model = genai.GenerativeModel('gemini-2.0-flash-exp')
24
 
25
- VOTES_FILE = "votes.json"
26
- votes = []
27
- votes_lock = threading.Lock()
28
-
29
- def load_votes():
30
- global votes
31
- if os.path.exists(VOTES_FILE):
32
- with open(VOTES_FILE, "r") as f:
33
- votes = json.load(f)
34
- else:
35
- votes = []
36
-
37
- def save_votes():
38
- with votes_lock:
39
- with open(VOTES_FILE, "w") as f:
40
- json.dump(votes, f, indent=2)
41
-
42
- def periodic_vote_dump(interval=60):
43
- def run():
44
- while True:
45
- save_votes()
46
- time.sleep(interval)
47
- t = threading.Thread(target=run, daemon=True)
48
- t.start()
49
-
50
- load_votes()
51
- periodic_vote_dump(60)
52
 
53
  def get_default_username(profile: gr.OAuthProfile | None) -> str:
54
  """
@@ -122,9 +108,37 @@ def process_image(image):
122
  return "Please upload an image.", "Please upload an image."
123
 
124
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  gemini_result = gemini_ocr(image)
126
  mistral_result = mistral_ocr(image)
127
  return gemini_result, mistral_result
 
128
  except Exception as e:
129
  logger.error(f"Error processing image: {e}")
130
  return f"Error processing image: {e}", f"Error processing image: {e}"
@@ -184,13 +198,6 @@ with gr.Blocks(title="OCR Comparison: Gemini vs Mistral", css="""
184
  if not username:
185
  gr.Info("Please login with your Hugging Face account to vote.")
186
  return
187
- image_id = get_image_id(image)
188
- with votes_lock:
189
- for v in votes:
190
- if v["image_id"] == image_id and v["username"] == username:
191
- gr.Info("You already voted for this image.")
192
- return
193
- votes.append({"image_id": image_id, "username": username, "winner": "gemini"})
194
  info_message = (
195
  f"<p>You voted for <strong style='color:green;'>👈 Gemini OCR</strong>.</p>"
196
  f"<p><span style='color:green;'>👈 Gemini OCR</span> - "
@@ -202,13 +209,6 @@ with gr.Blocks(title="OCR Comparison: Gemini vs Mistral", css="""
202
  if not username:
203
  gr.Info("Please login with your Hugging Face account to vote.")
204
  return
205
- image_id = get_image_id(image)
206
- with votes_lock:
207
- for v in votes:
208
- if v["image_id"] == image_id and v["username"] == username:
209
- gr.Info("You already voted for this image.")
210
- return
211
- votes.append({"image_id": image_id, "username": username, "winner": "mistral"})
212
  info_message = (
213
  f"<p>You voted for <strong style='color:blue;'>👉 Mistral OCR</strong>.</p>"
214
  f"<p><span style='color:green;'>👈 Gemini OCR</span> - "
 
6
  import base64
7
  import logging
8
  import os
9
+ from dotenv import load_dotenv
10
+ from storage import upload_file_to_bucket
11
+ import datetime
12
+
13
+ # Load environment variables from .env file
14
+ load_dotenv()
15
 
16
  # Configure logging
17
  logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
18
  logger = logging.getLogger(__name__)
19
 
20
+ # Configure API keys from environment variables
21
  GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
22
  MISTRAL_API_KEY = os.getenv("MISTRAL_API_KEY")
23
 
24
+ # Log API key status (without exposing the actual keys)
25
+ if GEMINI_API_KEY:
26
+ logger.info("✅ GEMINI_API_KEY loaded successfully")
27
+ else:
28
+ logger.error("❌ GEMINI_API_KEY not found in environment variables")
29
+
30
+ if MISTRAL_API_KEY:
31
+ logger.info("✅ MISTRAL_API_KEY loaded successfully")
32
+ else:
33
+ logger.error("❌ MISTRAL_API_KEY not found in environment variables")
34
+
35
  genai.configure(api_key=GEMINI_API_KEY)
36
  gemini_model = genai.GenerativeModel('gemini-2.0-flash-exp')
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
 
39
  def get_default_username(profile: gr.OAuthProfile | None) -> str:
40
  """
 
108
  return "Please upload an image.", "Please upload an image."
109
 
110
  try:
111
+ # Save the PIL image to a temporary file
112
+ temp_filename = f"temp_image_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
113
+ image.save(temp_filename)
114
+
115
+ # Upload the temporary file to Supabase storage
116
+ logger.info(f"📤 Uploading image to Supabase storage: {temp_filename}")
117
+ upload_result = upload_file_to_bucket(
118
+ file_path=temp_filename,
119
+ bucket_name="images",
120
+ storage_path=f"ocr_images/{temp_filename}",
121
+ file_options={"cache-control": "3600", "upsert": "false"}
122
+ )
123
+
124
+ if upload_result["success"]:
125
+ logger.info(f"✅ Image uploaded successfully: {upload_result['storage_path']}")
126
+ logger.info(f"🔗 Public URL: {upload_result['public_url']}")
127
+ else:
128
+ logger.error(f"❌ Image upload failed: {upload_result['error']}")
129
+
130
+ # Clean up temporary file
131
+ try:
132
+ os.remove(temp_filename)
133
+ logger.info(f"🗑️ Cleaned up temporary file: {temp_filename}")
134
+ except Exception as e:
135
+ logger.warning(f"⚠️ Could not remove temporary file {temp_filename}: {e}")
136
+
137
+ # Process OCR
138
  gemini_result = gemini_ocr(image)
139
  mistral_result = mistral_ocr(image)
140
  return gemini_result, mistral_result
141
+
142
  except Exception as e:
143
  logger.error(f"Error processing image: {e}")
144
  return f"Error processing image: {e}", f"Error processing image: {e}"
 
198
  if not username:
199
  gr.Info("Please login with your Hugging Face account to vote.")
200
  return
 
 
 
 
 
 
 
201
  info_message = (
202
  f"<p>You voted for <strong style='color:green;'>👈 Gemini OCR</strong>.</p>"
203
  f"<p><span style='color:green;'>👈 Gemini OCR</span> - "
 
209
  if not username:
210
  gr.Info("Please login with your Hugging Face account to vote.")
211
  return
 
 
 
 
 
 
 
212
  info_message = (
213
  f"<p>You voted for <strong style='color:blue;'>👉 Mistral OCR</strong>.</p>"
214
  f"<p><span style='color:green;'>👈 Gemini OCR</span> - "
storage.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Supabase storage helper for uploading files.
2
+
3
+ This module provides functions to upload files to Supabase storage
4
+ using HTTP requests, avoiding the dependency issues with the supabase client library.
5
+ """
6
+ import os
7
+ import requests
8
+ import logging
9
+ from typing import Optional, Dict, Any
10
+ from pathlib import Path
11
+
12
+ # Import Supabase credentials from environment variables
13
+ from dotenv import load_dotenv
14
+ load_dotenv()
15
+
16
+ SUPABASE_URL = os.getenv("SUPABASE_URL")
17
+ SUPABASE_KEY = os.getenv("SUPABASE_KEY")
18
+
19
+ if not SUPABASE_URL or not SUPABASE_KEY:
20
+ raise ImportError("Could not load Supabase credentials from environment variables")
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Supabase storage API configuration
25
+ STORAGE_BASE_URL = f"{SUPABASE_URL}/storage/v1"
26
+ HEADERS = {
27
+ "apikey": SUPABASE_KEY,
28
+ "Authorization": f"Bearer {SUPABASE_KEY}",
29
+ "Content-Type": "application/json"
30
+ }
31
+
32
+ def upload_file_to_bucket(
33
+ file_path: str,
34
+ bucket_name: str = "images",
35
+ storage_path: Optional[str] = None,
36
+ file_options: Optional[Dict[str, Any]] = None
37
+ ) -> Dict[str, Any]:
38
+ """
39
+ Upload a file to Supabase storage bucket.
40
+
41
+ Args:
42
+ file_path: Local path to the file to upload
43
+ bucket_name: Name of the storage bucket (default: "images")
44
+ storage_path: Path in the bucket where to store the file (default: filename)
45
+ file_options: Optional file options like cache-control, upsert, etc.
46
+
47
+ Returns:
48
+ Dictionary with upload result information
49
+ """
50
+ try:
51
+ # Check if file exists
52
+ if not os.path.exists(file_path):
53
+ raise FileNotFoundError(f"File not found: {file_path}")
54
+
55
+ # Get file info
56
+ file_path_obj = Path(file_path)
57
+ file_name = file_path_obj.name
58
+
59
+ # Use provided storage_path or default to filename
60
+ if storage_path is None:
61
+ storage_path = file_name
62
+
63
+ # Prepare upload URL
64
+ upload_url = f"{STORAGE_BASE_URL}/object/{bucket_name}/{storage_path}"
65
+
66
+ # Prepare headers for file upload
67
+ upload_headers = {
68
+ "apikey": SUPABASE_KEY,
69
+ "Authorization": f"Bearer {SUPABASE_KEY}"
70
+ }
71
+
72
+ # Add file options if provided
73
+ if file_options:
74
+ for key, value in file_options.items():
75
+ upload_headers[f"x-upsert"] = str(value).lower() if key == "upsert" else str(value)
76
+
77
+ # Read and upload file
78
+ with open(file_path, "rb") as f:
79
+ response = requests.post(
80
+ upload_url,
81
+ headers=upload_headers,
82
+ data=f
83
+ )
84
+
85
+ if response.status_code == 200:
86
+ result = response.json()
87
+ logger.info(f"✅ File uploaded successfully: {file_name}")
88
+ logger.info(f"📁 Storage path: {storage_path}")
89
+ logger.info(f"🔗 Public URL: {result.get('publicUrl', 'N/A')}")
90
+ return {
91
+ "success": True,
92
+ "file_name": file_name,
93
+ "storage_path": storage_path,
94
+ "public_url": result.get('publicUrl'),
95
+ "response": result
96
+ }
97
+ else:
98
+ logger.error(f"❌ Upload failed with status {response.status_code}")
99
+ logger.error(f"Response: {response.text}")
100
+ return {
101
+ "success": False,
102
+ "error": f"Upload failed: {response.status_code}",
103
+ "response": response.text
104
+ }
105
+
106
+ except Exception as e:
107
+ logger.error(f"❌ Error uploading file: {e}")
108
+ return {
109
+ "success": False,
110
+ "error": str(e)
111
+ }
112
+
113
+ def get_file_url(bucket_name: str, file_path: str) -> str:
114
+ """
115
+ Get the public URL for a file in Supabase storage.
116
+
117
+ Args:
118
+ bucket_name: Name of the storage bucket
119
+ file_path: Path to the file in the bucket
120
+
121
+ Returns:
122
+ Public URL for the file
123
+ """
124
+ return f"{SUPABASE_URL}/storage/v1/object/public/{bucket_name}/{file_path}"
125
+
126
+ def list_bucket_files(bucket_name: str = "images") -> Dict[str, Any]:
127
+ """
128
+ List all files in a storage bucket.
129
+
130
+ Args:
131
+ bucket_name: Name of the storage bucket
132
+
133
+ Returns:
134
+ Dictionary with list of files
135
+ """
136
+ try:
137
+ list_url = f"{STORAGE_BASE_URL}/object/list/{bucket_name}"
138
+ response = requests.get(list_url, headers=HEADERS)
139
+
140
+ if response.status_code == 200:
141
+ result = response.json()
142
+ logger.info(f"✅ Listed {len(result)} files in bucket '{bucket_name}'")
143
+ return {
144
+ "success": True,
145
+ "files": result
146
+ }
147
+ else:
148
+ logger.error(f"❌ Failed to list files: {response.status_code}")
149
+ return {
150
+ "success": False,
151
+ "error": f"Failed to list files: {response.status_code}"
152
+ }
153
+ except Exception as e:
154
+ logger.error(f"❌ Error listing files: {e}")
155
+ return {
156
+ "success": False,
157
+ "error": str(e)
158
+ }
159
+
160
+ def test_upload_image_png():
161
+ """Test function to upload Image.png to the images bucket."""
162
+ try:
163
+ logger.info("🚀 Testing file upload to Supabase storage...")
164
+
165
+ # First, test if we can list files (this tests basic connectivity)
166
+ logger.info("📋 Testing bucket connectivity...")
167
+ list_result = list_bucket_files("images")
168
+ if list_result["success"]:
169
+ logger.info(f"✅ Bucket connectivity successful! Found {len(list_result['files'])} files")
170
+ else:
171
+ logger.warning(f"⚠️ Bucket connectivity issue: {list_result['error']}")
172
+
173
+ # Test upload
174
+ logger.info("📤 Testing file upload...")
175
+ result = upload_file_to_bucket(
176
+ file_path="Image.png",
177
+ bucket_name="images",
178
+ storage_path="test/Image.png",
179
+ file_options={"cache-control": "3600", "upsert": "false"}
180
+ )
181
+
182
+ if result["success"]:
183
+ logger.info("✅ Upload test successful!")
184
+ logger.info(f"📁 File uploaded to: {result['storage_path']}")
185
+ logger.info(f"🔗 Public URL: {result['public_url']}")
186
+ return True
187
+ else:
188
+ logger.error(f"❌ Upload test failed: {result['error']}")
189
+
190
+ # Provide helpful error information
191
+ if "row-level security policy" in result.get('response', ''):
192
+ logger.error("🔧 This is a storage permissions issue.")
193
+ logger.error("📋 To fix this, you need to configure your Supabase storage bucket:")
194
+ logger.error("""
195
+ 1. Go to your Supabase dashboard
196
+ 2. Navigate to Storage > Policies
197
+ 3. For the 'images' bucket, add a policy like:
198
+
199
+ CREATE POLICY "Allow public uploads" ON storage.objects
200
+ FOR INSERT WITH CHECK (bucket_id = 'images');
201
+
202
+ CREATE POLICY "Allow public reads" ON storage.objects
203
+ FOR SELECT USING (bucket_id = 'images');
204
+
205
+ Or make the bucket public in Storage > Settings
206
+ """)
207
+
208
+ return False
209
+
210
+ except Exception as e:
211
+ logger.error(f"❌ Upload test error: {e}")
212
+ return False