File size: 8,383 Bytes
b22bc60
df623ed
b22bc60
 
 
 
 
df623ed
b22bc60
44f5bca
 
 
 
 
 
b22bc60
44f5bca
 
 
 
 
 
 
 
b22bc60
 
44f5bca
 
 
 
 
 
 
b22bc60
 
44f5bca
 
 
 
 
 
 
 
b22bc60
 
44f5bca
 
 
 
 
 
 
b22bc60
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df623ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
"""
Enhanced utility functions for LabelIt! application with real-time analytics
"""

import json
import os
from PIL import Image
from collections import Counter

# File paths for data storage (compatible with pre-existing folders)
DATA_DIR = 'data'
USERS_FILE = os.path.join(DATA_DIR, 'users.json')
LABELS_FILE = os.path.join(DATA_DIR, 'labels.json')
IMAGES_DIR = os.path.join(DATA_DIR, 'images')

def load_users():
    """Load users from JSON file with error handling for pre-existing files"""
    if os.path.exists(USERS_FILE):
        try:
            with open(USERS_FILE, 'r') as f:
                return json.load(f)
        except (FileNotFoundError, json.JSONDecodeError, PermissionError):
            pass
    return {}

def save_users(users):
    """Save users to JSON file with error handling for read-only environments"""
    if os.path.exists(DATA_DIR):
        try:
            with open(USERS_FILE, 'w') as f:
                json.dump(users, f, indent=2)
        except (OSError, PermissionError):
            pass

def load_labels():
    """Load labels from JSON file with error handling for pre-existing files"""
    if os.path.exists(LABELS_FILE):
        try:
            with open(LABELS_FILE, 'r') as f:
                return json.load(f)
        except (FileNotFoundError, json.JSONDecodeError, PermissionError):
            pass
    return {}

def save_labels(labels):
    """Save labels to JSON file with error handling for read-only environments"""
    if os.path.exists(DATA_DIR):
        try:
            with open(LABELS_FILE, 'w') as f:
                json.dump(labels, f, indent=2)
        except (OSError, PermissionError):
            pass

def validate_image(uploaded_file):
    """Validate uploaded image file"""
    # Check file size (10MB limit)
    if uploaded_file.size > 10 * 1024 * 1024:  # 10MB in bytes
        return {
            'valid': False,
            'error': 'file_too_large'
        }
    
    # Check file type
    allowed_types = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif']
    if uploaded_file.type not in allowed_types:
        return {
            'valid': False,
            'error': 'invalid_file_type'
        }
    
    # Try to open and verify image
    try:
        image = Image.open(uploaded_file)
        image.verify()
        uploaded_file.seek(0)  # Reset file pointer
        return {
            'valid': True,
            'error': None
        }
    except Exception:
        return {
            'valid': False,
            'error': 'image_processing_error'
        }

def get_categories():
    """Get list of available categories"""
    return [
        'Animals',
        'Food',
        'Objects',
        'Nature',
        'People',
        'Transportation'
    ]

