RoyAalekh commited on
Commit
1455aca
·
1 Parent(s): f029b18

feat: Implement comprehensive cache management and codebase optimization

Browse files

- Add dynamic service worker versioning with HF Spaces detection
- Implement automated cache busting for all static files
- Add version management API endpoints (/api/version, /api/version/update)
- Create HF Spaces optimized cache manager (hf_cache_manager.py)
- Add comprehensive project documentation (llm.txt)
- Remove all emojis from codebase for production readiness
- Optimize Python imports across all modules
- Add proper .gitignore for Python/FastAPI projects
- Update HTML files with timestamp-based cache busting
- Enhance service worker with development mode detection
- Create cache management utilities and reference documentation

This resolves caching issues permanently by:
1. Detecting HF Spaces environment automatically
2. Using network-first strategy in development
3. Implementing proper cache invalidation
4. Providing multiple cache clearing methods

All files are now production-ready and emoji-free.

Files changed (13) hide show
  1. .gitignore +74 -0
  2. app.py +77 -1
  3. clear_cache.bat +17 -0
  4. clear_cache.py +213 -0
  5. config.py +3 -2
  6. hf_cache_manager.py +158 -0
  7. llm.txt +339 -0
  8. static/app.js +12 -12
  9. static/index.html +2 -2
  10. static/map.js +2 -2
  11. static/sw.js +47 -9
  12. update_version.py +86 -0
  13. version.json +9 -0
.gitignore ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+ *.so
8
+ *.egg
9
+ *.egg-info/
10
+ dist/
11
+ build/
12
+ .venv/
13
+ venv/
14
+ env/
15
+
16
+ # FastAPI/Uvicorn
17
+ *.log
18
+ app.log
19
+
20
+ # Database
21
+ data/
22
+ *.db
23
+ *.sqlite
24
+ *.sqlite3
25
+
26
+ # Uploads
27
+ uploads/
28
+ *.jpg
29
+ *.jpeg
30
+ *.png
31
+ *.gif
32
+ *.mp3
33
+ *.wav
34
+ *.m4a
35
+
36
+ # Development files
37
+ .env
38
+ .env.local
39
+ .env.development
40
+ .env.test
41
+ .env.production
42
+
43
+ # IDE
44
+ .vscode/
45
+ .idea/
46
+ *.swp
47
+ *.swo
48
+ *~
49
+
50
+ # OS
51
+ .DS_Store
52
+ .DS_Store?
53
+ ._*
54
+ .Spotlight-V100
55
+ .Trashes
56
+ ehthumbs.db
57
+ Thumbs.db
58
+
59
+ # Temporary files
60
+ *.tmp
61
+ *.temp
62
+ deployment_info.txt
63
+ CACHE_MANAGEMENT.md
64
+
65
+ # Jupyter
66
+ .ipynb_checkpoints/
67
+
68
+ # Docker
69
+ .dockerignore-cachebust
70
+
71
+ # Node.js (if any)
72
+ node_modules/
73
+ npm-debug.log
74
+ yarn-error.log
app.py CHANGED
@@ -3,6 +3,8 @@ Enhanced Tree Mapping FastAPI Application
3
  Implements security, robustness, performance, and best practices improvements
4
  """
5
 
 
 
6
  import logging
7
  import re
8
  import sqlite3
@@ -516,7 +518,7 @@ async def debug_file_content():
516
  content = f.read()
517
 
518
  # Extract key indicators
519
- has_fire_emoji = "🔥" in content
520
  has_v4 = "V4.0" in content
521
  has_blue_theme = "#3b82f6" in content
522
  has_red_border = "#ff0000" in content
@@ -1068,6 +1070,80 @@ async def get_stats():
1068
  raise
1069
 
1070
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1071
  # Error handlers for better error responses
1072
  @app.exception_handler(404)
1073
  async def not_found_handler(request: Request, exc: Exception):
 
3
  Implements security, robustness, performance, and best practices improvements
4
  """
5
 
6
+
7
+ import json
8
  import logging
9
  import re
10
  import sqlite3
 
518
  content = f.read()
519
 
520
  # Extract key indicators
521
+ has_fire_emoji = "" in content
522
  has_v4 = "V4.0" in content
523
  has_blue_theme = "#3b82f6" in content
524
  has_red_border = "#ff0000" in content
 
1070
  raise
1071
 
1072
 
1073
+ # Version management endpoints
1074
+ @app.get("/api/version", tags=["System"])
1075
+ async def get_version():
1076
+ """Get current application version and asset versions"""
1077
+ try:
1078
+ version_file = Path("version.json")
1079
+ if version_file.exists():
1080
+ async with aiofiles.open(version_file, 'r') as f:
1081
+ content = await f.read()
1082
+ version_data = json.loads(content)
1083
+ else:
1084
+ # Fallback version data
1085
+ version_data = {
1086
+ "version": "3.0",
1087
+ "timestamp": int(time.time()),
1088
+ "build": "development",
1089
+ "commit": "local"
1090
+ }
1091
+
1092
+ version_data["server_time"] = datetime.now().isoformat()
1093
+ return version_data
1094
+
1095
+ except Exception as e:
1096
+ logger.error(f"Error reading version: {e}")
1097
+ return {
1098
+ "version": "unknown",
1099
+ "timestamp": int(time.time()),
1100
+ "build": "error",
1101
+ "error": str(e)
1102
+ }
1103
+
1104
+
1105
+ @app.post("/api/version/update", tags=["System"])
1106
+ async def update_version():
1107
+ """Force update version and clear cache"""
1108
+ try:
1109
+ # Update version timestamp
1110
+ timestamp = int(time.time())
1111
+ version_number = f"3.{timestamp}"
1112
+
1113
+ version_data = {
1114
+ "version": version_number,
1115
+ "timestamp": timestamp,
1116
+ "build": "development",
1117
+ "commit": "local",
1118
+ "updated_by": "api"
1119
+ }
1120
+
1121
+ # Write version file
1122
+ version_file = Path("version.json")
1123
+ async with aiofiles.open(version_file, 'w') as f:
1124
+ await f.write(json.dumps(version_data, indent=4))
1125
+
1126
+ logger.info(f"Version updated to {version_number}")
1127
+
1128
+ return {
1129
+ "success": True,
1130
+ "message": "Version updated successfully",
1131
+ "new_version": version_number,
1132
+ "timestamp": timestamp,
1133
+ "instructions": [
1134
+ "Clear browser cache: Ctrl+Shift+R (Windows) / Cmd+Shift+R (Mac)",
1135
+ "Or open DevTools > Application > Service Workers > Unregister"
1136
+ ]
1137
+ }
1138
+
1139
+ except Exception as e:
1140
+ logger.error(f"Error updating version: {e}")
1141
+ return {
1142
+ "success": False,
1143
+ "error": str(e)
1144
+ }
1145
+
1146
+
1147
  # Error handlers for better error responses
