File size: 9,432 Bytes
65933cd
 
 
 
3d94de1
65933cd
 
 
 
 
 
4991707
84aedaf
65933cd
 
 
 
 
 
 
 
 
 
 
 
 
07e2e28
 
 
 
 
3d94de1
07e2e28
 
 
 
 
 
 
3d94de1
 
 
 
07e2e28
 
 
 
 
bc8e3e9
 
 
 
 
 
65933cd
 
 
07e2e28
 
65933cd
 
 
 
07e2e28
 
65933cd
3bc42eb
07e2e28
779c5c3
3bc42eb
779c5c3
 
 
 
07e2e28
779c5c3
3bc42eb
779c5c3
 
 
65933cd
07e2e28
65933cd
07e2e28
65933cd
 
 
 
 
84aedaf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65933cd
07e2e28
 
 
65933cd
84aedaf
07e2e28
65933cd
82ff6c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65933cd
 
 
82ff6c1
 
 
65933cd
82ff6c1
 
 
 
 
 
 
 
 
 
 
65933cd
07e2e28
 
65933cd
bc8e3e9
07e2e28
65933cd
 
07e2e28
65933cd
 
07e2e28
65933cd
07e2e28
65933cd
 
07e2e28
65933cd
 
 
07e2e28
 
 
 
65933cd
 
07e2e28
 
 
 
 
 
 
 
 
3d94de1
07e2e28
 
 
 
 
 
 
 
f503159
 
07e2e28
 
 
 
3d94de1
07e2e28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import hashlib
import mimetypes
from fastapi import APIRouter, HTTPException, Depends
from sqlalchemy.orm import Session
from sqlalchemy import text

from ..database import SessionLocal
from ..models import Images, image_countries
from ..images import CreateImageFromUrlIn, CreateImageFromUrlOut
from .. import storage
from ..storage import upload_bytes, get_object_url
from ..config import settings
from ..services.image_preprocessor import ImagePreprocessor

