Spaces:
Sleeping
Sleeping
Update utils.py
Browse files
utils.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
| 1 |
"""
|
| 2 |
-
|
| 3 |
"""
|
| 4 |
|
| 5 |
import json
|
| 6 |
import os
|
| 7 |
from PIL import Image
|
|
|
|
| 8 |
|
| 9 |
def load_users():
|
| 10 |
"""Load users from JSON file"""
|
|
@@ -73,4 +74,171 @@ def get_categories():
|
|
| 73 |
'Nature',
|
| 74 |
'People',
|
| 75 |
'Transportation'
|
| 76 |
-
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
"""
|
| 2 |
+
Enhanced utility functions for LabelIt! application with real-time analytics
|
| 3 |
"""
|
| 4 |
|
| 5 |
import json
|
| 6 |
import os
|
| 7 |
from PIL import Image
|
| 8 |
+
from collections import Counter
|
| 9 |
|
| 10 |
def load_users():
|
| 11 |
"""Load users from JSON file"""
|
|
|
|
| 74 |
'Nature',
|
| 75 |
'People',
|
| 76 |
'Transportation'
|
| 77 |
+
]
|
| 78 |
+
|
| 79 |
+
def calculate_statistics():
|
| 80 |
+
"""Calculate comprehensive real-time statistics for the analytics dashboard"""
|
| 81 |
+
users = load_users()
|
| 82 |
+
labels_data = load_labels()
|
| 83 |
+
|
| 84 |
+
# Basic counts
|
| 85 |
+
total_users = len(users)
|
| 86 |
+
total_images = len(labels_data)
|
| 87 |
+
|
| 88 |
+
# Count total labels across all images
|
| 89 |
+
total_labels = 0
|
| 90 |
+
languages_used = set()
|
| 91 |
+
language_breakdown = Counter()
|
| 92 |
+
category_breakdown = Counter()
|
| 93 |
+
|
| 94 |
+
# Location-based statistics
|
| 95 |
+
images_with_location = 0
|
| 96 |
+
gps_accuracy_breakdown = Counter()
|
| 97 |
+
location_methods = Counter()
|
| 98 |
+
country_breakdown = Counter()
|
| 99 |
+
city_breakdown = Counter()
|
| 100 |
+
|
| 101 |
+
for entry_data in labels_data.values():
|
| 102 |
+
# Count labels for this image
|
| 103 |
+
image_labels = entry_data.get('labels', [])
|
| 104 |
+
total_labels += len(image_labels)
|
| 105 |
+
|
| 106 |
+
# Track languages used
|
| 107 |
+
for label in image_labels:
|
| 108 |
+
lang = label.get('language', 'unknown')
|
| 109 |
+
languages_used.add(lang)
|
| 110 |
+
language_breakdown[lang] += 1
|
| 111 |
+
|
| 112 |
+
# Track categories
|
| 113 |
+
category = entry_data.get('category', 'Unknown')
|
| 114 |
+
category_breakdown[category] += 1
|
| 115 |
+
|
| 116 |
+
# Track location data
|
| 117 |
+
location = entry_data.get('location')
|
| 118 |
+
if location and location.get('latitude'):
|
| 119 |
+
images_with_location += 1
|
| 120 |
+
|
| 121 |
+
# Track location capture method
|
| 122 |
+
method = location.get('method', 'Unknown')
|
| 123 |
+
location_methods[method] += 1
|
| 124 |
+
|
| 125 |
+
# Track GPS accuracy levels
|
| 126 |
+
accuracy = location.get('accuracy')
|
| 127 |
+
if accuracy is not None:
|
| 128 |
+
if accuracy <= 10:
|
| 129 |
+
gps_accuracy_breakdown['High'] += 1
|
| 130 |
+
elif accuracy <= 50:
|
| 131 |
+
gps_accuracy_breakdown['Medium'] += 1
|
| 132 |
+
else:
|
| 133 |
+
gps_accuracy_breakdown['Low'] += 1
|
| 134 |
+
else:
|
| 135 |
+
gps_accuracy_breakdown['Unknown'] += 1
|
| 136 |
+
|
| 137 |
+
# Track countries and cities
|
| 138 |
+
country = location.get('country')
|
| 139 |
+
if country:
|
| 140 |
+
country_breakdown[country] += 1
|
| 141 |
+
|
| 142 |
+
city = location.get('city')
|
| 143 |
+
if city:
|
| 144 |
+
city_breakdown[city] += 1
|
| 145 |
+
|
| 146 |
+
return {
|
| 147 |
+
'total_users': total_users,
|
| 148 |
+
'total_images': total_images,
|
| 149 |
+
'total_labels': total_labels,
|
| 150 |
+
'languages_used': len(languages_used),
|
| 151 |
+
'language_breakdown': dict(language_breakdown),
|
| 152 |
+
'category_breakdown': dict(category_breakdown),
|
| 153 |
+
'images_with_location': images_with_location,
|
| 154 |
+
'gps_accuracy_breakdown': dict(gps_accuracy_breakdown),
|
| 155 |
+
'location_methods': dict(location_methods),
|
| 156 |
+
'country_breakdown': dict(country_breakdown),
|
| 157 |
+
'city_breakdown': dict(city_breakdown)
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
def get_location_accuracy_level(accuracy):
|
| 161 |
+
"""Determine location accuracy level based on accuracy value"""
|
| 162 |
+
if accuracy is None:
|
| 163 |
+
return "Unknown", "#95a5a6"
|
| 164 |
+
|
| 165 |
+
if accuracy <= 10:
|
| 166 |
+
return "High", "#27ae60"
|
| 167 |
+
elif accuracy <= 50:
|
| 168 |
+
return "Medium", "#f39c12"
|
| 169 |
+
else:
|
| 170 |
+
return "Low", "#e74c3c"
|
| 171 |
+
|
| 172 |
+
def format_location_display(location_data):
|
| 173 |
+
"""Format location data for display"""
|
| 174 |
+
if not location_data or not location_data.get('lat'):
|
| 175 |
+
return "📍 Location not available"
|
| 176 |
+
|
| 177 |
+
lat = location_data['lat']
|
| 178 |
+
lon = location_data['lon']
|
| 179 |
+
method = location_data.get('method', 'Unknown')
|
| 180 |
+
accuracy = location_data.get('accuracy')
|
| 181 |
+
|
| 182 |
+
# Method icon
|
| 183 |
+
method_icons = {
|
| 184 |
+
'GPS': '🛰️',
|
| 185 |
+
'IP': '🌐',
|
| 186 |
+
'Manual': '📝'
|
| 187 |
+
}
|
| 188 |
+
method_icon = method_icons.get(method, '📍')
|
| 189 |
+
|
| 190 |
+
# Base location string
|
| 191 |
+
location_str = f"{method_icon} {lat:.6f}, {lon:.6f}"
|
| 192 |
+
|
| 193 |
+
# Add accuracy if available
|
| 194 |
+
if accuracy:
|
| 195 |
+
level, _ = get_location_accuracy_level(accuracy)
|
| 196 |
+
location_str += f" (±{accuracy:.0f}m - {level} Accuracy)"
|
| 197 |
+
|
| 198 |
+
# Add city/country if available
|
| 199 |
+
if location_data.get('city') and location_data.get('country'):
|
| 200 |
+
location_str += f" - {location_data['city']}, {location_data['country']}"
|
| 201 |
+
|
| 202 |
+
return location_str
|
| 203 |
+
|
| 204 |
+
def validate_coordinates(lat, lon):
|
| 205 |
+
"""Validate latitude and longitude coordinates"""
|
| 206 |
+
try:
|
| 207 |
+
lat = float(lat)
|
| 208 |
+
lon = float(lon)
|
| 209 |
+
|
| 210 |
+
if -90 <= lat <= 90 and -180 <= lon <= 180:
|
| 211 |
+
return True, lat, lon
|
| 212 |
+
else:
|
| 213 |
+
return False, None, None
|
| 214 |
+
except (ValueError, TypeError):
|
| 215 |
+
return False, None, None
|
| 216 |
+
|
| 217 |
+
def get_user_contribution_stats(username):
|
| 218 |
+
"""Get contribution statistics for a specific user"""
|
| 219 |
+
labels_data = load_labels()
|
| 220 |
+
|
| 221 |
+
user_stats = {
|
| 222 |
+
'images_uploaded': 0,
|
| 223 |
+
'labels_added': 0,
|
| 224 |
+
'languages_contributed': set(),
|
| 225 |
+
'categories_contributed': set()
|
| 226 |
+
}
|
| 227 |
+
|
| 228 |
+
for entry_data in labels_data.values():
|
| 229 |
+
# Check if user uploaded this image
|
| 230 |
+
if entry_data.get('uploaded_by') == username:
|
| 231 |
+
user_stats['images_uploaded'] += 1
|
| 232 |
+
user_stats['categories_contributed'].add(entry_data.get('category', 'Unknown'))
|
| 233 |
+
|
| 234 |
+
# Check labels added by this user
|
| 235 |
+
for label in entry_data.get('labels', []):
|
| 236 |
+
if label.get('added_by') == username:
|
| 237 |
+
user_stats['labels_added'] += 1
|
| 238 |
+
user_stats['languages_contributed'].add(label.get('language', 'unknown'))
|
| 239 |
+
|
| 240 |
+
# Convert sets to counts
|
| 241 |
+
user_stats['languages_contributed'] = len(user_stats['languages_contributed'])
|
| 242 |
+
user_stats['categories_contributed'] = len(user_stats['categories_contributed'])
|
| 243 |
+
|
| 244 |
+
return user_stats
|