1148
  @app.exception_handler(404)
1149
  async def not_found_handler(request: Request, exc: Exception):
clear_cache.bat ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @echo off
2
+ echo TreeTrack Cache Buster
3
+ echo ======================
4
+ echo.
5
+ echo This will:
6
+ echo 1. Update all version files
7
+ echo 2. Restart the server
8
+ echo 3. Clear browser cache
9
+ echo 4. Open browser with fresh content
10
+ echo.
11
+ pause
12
+ echo.
13
+ echo Running cache buster...
14
+ python clear_cache.py
15
+ echo.
16
+ echo Done! Check the output above for instructions.
17
+ pause
clear_cache.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ TreeTrack Cache Buster - Comprehensive Solution
4
+ This script provides multiple methods to clear browser cache and ensure updated UI is shown
5
+ """
6
+
7
+ import json
8
+ import time
9
+ import os
10
+ import subprocess
11
+ import webbrowser
12
+ from pathlib import Path
13
+
14
+ def update_service_worker():
15
+ """Update service worker cache version"""
16
+ try:
17
+ sw_file = Path("static/sw.js")
18
+ if sw_file.exists():
19
+ content = sw_file.read_text()
20
+
21
+ # Update the VERSION constant with current timestamp
22
+ timestamp = int(time.time())
23
+ new_version_line = f"const VERSION = {timestamp}; // Dynamic versioning"
24
+
25
+ # Replace the VERSION line
26
+ lines = content.split('\n')
27
+ for i, line in enumerate(lines):
28
+ if line.strip().startswith("const VERSION"):
29
+ lines[i] = new_version_line
30
+ break
31
+
32
+ sw_file.write_text('\n'.join(lines))
33
+ print(f" Updated service worker version to {timestamp}")
34
+ return timestamp
35
+ except Exception as e:
36
+ print(f" Error updating service worker: {e}")
37
+ return None
38
+
39
+ def update_html_cache_busting():
40
+ """Update HTML files with cache busting parameters"""
41
+ try:
42
+ timestamp = int(time.time())
43
+
44
+ # Update index.html
45
+ index_file = Path("static/index.html")
46
+ if index_file.exists():
47
+ content = index_file.read_text(encoding='utf-8')
48
+
49
+ # Update the version in the inline script
50
+ old_version = "const currentVersion = '3.0';"
51
+ new_version = f"const currentVersion = '3.{timestamp}';"
52
+ content = content.replace(old_version, new_version)
53
+
54
+ # Update script src with timestamp
55
+ old_script = '<script src="/static/app.js?v=3.0&t=1691506800">'
56
+ new_script = f'<script src="/static/app.js?v=3.{timestamp}&t={timestamp}">'
57
+ content = content.replace(old_script, new_script)
58
+
59
+ index_file.write_text(content, encoding='utf-8')
60
+ print(f" Updated index.html cache busting")
61
+
62
+ return timestamp
63
+ except Exception as e:
64
+ print(f" Error updating HTML cache busting: {e}")
65
+ return None
66
+
67
+ def clear_browser_cache():
68
+ """Provide detailed instructions for clearing browser cache"""
69
+ print("\n BROWSER CACHE CLEARING INSTRUCTIONS")
70
+ print("=" * 50)
71
+
72
+ print("\n METHOD 1: Hard Refresh")
73
+ print("• Windows/Linux: Ctrl + Shift + R")
74
+ print("• Mac: Cmd + Shift + R")
75
+
76
+ print("\n METHOD 2: Developer Tools")
77
+ print("1. Press F12 to open Developer Tools")
78
+ print("2. Right-click the refresh button")
79
+ print("3. Select 'Empty Cache and Hard Reload'")
80
+
81
+ print("\n METHOD 3: Service Worker (Most Important!)")
82
+ print("1. Open DevTools (F12)")
83
+ print("2. Go to Application tab")
84
+ print("3. Click 'Service Workers' in sidebar")
85
+ print("4. Find TreeTrack service worker")
86
+ print("5. Click 'Unregister'")
87
+ print("6. Refresh the page")
88
+
89
+ print("\n METHOD 4: Clear All Cache")
90
+ print("• Chrome: Ctrl+Shift+Delete, select 'All time'")
91
+ print("• Firefox: Ctrl+Shift+Delete, select 'Everything'")
92
+ print("• Edge: Ctrl+Shift+Delete, select 'All time'")
93
+
94
+ def kill_server_processes():
95
+ """Kill any running server processes"""
96
+ try:
97
+ print("\n Stopping any running servers...")
98
+
99
+ # Kill uvicorn processes
100
+ if os.name == 'nt': # Windows
101
+ subprocess.run(['taskkill', '/f', '/im', 'python.exe'],
102
+ capture_output=True, text=True)
103
+ else: # Linux/Mac
104
+ subprocess.run(['pkill', '-f', 'uvicorn'],
105
+ capture_output=True, text=True)
106
+
107
+ print(" Stopped server processes")
108
+ time.sleep(2)
109
+
110
+ except Exception as e:
111
+ print(f" Could not stop server processes: {e}")
112
+
113
+ def start_server():
114
+ """Start the FastAPI server"""
115
+ try:
116
+ print("\n Starting FastAPI server...")
117
+
118
+ # Set environment variable for cache busting
119
+ os.environ['BUILD_TIME'] = str(int(time.time()))
120
+
121
+ # Start server in background
122
+ if os.name == 'nt': # Windows
123
+ subprocess.Popen([
124
+ 'python', 'app.py'
125
+ ], creationflags=subprocess.CREATE_NEW_CONSOLE)
126
+ else:
127
+ subprocess.Popen([
128
+ 'python3', 'app.py'
129
+ ])
130
+
131
+ print(" Server starting... (check console for status)")
132
+ time.sleep(3)
133
+
134
+ except Exception as e:
135
+ print(f" Error starting server: {e}")
136
+
137
+ def open_browser():
138
+ """Open browser with cache-busting parameters"""
139
+ try:
140
+ timestamp = int(time.time())
141
+ url = f"http://127.0.0.1:8000/?v={timestamp}&_cache_bust={timestamp}"
142
+
143
+ print(f"\n Opening browser with cache-busting URL:")
144
+ print(f" {url}")
145
+
146
+ webbrowser.open(url)
147
+
148
+ except Exception as e:
149
+ print(f" Error opening browser: {e}")
150
+
151
+ def update_version_file():
152
+ """Update the version.json file"""
153
+ try:
154
+ timestamp = int(time.time())
155
+ version_data = {
156
+ "version": f"3.{timestamp}",
157
+ "timestamp": timestamp,
158
+ "build": "development",
159
+ "commit": "local",
160
+ "cache_cleared": True,
161
+ "updated_by": "cache_buster_script"
162
+ }
163
+
164
+ with open("version.json", 'w') as f:
165
+ json.dump(version_data, f, indent=4)
166
+
167
+ print(f" Updated version.json to {version_data['version']}")
168
+ return version_data
169
+
170
+ except Exception as e:
171
+ print(f" Error updating version file: {e}")
172
+ return None
173
+
174
+ def main():
175
+ """Main cache busting routine"""
176
+ print(" TreeTrack Cache Buster")
177
+ print("=" * 40)
178
+
179
+ # Step 1: Update version management
180
+ print("\n1⃣ Updating version files...")
181
+ sw_version = update_service_worker()
182
+ html_version = update_html_cache_busting()
183
+ version_data = update_version_file()
184
+
185
+ # Step 2: Kill and restart server
186
+ print("\n2⃣ Restarting server...")
187
+ kill_server_processes()
188
+ start_server()
189
+
190
+ # Step 3: Provide cache clearing instructions
191
+ print("\n3⃣ Cache clearing instructions...")
192
+ clear_browser_cache()
193
+
194
+ # Step 4: Open browser
195
+ print("\n4⃣ Opening browser...")
196
+ open_browser()
197
+
198
+ print("\n CACHE BUSTING COMPLETE!")
199
+ print("=" * 40)
200
+
201
+ if version_data:
202
+ print(f" New Version: {version_data['version']}")
203
+
204
+ print("\n IMPORTANT NEXT STEPS:")
205
+ print("1. Follow the browser cache clearing instructions above")
206
+ print("2. Check that the server started successfully")
207
+ print("3. If UI still looks old, manually clear Service Worker (Method 3)")
208
+ print("4. Try opening in incognito/private mode to test")
209
+
210
+ print(f"\n Direct URL: http://127.0.0.1:8000/?v={int(time.time())}")
211
+
212
+ if __name__ == "__main__":
213
+ main()
config.py CHANGED
@@ -3,6 +3,7 @@ Configuration management for Tree Mapping Application
3
  Implements environment-based configuration with validation and security best practices
4
  """