router = APIRouter()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@router.post("/from-url", response_model=CreateImageFromUrlOut)
async def create_image_from_url(payload: CreateImageFromUrlIn, db: Session = Depends(get_db)):
    try:
        print(f"DEBUG: Creating contribution from URL: {payload.url}")
        print(f"DEBUG: Payload: {payload}")
        
        # Check database connectivity
        try:
            db.execute(text("SELECT 1"))
            print("βœ“ Database connection OK")
        except Exception as db_error:
            print(f"βœ— Database connection failed: {db_error}")
            raise HTTPException(status_code=500, detail=f"Database connection failed: {db_error}")
        
        # Check if required tables exist
        try:
            db.execute(text("SELECT 1 FROM sources LIMIT 1"))
            db.execute(text("SELECT 1 FROM event_types LIMIT 1")) 
            db.execute(text("SELECT 1 FROM spatial_references LIMIT 1"))
            db.execute(text("SELECT 1 FROM image_types LIMIT 1"))
            print("βœ“ Required tables exist")
        except Exception as table_error:
            print(f"βœ— Required tables missing: {table_error}")
            raise HTTPException(status_code=500, detail=f"Required tables missing: {table_error}")
        
        if '/api/images/' in payload.url and '/file' in payload.url:
            url_parts = payload.url.split('/api/images/')
            if len(url_parts) > 1:
                image_id = url_parts[1].split('/file')[0]
            else:
                raise HTTPException(status_code=400, detail="Invalid image URL format")
        else:
            raise HTTPException(status_code=400, detail="Invalid image URL format")
        
        print(f"DEBUG: Extracted image_id: {image_id}")
        
        existing_image = db.query(Images).filter(Images.image_id == image_id).first()
        if not existing_image:
            raise HTTPException(status_code=404, detail="Source image not found")
        
        print(f"DEBUG: Found existing image: {existing_image.image_id}")
        
        try:
            if hasattr(storage, 's3') and settings.STORAGE_PROVIDER != "local":
                print(f"DEBUG: Using S3 storage, bucket: {settings.S3_BUCKET}")
                response = storage.s3.get_object(
                    Bucket=settings.S3_BUCKET,
                    Key=existing_image.file_key,
                )
                data = response["Body"].read()
            else:
                print(f"DEBUG: Using local storage: {settings.STORAGE_DIR}")
                import os
                file_path = os.path.join(settings.STORAGE_DIR, existing_image.file_key)
                with open(file_path, 'rb') as f:
                    data = f.read()
            
            content_type = "image/jpeg"
            print(f"DEBUG: Image data size: {len(data)} bytes")
        except Exception as e:
            print(f"ERROR: Failed to fetch image from storage: {e}")
            raise HTTPException(status_code=400, detail=f"Failed to fetch image from storage: {e}")
        
        if len(data) > 25 * 1024 * 1024:
            raise HTTPException(status_code=413, detail="Image too large")

        # Preprocess image if needed
        try:
            processed_data, processed_filename, mime_type = ImagePreprocessor.preprocess_image(
                data, 
                f"contributed.jpg",  # Default filename
                target_format='PNG',  # Default to PNG for better quality
                quality=95
            )
            
            # Log preprocessing info
            print(f"DEBUG: Image preprocessed: {mime_type} -> {processed_filename}")
            
        except Exception as e:
            print(f"DEBUG: Image preprocessing failed: {str(e)}")
            # Fall back to original content if preprocessing fails
            processed_data = data
            processed_filename = f"contributed.jpg"
            mime_type = 'image/jpeg'  # Default fallback

        ext = mimetypes.guess_extension(mime_type) or ".png"
        key = upload_bytes(processed_data, filename=processed_filename, content_type=mime_type)
        image_url = get_object_url(key, expires_in=86400)
        
        print(f"DEBUG: Uploaded to key: {key}")
        print(f"DEBUG: Generated URL: {image_url}")

        sha = hashlib.sha256(processed_data).hexdigest()
        print(f"DEBUG: Generated SHA256: {sha}")

        # Set prompt and schema based on image type
        prompt_code = "DEFAULT_CRISIS_MAP"
        schema_id = "default_caption@1.0.0"
        if payload.image_type == "drone_image":
            prompt_code = "DEFAULT_DRONE_IMAGE"
            schema_id = "drone_caption@1.0.0"
        

        if payload.image_type == "drone_image":
            source = payload.source
            event_type = payload.event_type if payload.event_type else "OTHER"
            epsg = payload.epsg if payload.epsg else "OTHER"
        else:
            source = payload.source if payload.source else "OTHER"
            event_type = payload.event_type if payload.event_type else "OTHER"
            epsg = payload.epsg if payload.epsg else "OTHER"
        
        if payload.image_type != "drone_image":
            payload.center_lon = None
            payload.center_lat = None
            payload.amsl_m = None
            payload.agl_m = None
            payload.heading_deg = None
            payload.yaw_deg = None
            payload.pitch_deg = None
            payload.roll_deg = None
            payload.rtk_fix = None
            payload.std_h_m = None
            payload.std_v_m = None
        
        img = Images(
            file_key=key,
            sha256=sha,
            source=source,
            event_type=event_type,
            epsg=epsg,
            image_type=payload.image_type,
            center_lon=payload.center_lon,
            center_lat=payload.center_lat,
            amsl_m=payload.amsl_m,
            agl_m=payload.agl_m,
            heading_deg=payload.heading_deg,
            yaw_deg=payload.yaw_deg,
            pitch_deg=payload.pitch_deg,
            roll_deg=payload.roll_deg,
            rtk_fix=payload.rtk_fix,
            std_h_m=payload.std_h_m,
            std_v_m=payload.std_v_m
        )
        
        print(f"DEBUG: Created Images object: {img}")
        db.add(img)
        db.flush()
        print(f"DEBUG: Flushed to database, image_id: {img.image_id}")

        for c in payload.countries:
            print(f"DEBUG: Adding country: {c}")
            db.execute(image_countries.insert().values(image_id=img.image_id, c_code=c))

        print(f"DEBUG: About to commit transaction")
        db.commit()
        print(f"DEBUG: Transaction committed successfully")

        result = CreateImageFromUrlOut(image_id=str(img.image_id), image_url=image_url)
        print(f"DEBUG: Returning result: {result}")
        return result
        
    except Exception as e:
        print(f"ERROR: Exception in create_image_from_url: {e}")
        print(f"ERROR: Exception type: {type(e)}")
        import traceback
        traceback.print_exc()
        db.rollback()
        raise HTTPException(status_code=500, detail=f"Failed to create image: {str(e)}")

@router.get("/debug/db-status")
async def debug_database_status(db: Session = Depends(get_db)):
    """Debug endpoint to check database status"""
    try:
        status = {}
        
        # Check basic connectivity
        try:
            db.execute(text("SELECT 1"))
            status["database_connection"] = "OK"
        except Exception as e:
            status["database_connection"] = f"FAILED: {e}"
            return status
        
        # Check required tables
        tables_to_check = [
            "sources", "event_types", "spatial_references", "image_types",
            "prompts", "models", "json_schemas", "images", "image_countries",
            "captions", "images_captions"
        ]
        
        for table in tables_to_check:
            try:
                result = db.execute(text(f"SELECT COUNT(*) FROM {table}"))
                count = result.scalar()
                status[f"table_{table}"] = f"EXISTS ({count} rows)"
            except Exception as e:
                status[f"table_{table}"] = f"MISSING: {e}"
        
        # Check foreign key constraints
        try:
            # Check if we can create a simple Images object
            from ..models import Images
            test_img = Images(
                file_key="test",
                sha256="test",
                source="OTHER",
                event_type="OTHER", 
                epsg="OTHER",
                image_type="crisis_map"
            )
            status["model_creation"] = "OK"
        except Exception as e:
            status["model_creation"] = f"FAILED: {e}"
        
        return status
        
    except Exception as e:
        return {"error": str(e)}