Spaces:
Running
Running
Fix data persistence: Add database and sample data
Browse files- Updated .gitignore to allow database files in production
- Added init_db.py to create database with sample tree data
- Added export_trees.py for CSV backup functionality
- Created initial trees.db with 5 sample trees across US cities
- Generated trees.csv backup file
- Fixed hovering pin animation on map (gentle glow instead of pulse)
Resolves issue where trees added locally weren't visible in HF repo.
Database and CSV files now tracked for production deployment.
- .gitignore +6 -5
- data/export_trees.py +51 -0
- data/init_db.py +83 -0
- data/trees.csv +6 -0
- data/trees.db +0 -0
- static/map.html +26 -8
.gitignore
CHANGED
@@ -17,11 +17,12 @@ env/
|
|
17 |
*.log
|
18 |
app.log
|
19 |
|
20 |
-
# Database
|
21 |
-
data
|
22 |
-
|
23 |
-
*.
|
24 |
-
*.
|
|
|
25 |
|
26 |
# Uploads
|
27 |
uploads/
|
|
|
17 |
*.log
|
18 |
app.log
|
19 |
|
20 |
+
# Database (excluding production db)
|
21 |
+
data/*.tmp
|
22 |
+
data/*.backup
|
23 |
+
# *.db - Allow trees.db for production
|
24 |
+
# *.sqlite - Allow for production
|
25 |
+
# *.sqlite3 - Allow for production
|
26 |
|
27 |
# Uploads
|
28 |
uploads/
|
data/export_trees.py
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Export trees database to CSV format
|
4 |
+
"""
|
5 |
+
|
6 |
+
import sqlite3
|
7 |
+
import csv
|
8 |
+
from pathlib import Path
|
9 |
+
|
10 |
+
def export_to_csv():
|
11 |
+
"""Export trees database to CSV"""
|
12 |
+
db_path = Path("data/trees.db")
|
13 |
+
csv_path = Path("data/trees.csv")
|
14 |
+
|
15 |
+
if not db_path.exists():
|
16 |
+
print("β Database not found. Please run init_db.py first.")
|
17 |
+
return
|
18 |
+
|
19 |
+
conn = sqlite3.connect(db_path)
|
20 |
+
cursor = conn.cursor()
|
21 |
+
|
22 |
+
# Get all trees
|
23 |
+
cursor.execute("""
|
24 |
+
SELECT id, species, latitude, longitude, diameter, height, age,
|
25 |
+
health_status, notes, planted_by, plant_date, created_at, updated_at
|
26 |
+
FROM trees
|
27 |
+
ORDER BY id
|
28 |
+
""")
|
29 |
+
|
30 |
+
trees = cursor.fetchall()
|
31 |
+
|
32 |
+
# Write to CSV
|
33 |
+
with open(csv_path, 'w', newline='', encoding='utf-8') as csvfile:
|
34 |
+
writer = csv.writer(csvfile)
|
35 |
+
|
36 |
+
# Write header
|
37 |
+
writer.writerow([
|
38 |
+
'id', 'species', 'latitude', 'longitude', 'diameter', 'height', 'age',
|
39 |
+
'health_status', 'notes', 'planted_by', 'plant_date', 'created_at', 'updated_at'
|
40 |
+
])
|
41 |
+
|
42 |
+
# Write data
|
43 |
+
writer.writerows(trees)
|
44 |
+
|
45 |
+
conn.close()
|
46 |
+
|
47 |
+
print(f"β
Exported {len(trees)} trees to {csv_path}")
|
48 |
+
print(f"π CSV file size: {csv_path.stat().st_size} bytes")
|
49 |
+
|
50 |
+
if __name__ == "__main__":
|
51 |
+
export_to_csv()
|
data/init_db.py
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""
|
3 |
+
Initialize database with sample tree data for TreeTrack application
|
4 |
+
"""
|
5 |
+
|
6 |
+
import sqlite3
|
7 |
+
import sys
|
8 |
+
from pathlib import Path
|
9 |
+
|
10 |
+
def create_database():
|
11 |
+
"""Create and initialize the database with sample data"""
|
12 |
+
db_path = Path("data/trees.db")
|
13 |
+
|
14 |
+
# Create the database
|
15 |
+
conn = sqlite3.connect(db_path)
|
16 |
+
cursor = conn.cursor()
|
17 |
+
|
18 |
+
# Create the trees table (based on your app.py structure)
|
19 |
+
cursor.execute('''
|
20 |
+
CREATE TABLE IF NOT EXISTS trees (
|
21 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
22 |
+
species TEXT NOT NULL,
|
23 |
+
latitude REAL NOT NULL,
|
24 |
+
longitude REAL NOT NULL,
|
25 |
+
diameter REAL,
|
26 |
+
height REAL,
|
27 |
+
age INTEGER,
|
28 |
+
health_status TEXT DEFAULT 'Good',
|
29 |
+
notes TEXT,
|
30 |
+
planted_by TEXT,
|
31 |
+
plant_date DATE,
|
32 |
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
33 |
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
34 |
+
)
|
35 |
+
''')
|
36 |
+
|
37 |
+
# Add some sample trees (using coordinates around different cities)
|
38 |
+
sample_trees = [
|
39 |
+
('Oak Tree', 40.7589, -73.9851, 45.5, 15.2, 25, 'Excellent', 'Healthy mature oak in Central Park area', 'TreeTrack Admin', '2023-04-15'),
|
40 |
+
('Maple', 34.0522, -118.2437, 32.1, 12.8, 18, 'Good', 'Beautiful red maple in urban setting', 'TreeTrack Admin', '2023-05-20'),
|
41 |
+
('Pine', 41.8781, -87.6298, 28.3, 18.5, 30, 'Good', 'Evergreen pine providing year-round shade', 'TreeTrack Admin', '2023-06-10'),
|
42 |
+
('Birch', 47.6062, -122.3321, 22.7, 10.3, 12, 'Fair', 'Young birch tree showing good growth', 'TreeTrack Admin', '2023-07-01'),
|
43 |
+
('Cedar', 39.7392, -104.9903, 38.9, 14.7, 22, 'Excellent', 'Mature cedar with excellent health', 'TreeTrack Admin', '2023-08-15'),
|
44 |
+
]
|
45 |
+
|
46 |
+
cursor.executemany('''
|
47 |
+
INSERT INTO trees (species, latitude, longitude, diameter, height, age, health_status, notes, planted_by, plant_date)
|
48 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
49 |
+
''', sample_trees)
|
50 |
+
|
51 |
+
# Create indexes for better query performance
|
52 |
+
cursor.execute('CREATE INDEX IF NOT EXISTS idx_species ON trees(species)')
|
53 |
+
cursor.execute('CREATE INDEX IF NOT EXISTS idx_location ON trees(latitude, longitude)')
|
54 |
+
cursor.execute('CREATE INDEX IF NOT EXISTS idx_health ON trees(health_status)')
|
55 |
+
cursor.execute('CREATE INDEX IF NOT EXISTS idx_created ON trees(created_at)')
|
56 |
+
|
57 |
+
conn.commit()
|
58 |
+
conn.close()
|
59 |
+
|
60 |
+
print(f"β
Database created successfully at {db_path}")
|
61 |
+
print(f"π Added {len(sample_trees)} sample trees")
|
62 |
+
|
63 |
+
# Verify the data
|
64 |
+
conn = sqlite3.connect(db_path)
|
65 |
+
cursor = conn.cursor()
|
66 |
+
cursor.execute("SELECT COUNT(*) FROM trees")
|
67 |
+
count = cursor.fetchone()[0]
|
68 |
+
print(f"π³ Total trees in database: {count}")
|
69 |
+
|
70 |
+
cursor.execute("SELECT species, latitude, longitude FROM trees LIMIT 3")
|
71 |
+
sample = cursor.fetchall()
|
72 |
+
print("\nπ Sample tree data:")
|
73 |
+
for tree in sample:
|
74 |
+
print(f" - {tree[0]} at ({tree[1]:.4f}, {tree[2]:.4f})")
|
75 |
+
|
76 |
+
conn.close()
|
77 |
+
|
78 |
+
if __name__ == "__main__":
|
79 |
+
try:
|
80 |
+
create_database()
|
81 |
+
except Exception as e:
|
82 |
+
print(f"β Error creating database: {e}")
|
83 |
+
sys.exit(1)
|
data/trees.csv
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
id,species,latitude,longitude,diameter,height,age,health_status,notes,planted_by,plant_date,created_at,updated_at
|
2 |
+
1,Oak Tree,40.7589,-73.9851,45.5,15.2,25,Excellent,Healthy mature oak in Central Park area,TreeTrack Admin,2023-04-15,2025-08-08 12:03:44,2025-08-08 12:03:44
|
3 |
+
2,Maple,34.0522,-118.2437,32.1,12.8,18,Good,Beautiful red maple in urban setting,TreeTrack Admin,2023-05-20,2025-08-08 12:03:44,2025-08-08 12:03:44
|
4 |
+
3,Pine,41.8781,-87.6298,28.3,18.5,30,Good,Evergreen pine providing year-round shade,TreeTrack Admin,2023-06-10,2025-08-08 12:03:44,2025-08-08 12:03:44
|
5 |
+
4,Birch,47.6062,-122.3321,22.7,10.3,12,Fair,Young birch tree showing good growth,TreeTrack Admin,2023-07-01,2025-08-08 12:03:44,2025-08-08 12:03:44
|
6 |
+
5,Cedar,39.7392,-104.9903,38.9,14.7,22,Excellent,Mature cedar with excellent health,TreeTrack Admin,2023-08-15,2025-08-08 12:03:44,2025-08-08 12:03:44
|
data/trees.db
ADDED
Binary file (28.7 kB). View file
|
|
static/map.html
CHANGED
@@ -246,14 +246,32 @@
|
|
246 |
background: #ff6b35;
|
247 |
border: 3px solid white;
|
248 |
border-radius: 50%;
|
249 |
-
box-shadow:
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
257 |
}
|
258 |
|
259 |
/* Loading States */
|
|
|
246 |
background: #ff6b35;
|
247 |
border: 3px solid white;
|
248 |
border-radius: 50%;
|
249 |
+
box-shadow:
|
250 |
+
0 2px 10px rgba(0,0,0,0.3),
|
251 |
+
0 0 15px rgba(255, 107, 53, 0.6),
|
252 |
+
0 0 25px rgba(255, 107, 53, 0.3);
|
253 |
+
animation: gentle-glow 3s ease-in-out infinite;
|
254 |
+
}
|
255 |
+
|
256 |
+
@keyframes gentle-glow {
|
257 |
+
0% {
|
258 |
+
box-shadow:
|
259 |
+
0 2px 10px rgba(0,0,0,0.3),
|
260 |
+
0 0 15px rgba(255, 107, 53, 0.6),
|
261 |
+
0 0 25px rgba(255, 107, 53, 0.3);
|
262 |
+
}
|
263 |
+
50% {
|
264 |
+
box-shadow:
|
265 |
+
0 2px 12px rgba(0,0,0,0.4),
|
266 |
+
0 0 20px rgba(255, 107, 53, 0.8),
|
267 |
+
0 0 35px rgba(255, 107, 53, 0.5);
|
268 |
+
}
|
269 |
+
100% {
|
270 |
+
box-shadow:
|
271 |
+
0 2px 10px rgba(0,0,0,0.3),
|
272 |
+
0 0 15px rgba(255, 107, 53, 0.6),
|
273 |
+
0 0 25px rgba(255, 107, 53, 0.3);
|
274 |
+
}
|
275 |
}
|
276 |
|
277 |
/* Loading States */
|