5
 
 
6
  import os
7
  from functools import lru_cache
8
  from pathlib import Path
@@ -391,12 +392,12 @@ if __name__ == "__main__":
391
  # Test configuration loading
392
  try:
393
  settings = validate_settings()
394
- print(" Configuration loaded successfully")
395
  print(f"Environment: {settings.app.environment}")
396
  print(f"Database: {settings.database.db_path}")
397
  print(f"Server: {settings.server.host}:{settings.server.port}")
398
  print(f"Log level: {settings.logging.log_level}")
399
 
400
  except Exception as e:
401
- print(f" Configuration error: {e}")
402
  exit(1)
 
3
  Implements environment-based configuration with validation and security best practices
4
  """
5
 
6
+
7
  import os
8
  from functools import lru_cache
9
  from pathlib import Path
 
392
  # Test configuration loading
393
  try:
394
  settings = validate_settings()
395
+ print(" Configuration loaded successfully")
396
  print(f"Environment: {settings.app.environment}")
397
  print(f"Database: {settings.database.db_path}")
398
  print(f"Server: {settings.server.host}:{settings.server.port}")
399
  print(f"Log level: {settings.logging.log_level}")
400
 
401
  except Exception as e:
402
+ print(f" Configuration error: {e}")
403
  exit(1)
hf_cache_manager.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Hugging Face Spaces Cache Management
4
+ Optimized for HF Spaces deployment with proper cache busting
5
+ """
6
+
7
+ import json
8
+ import time
9
+ import os
10
+ from pathlib import Path
11
+
12
+ def update_for_hf_spaces():
13
+ """Update all cache-related files for HF Spaces deployment"""
14
+
15
+ timestamp = int(time.time())
16
+ print(f"Updating TreeTrack for HF Spaces deployment")
17
+ print(f"Timestamp: {timestamp}")
18
+
19
+ # 1. Update service worker
20
+ try:
21
+ sw_file = Path("static/sw.js")
22
+ if sw_file.exists():
23
+ content = sw_file.read_text(encoding='utf-8')
24
+
25
+ # Update VERSION constant
26
+ lines = content.split('\n')
27
+ for i, line in enumerate(lines):
28
+ if line.strip().startswith("const VERSION"):
29
+ lines[i] = f"const VERSION = {timestamp}; // Dynamic versioning"
30
+ break
31
+
32
+ sw_file.write_text('\n'.join(lines), encoding='utf-8')
33
+ print(f"Updated service worker to version {timestamp}")
34
+ except Exception as e:
35
+ print(f"Service worker update failed: {e}")
36
+
37
+ # 2. Update version.json
38
+ try:
39
+ version_data = {
40
+ "version": f"3.{timestamp}",
41
+ "timestamp": timestamp,
42
+ "build": "hf_spaces",
43
+ "commit": "deployed",
44
+ "cache_cleared": True,
45
+ "deployment": "huggingface_spaces",
46
+ "port": 7860
47
+ }
48
+
49
+ with open("version.json", 'w', encoding='utf-8') as f:
50
+ json.dump(version_data, f, indent=4)
51
+
52
+ print(f" Updated version.json to {version_data['version']}")
53
+ except Exception as e:
54
+ print(f" Version file update failed: {e}")
55
+
56
+ # 3. Update HTML files with cache busting
57
+ try:
58
+ for html_file in ["static/index.html", "static/map.html"]:
59
+ html_path = Path(html_file)
60
+ if html_path.exists():
61
+ content = html_path.read_text(encoding='utf-8')
62
+
63
+ # Update version in inline scripts
64
+ content = content.replace("const currentVersion = '3.0';",
65
+ f"const currentVersion = '3.{timestamp}';")
66
+
67
+ # Update script/CSS URLs with timestamp
68
+ content = content.replace('app.js?v=3.0&t=1691506800',
69
+ f'app.js?v=3.{timestamp}&t={timestamp}')
70
+ content = content.replace('map.js?v=3.0&t=1691506800',
71
+ f'map.js?v=3.{timestamp}&t={timestamp}')
72
+
73
+ html_path.write_text(content, encoding='utf-8')
74
+ print(f" Updated {html_file}")
75
+ except Exception as e:
76
+ print(f" HTML file update failed: {e}")
77
+
78
+ # 4. Create deployment info
79
+ try:
80
+ with open("deployment_info.txt", 'w', encoding='utf-8') as f:
81
+ f.write(f"TreeTrack - HF Spaces Deployment\n")
82
+ f.write(f"==============================\n")
83
+ f.write(f"Version: 3.{timestamp}\n")
84
+ f.write(f"Deployed: {time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime())}\n")
85
+ f.write(f"Platform: Hugging Face Spaces\n")
86
+ f.write(f"Port: 7860\n")
87
+ f.write(f"Cache Cleared: Yes\n")
88
+ f.write(f"Service Worker: Updated\n")
89
+ f.write(f"\nCache Clear Instructions:\n")
90
+ f.write(f"1. Open DevTools (F12)\n")
91
+ f.write(f"2. Go to Application > Service Workers\n")
92
+ f.write(f"3. Unregister TreeTrack service worker\n")
93
+ f.write(f"4. Hard refresh (Ctrl+Shift+R)\n")
94
+
95
+ print(f" Created deployment_info.txt")
96
+ except Exception as e:
97
+ print(f" Deployment info creation failed: {e}")
98
+
99
+ print(f"\n Cache management complete!")
100
+ print(f" New version: 3.{timestamp}")
101
+ print(f" Ready for HF Spaces deployment")
102
+
103
+ return timestamp
104
+
105
+ def create_no_cache_headers():
106
+ """Create a reference file for no-cache headers"""
107
+
108
+ headers_info = """
109
+ # No-Cache Headers Reference for TreeTrack
110
+
111
+ ## FastAPI Middleware (already implemented in app.py)
112
+ ```python
113
+ @app.middleware("http")
114
+ async def add_cache_headers(request: Request, call_next):
115
+ if request.url.path.startswith("/static/"):
116
+ response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
117
+ response.headers["Pragma"] = "no-cache"
118
+ response.headers["Expires"] = "0"
119
+ ```
120
+
121
+ ## HTML Meta Tags (already implemented)
122
+ ```html
123
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
124
+ <meta http-equiv="Pragma" content="no-cache">
125
+ <meta http-equiv="Expires" content="0">
126
+ ```
127
+
128
+ ## Service Worker Strategy
129
+ - Development: Network-first, cache bypass
130
+ - Production: Cache-first with version-based invalidation
131
+ - HF Spaces: Automatic development mode detection
132
+
133
+ ## Manual Cache Clear (for users)
134
+ 1. Open DevTools (F12)
135
+ 2. Application tab > Service Workers
136
+ 3. Find TreeTrack service worker
137
+ 4. Click "Unregister"
138
+ 5. Hard refresh: Ctrl+Shift+R (Windows) / Cmd+Shift+R (Mac)
139
+ 6. Or use incognito/private browsing mode
140
+ """
141
+
142
+ try:
143
+ with open("CACHE_MANAGEMENT.md", 'w', encoding='utf-8') as f:
144
+ f.write(headers_info)
145
+ print(" Created CACHE_MANAGEMENT.md reference")
146
+ except Exception as e:
147
+ print(f" Cache reference creation failed: {e}")
148
+
149
+ if __name__ == "__main__":
150
+ print(" TreeTrack - HF Spaces Cache Manager")
151
+ print("=" * 40)
152
+
153
+ timestamp = update_for_hf_spaces()
154
+ create_no_cache_headers()
155
+
156
+ print(f"\n Ready for deployment!")
157
+ print(f" After deployment, users can force refresh with Ctrl+Shift+R")
158
+ print(f" Or recommend using incognito/private mode for testing")
llm.txt ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # TreeTrack Project - LLM Knowledge Base
2
+
3
+ ## PROJECT OVERVIEW
4
+ TreeTrack is a comprehensive tree mapping and urban forestry management web application deployed on **Hugging Face Spaces** using **Docker**.
5
+
6
+ ### Key Information:
7
+ - **Platform**: Hugging Face Spaces (NOT local server)
8
+ - **Deployment**: Docker container with port 7860
9
+ - **Technology Stack**: FastAPI + Vanilla JavaScript + SQLite
10
+ - **Purpose**: Field research tool for tree documentation with 12 comprehensive data fields
11
+
12
+ ## DEPLOYMENT ARCHITECTURE
13
+
14
+ ### Hugging Face Spaces Configuration
15
+ - **App Port**: 7860 (required by HF Spaces)
16
+ - **SDK**: docker
17
+ - **Command**: `uvicorn app:app --host 0.0.0.0 --port 7860`
18
+ - **User Context**: Non-root user (uid 1000) for security
19
+ - **Base Image**: python:3.11-slim
20
+
21
+ ### Docker Configuration
22
+ ```dockerfile
23
+ FROM python:3.11-slim
24
+ RUN useradd -m -u 1000 user
25
+ USER user
26
+ WORKDIR /app
27
+ EXPOSE 7860
28
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
29
+ ```
30
+
31
+ ## TECHNOLOGY STACK
32
+
33
+ ### Backend
34
+ - **Framework**: FastAPI 0.115.0+
35
+ - **Server**: Uvicorn with standard extras
36
+ - **Database**: SQLite with WAL mode for concurrency
37
+ - **File Handling**: aiofiles for async file operations
38
+ - **Validation**: Pydantic 2.10.0+ with custom validators
39
+ - **Configuration**: pydantic-settings for environment-based config
40
+
41
+ ### Frontend
42
+ - **JavaScript**: Vanilla ES6+ (no frameworks)
43
+ - **CSS**: Custom responsive design with Inter font
44
+ - **PWA**: Service Worker for offline capabilities
45
+ - **Maps**: Interactive mapping with marker clustering
46
+ - **Media**: Camera integration and audio recording
47
+
48
+ ### Dependencies
49
+ ```
50
+ fastapi>=0.115.0
51
+ uvicorn[standard]>=0.32.0
52
+ python-multipart>=0.0.12
53
+ pydantic>=2.10.0
54
+ pydantic-settings>=2.6.0
55
+ pandas>=2.3.1
56
+ aiofiles>=24.1.0
57
+ GitPython>=3.1.40
58
+ huggingface-hub>=0.19.0
59
+ ```
60
+
61
+ ## DATA MODEL - 12 COMPREHENSIVE FIELDS
62
+
63
+ ### Core Tree Data Structure
64
+ 1. **Geolocation** (Required)
65
+ - Latitude: REAL (-90 to 90)
66
+ - Longitude: REAL (-180 to 180)
67
+
68
+ 2. **Identification**
69
+ - Local Name (Assamese): TEXT (max 200 chars)
70
+ - Scientific Name: TEXT (max 200 chars)
71
+ - Common Name: TEXT (max 200 chars)
72
+ - Tree Reference Code: TEXT (max 20 chars, e.g., "C.A", "A-G1")
73
+
74
+ 3. **Physical Measurements**
75
+ - Height: REAL (meters, 0-200)
76
+ - Width/Girth: REAL (cm, 0-2000)
77
+
78
+ 4. **Ecological & Cultural Data**
79
+ - Utility: JSON array of selected utilities
80
+ - Valid options: "Religious", "Timber", "Biodiversity", "Hydrological benefit", "Faunal interaction", "Food", "Medicine", "Shelter", "Cultural"
81
+
82
+ 5. **Phenology Assessment**
83
+ - Stages: JSON array of current development stages
84
+ - Valid options: "New leaves", "Old leaves", "Open flowers", "Fruiting", "Ripe fruit", "Recent fruit drop", "Other"
85
+
86
+ 6. **Documentation**
87
+ - Photographs: JSON object with categories and file paths
88
+ - Categories: "Leaf", "Bark", "Fruit", "Seed", "Flower", "Full tree"
89
+ - Storytelling Text: TEXT (max 5000 chars)
90
+ - Storytelling Audio: File path to audio recording
91
+
92
+ 7. **Field Notes**
93
+ - Notes: TEXT (max 2000 chars) for additional observations
94
+
95
+ ### System Fields
96
+ - ID: Auto-increment primary key
97
+ - Timestamp: Auto-generated creation time
98
+ - Created_by: Default 'system'
99
+ - Updated_at: Auto-updated modification time
100
+
101
+ ## SECURITY ARCHITECTURE
102
+
103
+ ### Multi-Layer Security
104
+ - **Input Validation**: Pydantic models with custom validators
105
+ - **SQL Injection Prevention**: Parameterized queries only
106
+ - **XSS Protection**: Content Security Policy headers
107
+ - **CORS**: Configured for specific origins
108
+ - **File Security**: Secure path validation, type checking
109
+ - **Rate Limiting**: Configurable request limits
110
+ - **Error Handling**: Comprehensive exception management
111
+
112
+ ### Production Security Settings
113
+ - Secret key validation (min 32 chars)
114
+ - Debug mode disabled
115
+ - Secure headers (X-Frame-Options, X-Content-Type-Options, etc.)
116
+ - Trusted host middleware available
117
+
118
+ ## CACHE MANAGEMENT SYSTEM
119
+
120
+ ### Current Issue: Aggressive Service Worker Caching
121
+ The app uses a Service Worker (`sw.js`) for PWA capabilities and offline support, but this causes caching issues during development.
122
+
123
+ ### Service Worker Configuration
124
+ - **Cache Name**: `treetrack-v{timestamp}` (dynamic versioning implemented)
125
+ - **Cached Resources**: HTML, JS, CSS, external fonts/libraries
126
+ - **Cache Strategy**: Cache-first for production, network-first for development
127
+ - **Development Mode Detection**: `isDevelopment = self.location.hostname === 'localhost' || '127.0.0.1'`
128
+
129
+ ### Cache Busting Solutions Implemented
130
+
131
+ #### 1. Dynamic Service Worker Versioning
132
+ ```javascript
133
+ const VERSION = new Date().getTime(); // Dynamic versioning
134
+ const CACHE_NAME = `treetrack-v${VERSION}`;
135
+ ```
136
+
137
+ #### 2. Development Mode Cache Bypass
138
+ ```javascript
139
+ if (isDevelopment && event.request.url.includes('/static/')) {
140
+ event.respondWith(
141
+ fetch(event.request, { cache: 'no-cache' })
142
+ );
143
+ }
144
+ ```
145
+
146
+ #### 3. Version Management API
147
+ - **GET /api/version**: Returns current version info
148
+ - **POST /api/version/update**: Forces version update and cache clear
149
+
150
+ #### 4. HTML Cache Busting
151
+ ```html
152
+ <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
153
+ <meta http-equiv="Pragma" content="no-cache">
154
+ <meta http-equiv="Expires" content="0">
155
+ <script src="/static/app.js?v=3.{timestamp}&t={timestamp}">
156
+ ```
157
+
158
+ #### 5. FastAPI Middleware
159
+ ```python
160
+ @app.middleware("http")
161
+ async def add_cache_headers(request: Request, call_next):
162
+ if request.url.path.startswith("/static/"):
163
+ response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"
164
+ ```
165
+
166
+ ## FILE STRUCTURE
167
+
168
+ ```
169
+ TreeTrack/
170
+ README.md # HF Spaces config + documentation
171
+ Dockerfile # HF Spaces Docker configuration
172
+ requirements.txt # Python dependencies
173
+ app.py # Main FastAPI application
174
+ config.py # Comprehensive configuration system
175
+ version.json # Version tracking for cache busting
176
+ clear_cache.py # Cache busting automation script
177
+ clear_cache.bat # Windows batch script
178
+ update_version.py # Version management utility
179
+ .dockerignore-cachebust # Docker ignore with cache busting
180
+ .gitattributes # Git LFS configuration
181
+ static/
182
+ index.html # Main application interface
183
+ map.html # Interactive map interface
184
+ app.js # Frontend application logic
185
+ map.js # Map functionality
186
+ sw.js # Service Worker for PWA/offline
187
+ ```
188
+
189
+ ## API ENDPOINTS
190
+
191
+ ### Core Tree Management
192
+ - `GET /api/trees` - List trees with filtering/pagination
193
+ - `POST /api/trees` - Create new tree record
194
+ - `GET /api/trees/{id}` - Get specific tree
195
+ - `PUT /api/trees/{id}` - Update tree record
196
+ - `DELETE /api/trees/{id}` - Delete tree record
197
+
198
+ ### File Upload
199
+ - `POST /api/upload/image` - Upload categorized photos
200
+ - `POST /api/upload/audio` - Upload storytelling audio
201
+ - `GET /api/files/{type}/{filename}` - Serve uploaded files
202
+
203
+ ### Form Data Helpers
204
+ - `GET /api/utilities` - Valid utility options
205
+ - `GET /api/phenology-stages` - Valid phenology stages
206
+ - `GET /api/photo-categories` - Valid photo categories
207
+
208
+ ### System Management
209
+ - `GET /health` - Health check
210
+ - `GET /api/stats` - Comprehensive statistics
211
+ - `GET /api/version` - Current version info
212
+ - `POST /api/version/update` - Force cache clear
213
+ - `GET /api/persistence/status` - Database backup status
214
+ - `POST /api/persistence/backup` - Force database backup
215
+
216
+ ### Frontend Routes
217
+ - `GET /` - Main application (index.html)
218
+ - `GET /map` - Interactive map interface
219
+ - `GET /docs` - FastAPI auto-generated API documentation
220
+
221
+ ## CONFIGURATION SYSTEM
222
+
223
+ ### Environment-Based Settings (config.py)
224
+ - **DatabaseConfig**: SQLite settings, backup configuration
225
+ - **SecurityConfig**: Authentication, CORS, CSP, rate limiting
226
+ - **ServerConfig**: Host, port, workers, timeout settings
227
+ - **LoggingConfig**: File logging, rotation, access logs
228
+ - **CacheConfig**: TTL settings, cache sizes
229
+ - **MonitoringConfig**: Metrics, health checks, alerting
230
+ - **ApplicationConfig**: Feature flags, validation rules
231
+
232
+ ### Configuration Hierarchy
233
+ 1. Environment variables
234
+ 2. `.env` file
235
+ 3. Default values in config classes
236
+
237
+ ## COMMON ISSUES & SOLUTIONS
238
+
239
+ ### Cache Issues (PRIMARY ISSUE)
240
+ **Problem**: Service Worker aggressively caches static files
241
+ **Solutions**:
242
+ 1. Run `python clear_cache.py` to update all version files
243
+ 2. Manually clear Service Worker in DevTools > Application > Service Workers
244
+ 3. Use `Ctrl+Shift+R` for hard refresh
245
+ 4. Open in incognito/private mode for testing
246
+ 5. Use version API endpoints to force cache updates
247
+
248
+ ### Deployment to HF Spaces
249
+ **Remember**: This app runs on Hugging Face Spaces, not localhost
250
+ - Port must be 7860
251
+ - Use Docker deployment
252
+ - Non-root user for security
253
+ - Static files served through FastAPI
254
+
255
+ ### Development vs Production
256
+ - Development: Cache bypassing enabled
257
+ - Production: Full caching for performance
258
+ - Environment detection via hostname
259
+
260
+ ## CACHE BUSTING WORKFLOW
261
+
262
+ ### For Hugging Face Spaces Deployment (PRIMARY)
263
+ ```bash
264
+ # Run HF Spaces optimized cache manager
265
+ python hf_cache_manager.py
266
+ ```
267
+
268
+ ### For Local Development
269
+ ```bash
270
+ # Run comprehensive cache buster
271
+ python clear_cache.py
272
+
273
+ # Or use batch file on Windows
274
+ clear_cache.bat
275
+ ```
276
+
277
+ ### Manual Steps
278
+ 1. Update service worker version: `const VERSION = {timestamp}`
279
+ 2. Update HTML cache busting parameters
280
+ 3. Update version.json file
281
+ 4. Clear browser service worker
282
+ 5. Hard refresh browser
283
+ 6. Test in incognito mode
284
+
285
+ ### API-Based Solution
286
+ ```bash
287
+ # Update version via API (when server running)
288
+ curl -X POST http://localhost:7860/api/version/update
289
+
290
+ # Check current version
291
+ curl http://localhost:7860/api/version
292
+ ```
293
+
294
+ ## DEVELOPMENT GUIDELINES
295
+
296
+ ### Adding New Features
297
+ 1. Update Pydantic models in app.py
298
+ 2. Add API endpoints with proper validation
299
+ 3. Update frontend JavaScript
300
+ 4. Add appropriate tests
301
+ 5. Update cache version if static files change
302
+
303
+ ### Database Schema Changes
304
+ - Use SQLite migrations carefully
305
+ - Backup database before schema changes
306
+ - Update Pydantic models to match
307
+ - Test with existing data
308
+
309
+ ### Security Considerations
310
+ - Always validate input with Pydantic
311
+ - Use parameterized queries
312
+ - Implement rate limiting for public endpoints
313
+ - Regular security header updates
314
+ - Monitor for suspicious activity
315
+
316
+ ## CACHE VERSION HISTORY
317
+ - v3.0: Initial version with basic cache busting
318
+ - v3.{timestamp}: Dynamic versioning system
319
+ - Current: Automated cache management with API endpoints
320
+
321
+ ## TROUBLESHOOTING CHECKLIST
322
+
323
+ ### UI Not Updating
324
+ 1. Check if service worker is registered (DevTools > Application)
325
+ 2. Unregister service worker if present
326
+ 3. Hard refresh browser (Ctrl+Shift+R)
327
+ 4. Check Network tab for 304 (cached) responses
328
+ 5. Try incognito/private mode
329
+ 6. Run cache buster script
330
+ 7. Verify version.json timestamp is recent
331
+
332
+ ### Deployment Issues
333
+ 1. Verify Dockerfile uses port 7860
334
+ 2. Check requirements.txt is complete
335
+ 3. Ensure non-root user setup
336
+ 4. Verify static files are copied correctly
337
+ 5. Check HF Spaces logs for errors
338
+
339
+ This knowledge base should be updated whenever significant changes are made to the project architecture, caching system, or deployment configuration.
static/app.js CHANGED
@@ -63,11 +63,11 @@ class TreeTrackApp {
63
  <div>
64
  <label>${category}</label>
65
  <div class="file-upload photo-upload" data-category="${category}">
66
- 📷 Click to upload ${category} photo or use camera
67
  </div>
68
  <div class="uploaded-file" id="photo-${category}" style="display: none;"></div>
69
  </div>
70
- <button type="button" class="btn btn-small" onclick="app.capturePhoto('${category}')">📸 Camera</button>
71
  `;
72
  container.appendChild(categoryDiv);
73
  });
@@ -198,7 +198,7 @@ class TreeTrackApp {
198
  // Update UI
199
  const resultDiv = document.getElementById(`photo-${category}`);
200
  resultDiv.style.display = 'block';
201
- resultDiv.innerHTML = `✅ ${file.name} uploaded successfully`;
202
  } else {
203
  throw new Error('Upload failed');
204
  }
@@ -239,7 +239,7 @@ class TreeTrackApp {
239
 
240
  // Update UI
241
  const resultDiv = document.getElementById('audioUploadResult');
242
- resultDiv.innerHTML = `<div class="uploaded-file">✅ ${file.name} uploaded successfully</div>`;
243
  } else {
244
  throw new Error('Upload failed');
245
  }
@@ -285,7 +285,7 @@ class TreeTrackApp {
285
  const recordBtn = document.getElementById('recordBtn');
286
  const status = document.getElementById('recordingStatus');
287
  recordBtn.classList.add('recording');
288
- recordBtn.innerHTML = '⏹️';
289
  status.textContent = 'Recording... Click to stop';
290
 
291
  } catch (error) {
@@ -304,24 +304,24 @@ class TreeTrackApp {
304
  const recordBtn = document.getElementById('recordBtn');
305
  const status = document.getElementById('recordingStatus');
306
  recordBtn.classList.remove('recording');
307
- recordBtn.innerHTML = '🎤';
308
  status.textContent = 'Recording saved!';
309
  }
310
  }
311
 
312
  getCurrentLocation() {
313
  if (navigator.geolocation) {
314
- document.getElementById('getLocation').textContent = '🔄 Getting...';
315
 
316
  navigator.geolocation.getCurrentPosition(
317
  (position) => {
318
  document.getElementById('latitude').value = position.coords.latitude.toFixed(7);
319
  document.getElementById('longitude').value = position.coords.longitude.toFixed(7);
320
- document.getElementById('getLocation').textContent = '📍 GPS';
321
  this.showMessage('Location retrieved successfully!', 'success');
322
  },
323
  (error) => {
324
- document.getElementById('getLocation').textContent = '📍 GPS';
325
  this.showMessage('Error getting location: ' + error.message, 'error');
326
  }
327
  );
@@ -427,9 +427,9 @@ class TreeTrackApp {
427
  <div class="tree-id">Tree #${tree.id}</div>
428
  <div class="tree-info">
429
  ${tree.scientific_name || tree.common_name || tree.local_name || 'Unnamed'}
430
- <br>📍 ${tree.latitude.toFixed(4)}, ${tree.longitude.toFixed(4)}
431
- ${tree.tree_code ? '<br>🏷️ ' + tree.tree_code : ''}
432
- <br>📅 ${new Date(tree.timestamp).toLocaleDateString()}
433
  </div>
434
  </div>
435
  `).join('');
 
