Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
import os
|
| 3 |
import io
|
| 4 |
import json
|
|
|
|
| 5 |
import traceback
|
| 6 |
from typing import List
|
| 7 |
from io import BytesIO
|
|
@@ -19,16 +20,17 @@ from google.cloud import vision_v1 as vision
|
|
| 19 |
# βββββ Load .env (locally) ββββββββββββββββββββββββββββββββββββ
|
| 20 |
# In HF Spaces you'll set these as Secrets under Settings β Variables & secrets
|
| 21 |
load_dotenv()
|
| 22 |
-
openai.api_key
|
| 23 |
-
gcv_api_key
|
| 24 |
-
|
|
|
|
| 25 |
|
| 26 |
if not openai.api_key:
|
| 27 |
raise RuntimeError("Missing OPENAI_API_KEY")
|
| 28 |
if not gcv_api_key:
|
| 29 |
raise RuntimeError("Missing GCV_API_KEY")
|
| 30 |
-
if not
|
| 31 |
-
raise RuntimeError("Missing
|
| 32 |
|
| 33 |
# βββββ FastAPI setup ββββββββββββββββββββββββββββββββββββββββββ
|
| 34 |
app = FastAPI(title="Aliro Data Extraction API")
|
|
@@ -39,7 +41,7 @@ app.add_middleware(
|
|
| 39 |
allow_headers=["*"],
|
| 40 |
)
|
| 41 |
|
| 42 |
-
# βββββ A simple root so GET / won
|
| 43 |
@app.get("/")
|
| 44 |
def read_root():
|
| 45 |
return {"message": "Aliro Data Extraction API β POST your files to /extract"}
|
|
@@ -94,18 +96,79 @@ def process_buffers(buffers: List[BytesIO]) -> List[str]:
|
|
| 94 |
|
| 95 |
return results
|
| 96 |
|
| 97 |
-
# βββββ LinkedIn scrape via
|
| 98 |
def scrape_linkedin(url: str) -> str:
|
| 99 |
if not url:
|
| 100 |
return ""
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
# βββββ Summarize via OpenAI βββββββββββββββββββββββββββββββββ
|
| 111 |
def make_summary(chunks: List[str]) -> str:
|
|
@@ -155,6 +218,5 @@ async def extract_endpoint(
|
|
| 155 |
|
| 156 |
except requests.HTTPError as e:
|
| 157 |
raise HTTPException(status_code=502, detail=f"LinkedIn scrape failed: {e}")
|
| 158 |
-
except Exception:
|
| 159 |
-
|
| 160 |
-
raise HTTPException(status_code=500, detail=f"Processing error:\n\n{tb}")
|
|
|
|
| 2 |
import os
|
| 3 |
import io
|
| 4 |
import json
|
| 5 |
+
import time
|
| 6 |
import traceback
|
| 7 |
from typing import List
|
| 8 |
from io import BytesIO
|
|
|
|
| 20 |
# βββββ Load .env (locally) ββββββββββββββββββββββββββββββββββββ
|
| 21 |
# In HF Spaces you'll set these as Secrets under Settings β Variables & secrets
|
| 22 |
load_dotenv()
|
| 23 |
+
openai.api_key = os.getenv("OPENAI_API_KEY")
|
| 24 |
+
gcv_api_key = os.getenv("GCV_API_KEY")
|
| 25 |
+
brightdata_api_key = os.getenv("BRIGHTDATA_API_KEY")
|
| 26 |
+
brightdata_dataset_id = "gd_l1viktl72bvl7bjuj0"
|
| 27 |
|
| 28 |
if not openai.api_key:
|
| 29 |
raise RuntimeError("Missing OPENAI_API_KEY")
|
| 30 |
if not gcv_api_key:
|
| 31 |
raise RuntimeError("Missing GCV_API_KEY")
|
| 32 |
+
if not brightdata_api_key:
|
| 33 |
+
raise RuntimeError("Missing BRIGHTDATA_API_KEY")
|
| 34 |
|
| 35 |
# βββββ FastAPI setup ββββββββββββββββββββββββββββββββββββββββββ
|
| 36 |
app = FastAPI(title="Aliro Data Extraction API")
|
|
|
|
| 41 |
allow_headers=["*"],
|
| 42 |
)
|
| 43 |
|
| 44 |
+
# βββββ A simple root so GET / won't 404 βββββββββββββββββββββββ
|
| 45 |
@app.get("/")
|
| 46 |
def read_root():
|
| 47 |
return {"message": "Aliro Data Extraction API β POST your files to /extract"}
|
|
|
|
| 96 |
|
| 97 |
return results
|
| 98 |
|
| 99 |
+
# βββββ LinkedIn scrape via BrightData βββββββββββββββββββββββββ
|
| 100 |
def scrape_linkedin(url: str) -> str:
|
| 101 |
if not url:
|
| 102 |
return ""
|
| 103 |
+
|
| 104 |
+
try:
|
| 105 |
+
# Step 1: Trigger data collection
|
| 106 |
+
trigger_url = "https://api.brightdata.com/datasets/v3/trigger"
|
| 107 |
+
headers = {
|
| 108 |
+
"Authorization": f"Bearer {brightdata_api_key}",
|
| 109 |
+
"Content-Type": "application/json",
|
| 110 |
+
}
|
| 111 |
+
params = {
|
| 112 |
+
"dataset_id": brightdata_dataset_id,
|
| 113 |
+
"include_errors": "true",
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
trigger_response = requests.post(
|
| 117 |
+
trigger_url,
|
| 118 |
+
headers=headers,
|
| 119 |
+
params=params,
|
| 120 |
+
json=[{"url": url}],
|
| 121 |
+
timeout=30
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
if not trigger_response.ok:
|
| 125 |
+
raise Exception(f"BrightData trigger failed: {trigger_response.status_code} - {trigger_response.text}")
|
| 126 |
+
|
| 127 |
+
trigger_data = trigger_response.json()
|
| 128 |
+
snapshot_id = trigger_data.get('snapshot_id')
|
| 129 |
+
|
| 130 |
+
if not snapshot_id:
|
| 131 |
+
raise Exception("No snapshot_id received from BrightData")
|
| 132 |
+
|
| 133 |
+
print(f"BrightData collection triggered, snapshot_id: {snapshot_id}")
|
| 134 |
+
|
| 135 |
+
# Step 2: Poll for completion (max 5 minutes)
|
| 136 |
+
max_attempts = 30
|
| 137 |
+
for attempt in range(max_attempts):
|
| 138 |
+
time.sleep(10) # Wait 10 seconds between checks
|
| 139 |
+
|
| 140 |
+
progress_url = f"https://api.brightdata.com/datasets/v3/progress/{snapshot_id}"
|
| 141 |
+
progress_response = requests.get(progress_url, headers=headers, timeout=30)
|
| 142 |
+
|
| 143 |
+
if progress_response.ok:
|
| 144 |
+
progress_data = progress_response.json()
|
| 145 |
+
status = progress_data.get('status')
|
| 146 |
+
|
| 147 |
+
print(f"Progress check {attempt + 1}: status = {status}")
|
| 148 |
+
|
| 149 |
+
if status == 'ready':
|
| 150 |
+
# Step 3: Fetch results
|
| 151 |
+
result_url = f"https://api.brightdata.com/datasets/v3/snapshot/{snapshot_id}"
|
| 152 |
+
result_params = {"format": "json"}
|
| 153 |
+
result_response = requests.get(result_url, headers=headers, params=result_params, timeout=30)
|
| 154 |
+
|
| 155 |
+
if result_response.ok:
|
| 156 |
+
linkedin_data = result_response.json()
|
| 157 |
+
return json.dumps(linkedin_data, indent=2)
|
| 158 |
+
else:
|
| 159 |
+
raise Exception(f"Failed to fetch results: {result_response.status_code} - {result_response.text}")
|
| 160 |
+
|
| 161 |
+
elif status == 'failed':
|
| 162 |
+
raise Exception("BrightData collection failed")
|
| 163 |
+
|
| 164 |
+
# Continue polling if status is 'running'
|
| 165 |
+
else:
|
| 166 |
+
print(f"Progress check failed: {progress_response.status_code}")
|
| 167 |
+
|
| 168 |
+
raise Exception("Timeout waiting for BrightData collection to complete")
|
| 169 |
+
|
| 170 |
+
except Exception as e:
|
| 171 |
+
raise Exception(f"LinkedIn scraping error: {str(e)}")
|
| 172 |
|
| 173 |
# βββββ Summarize via OpenAI βββββββββββββββββββββββββββββββββ
|
| 174 |
def make_summary(chunks: List[str]) -> str:
|
|
|
|
| 218 |
|
| 219 |
except requests.HTTPError as e:
|
| 220 |
raise HTTPException(status_code=502, detail=f"LinkedIn scrape failed: {e}")
|
| 221 |
+
except Exception as e:
|
| 222 |
+
raise HTTPException(status_code=500, detail=f"Processing error: {str(e)}")
|
|
|