def calculate_statistics():
    """Calculate comprehensive real-time statistics for the analytics dashboard"""
    users = load_users()
    labels_data = load_labels()
    
    # Basic counts
    total_users = len(users)
    total_images = len(labels_data)
    
    # Count total labels across all images
    total_labels = 0
    languages_used = set()
    language_breakdown = Counter()
    category_breakdown = Counter()
    
    # Location-based statistics
    images_with_location = 0
    gps_accuracy_breakdown = Counter()
    location_methods = Counter()
    country_breakdown = Counter()
    city_breakdown = Counter()
    
    for entry_data in labels_data.values():
        # Count labels for this image
        image_labels = entry_data.get('labels', [])
        total_labels += len(image_labels)
        
        # Track languages used
        for label in image_labels:
            lang = label.get('language', 'unknown')
            languages_used.add(lang)
            language_breakdown[lang] += 1
        
        # Track categories
        category = entry_data.get('category', 'Unknown')
        category_breakdown[category] += 1
        
        # Track location data
        location = entry_data.get('location')
        if location and location.get('latitude'):
            images_with_location += 1
            
            # Track location capture method
            method = location.get('method', 'Unknown')
            location_methods[method] += 1
            
            # Track GPS accuracy levels
            accuracy = location.get('accuracy')
            if accuracy is not None:
                if accuracy <= 10:
                    gps_accuracy_breakdown['High'] += 1
                elif accuracy <= 50:
                    gps_accuracy_breakdown['Medium'] += 1
                else:
                    gps_accuracy_breakdown['Low'] += 1
            else:
                gps_accuracy_breakdown['Unknown'] += 1
            
            # Track countries and cities
            country = location.get('country')
            if country:
                country_breakdown[country] += 1
            
            city = location.get('city')
            if city:
                city_breakdown[city] += 1
    
    return {
        'total_users': total_users,
        'total_images': total_images,
        'total_labels': total_labels,
        'languages_used': len(languages_used),
        'language_breakdown': dict(language_breakdown),
        'category_breakdown': dict(category_breakdown),
        'images_with_location': images_with_location,
        'gps_accuracy_breakdown': dict(gps_accuracy_breakdown),
        'location_methods': dict(location_methods),
        'country_breakdown': dict(country_breakdown),
        'city_breakdown': dict(city_breakdown)
    }

def get_location_accuracy_level(accuracy):
    """Determine location accuracy level based on accuracy value"""
    if accuracy is None:
        return "Unknown", "#95a5a6"
    
    if accuracy <= 10:
        return "High", "#27ae60"
    elif accuracy <= 50:
        return "Medium", "#f39c12"
    else:
        return "Low", "#e74c3c"

def format_location_display(location_data):
    """Format location data for display"""
    if not location_data or not location_data.get('lat'):
        return "๐Ÿ“ Location not available"
    
    lat = location_data['lat']
    lon = location_data['lon']
    method = location_data.get('method', 'Unknown')
    accuracy = location_data.get('accuracy')
    
    # Method icon
    method_icons = {
        'GPS': '๐Ÿ›ฐ๏ธ',
        'IP': '๐ŸŒ',
        'Manual': '๐Ÿ“'
    }
    method_icon = method_icons.get(method, '๐Ÿ“')
    
    # Base location string
    location_str = f"{method_icon} {lat:.6f}, {lon:.6f}"
    
    # Add accuracy if available
    if accuracy:
        level, _ = get_location_accuracy_level(accuracy)
        location_str += f" (ยฑ{accuracy:.0f}m - {level} Accuracy)"
    
    # Add city/country if available
    if location_data.get('city') and location_data.get('country'):
        location_str += f" - {location_data['city']}, {location_data['country']}"
    
    return location_str

def validate_coordinates(lat, lon):
    """Validate latitude and longitude coordinates"""
    try:
        lat = float(lat)
        lon = float(lon)
        
        if -90 <= lat <= 90 and -180 <= lon <= 180:
            return True, lat, lon
        else:
            return False, None, None
    except (ValueError, TypeError):
        return False, None, None

def get_user_contribution_stats(username):
    """Get contribution statistics for a specific user"""
    labels_data = load_labels()
    
    user_stats = {
        'images_uploaded': 0,
        'labels_added': 0,
        'languages_contributed': set(),
        'categories_contributed': set()
    }
    
    for entry_data in labels_data.values():
        # Check if user uploaded this image
        if entry_data.get('uploaded_by') == username:
            user_stats['images_uploaded'] += 1
            user_stats['categories_contributed'].add(entry_data.get('category', 'Unknown'))
        
        # Check labels added by this user
        for label in entry_data.get('labels', []):
            if label.get('added_by') == username:
                user_stats['labels_added'] += 1
                user_stats['languages_contributed'].add(label.get('language', 'unknown'))
    
    # Convert sets to counts
    user_stats['languages_contributed'] = len(user_stats['languages_contributed'])
    user_stats['categories_contributed'] = len(user_stats['categories_contributed'])
    
    return user_stats