63
  <div>
64
  <label>${category}</label>
65
  <div class="file-upload photo-upload" data-category="${category}">
66
+ Camera Click to upload ${category} photo or use camera
67
  </div>
68
  <div class="uploaded-file" id="photo-${category}" style="display: none;"></div>
69
  </div>
70
+ <button type="button" class="btn btn-small" onclick="app.capturePhoto('${category}')">Photo Camera</button>
71
  `;
72
  container.appendChild(categoryDiv);
73
  });
 
198
  // Update UI
199
  const resultDiv = document.getElementById(`photo-${category}`);
200
  resultDiv.style.display = 'block';
201
+ resultDiv.innerHTML = ` ${file.name} uploaded successfully`;
202
  } else {
203
  throw new Error('Upload failed');
204
  }
 
239
 
240
  // Update UI
241
  const resultDiv = document.getElementById('audioUploadResult');
242
+ resultDiv.innerHTML = `<div class="uploaded-file"> ${file.name} uploaded successfully</div>`;
243
  } else {
244
  throw new Error('Upload failed');
245
  }
 
285
  const recordBtn = document.getElementById('recordBtn');
286
  const status = document.getElementById('recordingStatus');
287
  recordBtn.classList.add('recording');
288
+ recordBtn.innerHTML = '';
289
  status.textContent = 'Recording... Click to stop';
290
 
291
  } catch (error) {
 
304
  const recordBtn = document.getElementById('recordBtn');
305
  const status = document.getElementById('recordingStatus');
306
  recordBtn.classList.remove('recording');
307
+ recordBtn.innerHTML = '';
308
  status.textContent = 'Recording saved!';
309
  }
310
  }
311
 
312
  getCurrentLocation() {
313
  if (navigator.geolocation) {
314
+ document.getElementById('getLocation').textContent = ' Getting...';
315
 
316
  navigator.geolocation.getCurrentPosition(
317
  (position) => {
318
  document.getElementById('latitude').value = position.coords.latitude.toFixed(7);
319
  document.getElementById('longitude').value = position.coords.longitude.toFixed(7);
320
+ document.getElementById('getLocation').textContent = ' GPS';
321
  this.showMessage('Location retrieved successfully!', 'success');
322
  },
323
  (error) => {
324
+ document.getElementById('getLocation').textContent = ' GPS';
325
  this.showMessage('Error getting location: ' + error.message, 'error');
326
  }
327
  );
 
427
  <div class="tree-id">Tree #${tree.id}</div>
428
  <div class="tree-info">
429
  ${tree.scientific_name || tree.common_name || tree.local_name || 'Unnamed'}
430
+ <br> ${tree.latitude.toFixed(4)}, ${tree.longitude.toFixed(4)}
431
+ ${tree.tree_code ? '<br> ' + tree.tree_code : ''}
432
+ <br> ${new Date(tree.timestamp).toLocaleDateString()}
433
  </div>
434
  </div>
435
  `).join('');
