-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdigbee_list_projects.py
153 lines (126 loc) · 4.78 KB
/
digbee_list_projects.py
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
from fastapi import FastAPI, Request, HTTPException
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
import requests
from typing import List, Optional
from pydantic import BaseModel
import logging
from math import radians, sin, cos, sqrt, atan2
from fastapi.middleware.cors import CORSMiddleware
# Initialize FastAPI app
app = FastAPI()
# Configure CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Mount static files - ensure this is before any routes
app.mount("/static", StaticFiles(directory="static"), name="static")
# Initialize templates
templates = Jinja2Templates(directory="templates")
# Set up logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Pydantic models
class GeoPoint(BaseModel):
lat: float
lng: float
class Company(BaseModel):
id: int
name: str
class MiningProject(BaseModel):
id: Optional[int]
name: Optional[str]
status: Optional[str]
mineType: Optional[str]
companies: Optional[List[Company]] = []
companiesString: Optional[str]
country: Optional[str]
depositType: Optional[str]
geoPoint: Optional[GeoPoint]
region: Optional[str]
commodity: Optional[str]
grade: Optional[str]
contained: Optional[str]
lifeOfMine: Optional[str]
dataSource: Optional[str]
@app.get("/")
async def home(request: Request):
"""Render the main projects table page"""
return templates.TemplateResponse("index.html", {"request": request})
@app.get("/analytics")
async def analytics_page(request: Request):
"""Render the analytics page"""
return templates.TemplateResponse("analytics.html", {"request": request})
@app.get("/project/{project_id}")
async def project_details(request: Request, project_id: int):
"""Render the project details page"""
return templates.TemplateResponse("project_dashboard.html", {
"request": request,
"project_id": project_id
})
@app.get("/api/mining-data")
async def get_mining_data():
"""Get all mining project data"""
try:
url = "https://digbee.com/api/v2/search/projects"
headers = {
'User-Agent': 'Mozilla/5.0',
'Accept': 'application/json'
}
logger.info(f"Fetching data from {url}")
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
data = response.json()
logger.info(f"Successfully retrieved {len(data)} projects")
# Convert raw data to MiningProject objects for validation
mining_projects = [MiningProject(**project).dict() for project in data]
return mining_projects
except requests.RequestException as e:
logger.error(f"API request failed: {str(e)}")
raise HTTPException(status_code=503, detail=f"Failed to fetch data: {str(e)}")
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@app.get("/api/nearby-projects/{project_id}")
async def get_nearby_projects(project_id: int):
"""Get projects within 500km radius of the specified project"""
try:
all_projects = await get_mining_data()
target_project = next((p for p in all_projects if p["id"] == project_id), None)
if not target_project or not target_project.get("geoPoint"):
return []
nearby = []
for project in all_projects:
if project["id"] != project_id and project.get("geoPoint"):
distance = calculate_distance(
target_project["geoPoint"],
project["geoPoint"]
)
if distance <= 500: # 500km radius
project_dict = project.copy()
project_dict["distance"] = round(distance, 2)
nearby.append(project_dict)
return nearby
except Exception as e:
logger.error(f"Error finding nearby projects: {str(e)}")
raise HTTPException(status_code=500, detail="Error processing nearby projects")
def calculate_distance(point1: dict, point2: dict) -> float:
"""Calculate distance between two points using Haversine formula"""
R = 6371 # Earth's radius in kilometers
lat1 = radians(point1["lat"])
lon1 = radians(point1["lng"])
lat2 = radians(point2["lat"])
lon2 = radians(point2["lng"])
dlat = lat2 - lat1
dlon = lon2 - lon1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * atan2(sqrt(a), sqrt(1-a))
distance = R * c
return distance
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)