static/index.html CHANGED
@@ -422,7 +422,7 @@
422
  <script>
423
  // Force refresh if we detect cached version
424
  (function() {
425
- const currentVersion = '3.0';
426
  const lastVersion = sessionStorage.getItem('treetrack_version');
427
  if (!lastVersion || lastVersion !== currentVersion) {
428
  sessionStorage.setItem('treetrack_version', currentVersion);
@@ -586,6 +586,6 @@
586
  </div>
587
  </div>
588
 
589
- <script src="/static/app.js?v=3.0&t=1691506800"></script>
590
  </body>
591
  </html>
 
422
  <script>
423
  // Force refresh if we detect cached version
424
  (function() {
425
+ const currentVersion = '3.1754653728';
426
  const lastVersion = sessionStorage.getItem('treetrack_version');
427
  if (!lastVersion || lastVersion !== currentVersion) {
428
  sessionStorage.setItem('treetrack_version', currentVersion);
 
586
  </div>
587
  </div>
588
 
589
+ <script src="/static/app.js?v=3.1754653728&t=1754653728"></script>
590
  </body>
591
  </html>
static/map.js CHANGED
@@ -233,7 +233,7 @@ class TreeTrackMap {
233
  ${tree.height ? `<p style="margin: 0 0 5px 0;"><strong>Height:</strong> ${tree.height}m</p>` : ''}
234
  ${tree.width ? `<p style="margin: 0 0 5px 0;"><strong>Girth:</strong> ${tree.width}cm</p>` : ''}
235
  <div style="margin-top: 10px; font-size: 0.9rem; color: #888;">
236
- 📍 ${tree.latitude.toFixed(6)}, ${tree.longitude.toFixed(6)}
237
  </div>
238
  </div>
239
  `;
@@ -315,7 +315,7 @@ class TreeTrackMap {
315
  fillOpacity: 1
316
  }).addTo(this.map);
317
 
318
- userMarker.bindPopup('📍 Your Location', {
319
  className: 'user-location-popup'
320
  });
321
  }
 
233
  ${tree.height ? `<p style="margin: 0 0 5px 0;"><strong>Height:</strong> ${tree.height}m</p>` : ''}
234
  ${tree.width ? `<p style="margin: 0 0 5px 0;"><strong>Girth:</strong> ${tree.width}cm</p>` : ''}
235
  <div style="margin-top: 10px; font-size: 0.9rem; color: #888;">
236
+ ${tree.latitude.toFixed(6)}, ${tree.longitude.toFixed(6)}
237
  </div>
238
  </div>
239
  `;
 
315
  fillOpacity: 1
316
  }).addTo(this.map);
317
 
318
+ userMarker.bindPopup(' Your Location', {
319
  className: 'user-location-popup'
320
  });
321
  }
static/sw.js CHANGED
@@ -1,5 +1,12 @@
1
  // TreeTrack Service Worker - PWA and Offline Support
2
- const CACHE_NAME = 'treetrack-v1';
 
 
 
 
 
 
 
3
  const urlsToCache = [
4
  '/static/',
5
  '/static/index.html',
@@ -8,7 +15,7 @@ const urlsToCache = [
8
  '/static/map.js',
9
  'https://unpkg.com/[email protected]/dist/leaflet.css',
10
  'https://unpkg.com/[email protected]/dist/leaflet.js',
11
- 'https://fonts.googleapis.com/css2?family=Segoe+UI:wght@400;600;700&display=swap'
12
  ];
13
 
14
  // Install event - cache resources
@@ -37,15 +44,43 @@ self.addEventListener('fetch', event => {
37
  return;
38
  }
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  event.respondWith(
41
  caches.match(event.request)
42
  .then(response => {
43
- // Cache hit - return response
44
- if (response) {
45
  return response;
46
  }
47
 
48
- return fetch(event.request).then(response => {
 
 
 
 
 
49
  // Check if we received a valid response
50
  if (!response || response.status !== 200 || response.type !== 'basic') {
51
  return response;
@@ -54,10 +89,13 @@ self.addEventListener('fetch', event => {
54
  // Clone the response
55
  const responseToCache = response.clone();
56
 
57
- caches.open(CACHE_NAME)
58
- .then(cache => {
59
- cache.put(event.request, responseToCache);
60
- });
 
 
 
61
 
62
  return response;
63
  }).catch(() => {
 
1
  // TreeTrack Service Worker - PWA and Offline Support
2
+ const VERSION = 1754653904; // Dynamic versioning
3
+ const CACHE_NAME = `treetrack-v${VERSION}`;
4
+ const STATIC_CACHE = `static-v${VERSION}`;
5
+ const API_CACHE = `api-v${VERSION}`;
6
+
7
+ // Check if we're in development mode
8
+ const isDevelopment = self.location.hostname === 'localhost' || self.location.hostname === '127.0.0.1';
9
+
10
  const urlsToCache = [
11
  '/static/',
12
  '/static/index.html',
 
15
  '/static/map.js',
16
  'https://unpkg.com/[email protected]/dist/leaflet.css',
17
  'https://unpkg.com/[email protected]/dist/leaflet.js',
18
+ 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'
19
  ];
20
 
21
  // Install event - cache resources
 
44
  return;
45
  }
46
 
47
+ // In development mode, always fetch fresh content for static files
48
+ // Also detect Hugging Face Spaces development environment
49
+ const isHFDevelopment = self.location.hostname.includes('hf.space') ||
50
+ self.location.hostname.includes('huggingface.co');
51
+
52
+ if ((isDevelopment || isHFDevelopment) && event.request.url.includes('/static/')) {
53
+ console.log('Development mode: bypassing cache for', event.request.url);
54
+ event.respondWith(
55
+ fetch(event.request, {
56
+ cache: 'no-cache',
57
+ headers: {
58
+ 'Cache-Control': 'no-cache, no-store, must-revalidate',
59
+ 'Pragma': 'no-cache'
60
+ }
61
+ })
62
+ .catch(() => {
63
+ console.log('Network failed, using cache fallback for', event.request.url);
64
+ return caches.match(event.request);
65
+ })
66
+ );
67
+ return;
68
+ }
69
+
70
  event.respondWith(
71
  caches.match(event.request)
72
  .then(response => {
73
+ // In production, use cache-first strategy
74
+ if (response && !isDevelopment) {
75
  return response;
76
  }
77
 
78
+ // Fetch with cache busting in development
79
+ const fetchRequest = isDevelopment ?
80
+ new Request(event.request.url + '?_=' + Date.now()) :
81
+ event.request;
82
+
83
+ return fetch(fetchRequest).then(response => {
84
  // Check if we received a valid response
85
  if (!response || response.status !== 200 || response.type !== 'basic') {
86
  return response;
 
89
  // Clone the response
90
  const responseToCache = response.clone();
91
 
92
+ // Only cache in production or for offline fallbacks
93
+ if (!isDevelopment) {
94
+ caches.open(CACHE_NAME)
95
+ .then(cache => {
96
+ cache.put(event.request, responseToCache);
97
+ });
98
+ }
99
 
100
  return response;
101
  }).catch(() => {
update_version.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Version management script for TreeTrack
4
+ Updates version numbers and forces cache refresh
5
+ """
6
+
7
+ import json
8
+ import time
9
+ import os
10
+ import hashlib
11
+ from pathlib import Path
12
+
13
+ def get_file_hash(file_path):
14
+ """Generate MD5 hash of file for version tracking"""
15
+ if not os.path.exists(file_path):
16
+ return "missing"
17
+
18
+ with open(file_path, 'rb') as f:
19
+ return hashlib.md5(f.read()).hexdigest()[:8]
20
+
21
+ def update_version():
22
+ """Update version.json with current timestamp and file hashes"""
23
+
24
+ # Get current directory
25
+ base_dir = Path(__file__).parent
26
+ static_dir = base_dir / "static"
27
+ version_file = base_dir / "version.json"
28
+
29
+ # Generate new version info
30
+ timestamp = int(time.time())
31
+ version_number = f"3.{timestamp}"
32
+
33
+ # Get file hashes for cache busting
34
+ assets = {}
35
+ static_files = ["app.js", "index.html", "map.html", "map.js", "sw.js"]
36
+
37
+ for filename in static_files:
38
+ file_path = static_dir / filename
39
+ file_hash = get_file_hash(file_path)
40
+ assets[filename] = f"{version_number}.{file_hash}"
41
+
42
+ # Create version data
43
+ version_data = {
44
+ "version": version_number,
45
+ "timestamp": timestamp,
46
+ "build": "development" if os.getenv('NODE_ENV') != 'production' else "production",
47
+ "commit": os.getenv('GIT_COMMIT', 'local'),
48
+ "assets": assets
49
+ }
50
+
51
+ # Write version file
52
+ with open(version_file, 'w') as f:
53
+ json.dump(version_data, f, indent=4)
54
+
55
+ print(f" Updated version to: {version_number}")
56
+ print(f" Updated {len(assets)} asset versions")
57
+
58
+ return version_data
59
+
60
+ def clear_browser_cache():
61
+ """Provide instructions for clearing browser cache"""
62
+ print("\n To clear browser cache completely:")
63
+ print("1. Open Developer Tools (F12)")
64
+ print("2. Right-click the refresh button")
65
+ print("3. Select 'Empty Cache and Hard Reload'")
66
+ print("4. Or use Ctrl+Shift+R (Windows) / Cmd+Shift+R (Mac)")
67
+ print("\n For Service Workers:")
68
+ print("1. Go to Application tab in DevTools")
69
+ print("2. Click 'Service Workers' in sidebar")
70
+ print("3. Click 'Unregister' next to your service worker")
71
+ print("4. Refresh the page")
72
+
73
+ if __name__ == "__main__":
74
+ print(" TreeTrack Version Manager")
75
+ print("=" * 30)
76
+
77
+ try:
78
+ version_data = update_version()
79
+ clear_browser_cache()
80
+
81
+ print(f"\n Version update complete!")
82
+ print(f"Current version: {version_data['version']}")
83
+
84
+ except Exception as e:
85
+ print(f" Error updating version: {e}")
86
+ exit(1)
version.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "3.1754653904",
3
+ "timestamp": 1754653904,
4
+ "build": "hf_spaces",
5
+ "commit": "deployed",
6
+ "cache_cleared": true,
7
+ "deployment": "huggingface_spaces",
8
+ "port": 7860
9
+ }