extracted client
This commit is contained in:
167
app.py
167
app.py
@@ -6,11 +6,12 @@ import pytz
|
|||||||
|
|
||||||
from flask import Flask, render_template, request, redirect, url_for, session, abort, jsonify
|
from flask import Flask, render_template, request, redirect, url_for, session, abort, jsonify
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
load_dotenv()
|
||||||
import firebase_admin
|
import firebase_admin
|
||||||
from firebase_admin import credentials, auth as fb_auth, firestore
|
from firebase_admin import credentials, auth as fb_auth, firestore
|
||||||
import requests
|
import requests
|
||||||
|
from filevine_client import FilevineClient
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = os.environ.get("FLASK_SECRET_KEY", os.urandom(32))
|
app.secret_key = os.environ.get("FLASK_SECRET_KEY", os.urandom(32))
|
||||||
@@ -126,18 +127,19 @@ def fetch_all_projects():
|
|||||||
"""Fetch all projects for a user and store them in Firestore"""
|
"""Fetch all projects for a user and store them in Firestore"""
|
||||||
|
|
||||||
print("Fetching projects....")
|
print("Fetching projects....")
|
||||||
# Get bearer token
|
# Initialize Filevine client
|
||||||
bearer = get_filevine_bearer()
|
client = FilevineClient()
|
||||||
|
bearer = client.get_bearer_token()
|
||||||
|
|
||||||
# List projects (all pages)
|
# List projects (all pages)
|
||||||
projects = list_all_projects(bearer)
|
projects = client.list_all_projects()
|
||||||
projects = projects[:]
|
projects = projects[:]
|
||||||
|
|
||||||
# Fetch details for each
|
# Fetch details for each
|
||||||
detailed_rows = []
|
detailed_rows = []
|
||||||
|
|
||||||
import worker_pool
|
import worker_pool
|
||||||
detailed_rows = worker_pool.process_projects_parallel(projects, bearer, 9)
|
detailed_rows = worker_pool.process_projects_parallel(projects, client, 9)
|
||||||
# Store the results in Firestore
|
# Store the results in Firestore
|
||||||
projects_ref = db.collection("projects")
|
projects_ref = db.collection("projects")
|
||||||
|
|
||||||
@@ -208,160 +210,7 @@ def welcome():
|
|||||||
|
|
||||||
|
|
||||||
# --- Filevine API ---
|
# --- Filevine API ---
|
||||||
|
# Filevine client is now in filevine_client.py
|
||||||
def get_filevine_bearer():
|
|
||||||
url = "https://identity.filevine.com/connect/token"
|
|
||||||
data = {
|
|
||||||
"client_id": FV_CLIENT_ID,
|
|
||||||
"client_secret": FV_CLIENT_SECRET,
|
|
||||||
"grant_type": "personal_access_token",
|
|
||||||
"scope": "fv.api.gateway.access tenant filevine.v2.api.* email openid fv.auth.tenant.read",
|
|
||||||
"token": FV_PAT,
|
|
||||||
}
|
|
||||||
headers = {"Accept": "application/json"}
|
|
||||||
resp = requests.post(url, data=data, headers=headers, timeout=30)
|
|
||||||
resp.raise_for_status()
|
|
||||||
js = resp.json()
|
|
||||||
token = js.get("access_token")
|
|
||||||
print(f"Got bearer js", js)
|
|
||||||
return token
|
|
||||||
|
|
||||||
|
|
||||||
def list_all_projects(bearer: str):
|
|
||||||
base = "https://api.filevineapp.com/fv-app/v2/Projects?limit=500"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
results = []
|
|
||||||
last_count = None
|
|
||||||
tries = 0
|
|
||||||
offset = 0
|
|
||||||
# TODO we probably need to sync the data with fierbase
|
|
||||||
cnt = 0
|
|
||||||
while True:
|
|
||||||
cnt = len(results)
|
|
||||||
print(f"list try {tries}, starting at {offset}, previous count {last_count}, currently at {cnt}")
|
|
||||||
tries += 1
|
|
||||||
url = base
|
|
||||||
params = {}
|
|
||||||
if last_count is not None:
|
|
||||||
# Some deployments use LastID/Offset pagination; adapt if needed
|
|
||||||
offset = offset + last_count
|
|
||||||
params["offset"] = offset
|
|
||||||
r = requests.get(url, headers=headers, params=params, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
page = r.json()
|
|
||||||
from pprint import pprint
|
|
||||||
print(f"Fetched page. Headers: {r.headers}, Offset: {offset}")
|
|
||||||
items = page.get("items", [])
|
|
||||||
results.extend(items)
|
|
||||||
has_more = page.get("hasMore")
|
|
||||||
last_count = page.get("count")
|
|
||||||
if not has_more:
|
|
||||||
break
|
|
||||||
# Safety valve
|
|
||||||
if tries > 200:
|
|
||||||
break
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_project_detail(bearer: str, project_id_native: int):
|
|
||||||
url = f"https://api.filevineapp.com/fv-app/v2/Projects/{project_id_native}"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
r = requests.get(url, headers=headers, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
return r.json()
|
|
||||||
|
|
||||||
def fetch_project_team(bearer: str, project_id_native: int):
|
|
||||||
url = f"https://api.filevineapp.com/fv-app/v2/Projects/{project_id_native}/team?limit=1000"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
r = requests.get(url, headers=headers, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
from pprint import pprint
|
|
||||||
return r.json().get('items') or []
|
|
||||||
|
|
||||||
def fetch_project_tasks(bearer: str, project_id_native: int):
|
|
||||||
url = f"https://api.filevineapp.com/fv-app/v2/Projects/{project_id_native}/tasks"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
r = requests.get(url, headers=headers, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
return r.json()
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_client(bearer: str, client_id_native: int):
|
|
||||||
url = f"https://api.filevineapp.com/fv-app/v2/contacts/{client_id_native}"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
r = requests.get(url, headers=headers, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
return r.json()
|
|
||||||
|
|
||||||
def fetch_contacts(bearer: str, project_id_native: int):
|
|
||||||
url = f"https://api.filevineapp.com/fv-app/v2/projects/{project_id_native}/contacts"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
r = requests.get(url, headers=headers, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
return r.json().get("items")
|
|
||||||
|
|
||||||
|
|
||||||
def fetch_form(bearer: str, project_id_native: int, form: str):
|
|
||||||
try:
|
|
||||||
url = f"https://api.filevineapp.com/fv-app/v2/Projects/{project_id_native}/Forms/{form}"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
r = requests.get(url, headers=headers, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
return r.json()
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def fetch_collection(bearer: str, project_id_native: int, collection: str):
|
|
||||||
try:
|
|
||||||
url = f"https://api.filevineapp.com/fv-app/v2/Projects/{project_id_native}/Collections/{collection}"
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Authorization": f"Bearer {bearer}",
|
|
||||||
"x-fv-orgid": str(FV_ORG_ID),
|
|
||||||
"x-fv-userid": str(FV_USER_ID),
|
|
||||||
}
|
|
||||||
r = requests.get(url, headers=headers, timeout=30)
|
|
||||||
r.raise_for_status()
|
|
||||||
return [x.get('dataObject') for x in r.json().get("items")]
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return {}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
132
filevine_client.py
Normal file
132
filevine_client.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
|
||||||
|
# Load environment variables
|
||||||
|
FV_CLIENT_ID = os.environ.get("FILEVINE_CLIENT_ID")
|
||||||
|
FV_CLIENT_SECRET = os.environ.get("FILEVINE_CLIENT_SECRET")
|
||||||
|
FV_PAT = os.environ.get("FILEVINE_PERSONAL_ACCESS_TOKEN")
|
||||||
|
FV_ORG_ID = os.environ.get("FILEVINE_ORG_ID")
|
||||||
|
FV_USER_ID = os.environ.get("FILEVINE_USER_ID")
|
||||||
|
|
||||||
|
class FilevineClient:
|
||||||
|
def __init__(self, bearer_token: str = None):
|
||||||
|
self.bearer_token = bearer_token
|
||||||
|
self.base_url = "https://api.filevineapp.com/fv-app/v2"
|
||||||
|
self.headers = {
|
||||||
|
"Accept": "application/json",
|
||||||
|
"Authorization": f"Bearer {self.bearer_token}",
|
||||||
|
"x-fv-orgid": str(FV_ORG_ID),
|
||||||
|
"x-fv-userid": str(FV_USER_ID),
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_bearer_token(self) -> str:
|
||||||
|
"""Get a new bearer token using Filevine credentials"""
|
||||||
|
url = "https://identity.filevine.com/connect/token"
|
||||||
|
data = {
|
||||||
|
"client_id": FV_CLIENT_ID,
|
||||||
|
"client_secret": FV_CLIENT_SECRET,
|
||||||
|
"grant_type": "personal_access_token",
|
||||||
|
"scope": "fv.api.gateway.access tenant filevine.v2.api.* email openid fv.auth.tenant.read",
|
||||||
|
"token": FV_PAT,
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {"Accept": "application/json"}
|
||||||
|
print(data)
|
||||||
|
resp = requests.post(url, data=data, headers=headers, timeout=30)
|
||||||
|
resp.raise_for_status()
|
||||||
|
js = resp.json()
|
||||||
|
token = js.get("access_token")
|
||||||
|
print(f"Got bearer js", js)
|
||||||
|
self.bearer_token = token
|
||||||
|
self.headers["Authorization"] = f"Bearer {token}"
|
||||||
|
return token
|
||||||
|
|
||||||
|
def list_all_projects(self) -> List[Dict[str, Any]]:
|
||||||
|
"""Fetch all projects from Filevine API"""
|
||||||
|
base = f"{self.base_url}/Projects?limit=500"
|
||||||
|
results = []
|
||||||
|
last_count = None
|
||||||
|
tries = 0
|
||||||
|
offset = 0
|
||||||
|
cnt = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
cnt = len(results)
|
||||||
|
print(f"list try {tries}, starting at {offset}, previous count {last_count}, currently at {cnt}")
|
||||||
|
tries += 1
|
||||||
|
url = base
|
||||||
|
params = {}
|
||||||
|
if last_count is not None:
|
||||||
|
offset = offset + last_count
|
||||||
|
params["offset"] = offset
|
||||||
|
r = requests.get(url, headers=self.headers, params=params, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
page = r.json()
|
||||||
|
items = page.get("items", [])
|
||||||
|
results.extend(items)
|
||||||
|
has_more = page.get("hasMore")
|
||||||
|
last_count = page.get("count")
|
||||||
|
if not has_more:
|
||||||
|
break
|
||||||
|
# Safety valve
|
||||||
|
if tries > 200:
|
||||||
|
break
|
||||||
|
return results
|
||||||
|
|
||||||
|
def fetch_project_detail(self, project_id_native: int) -> Dict[str, Any]:
|
||||||
|
"""Fetch detailed information for a specific project"""
|
||||||
|
url = f"{self.base_url}/Projects/{project_id_native}"
|
||||||
|
r = requests.get(url, headers=self.headers, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def fetch_project_team(self, project_id_native: int) -> List[Dict[str, Any]]:
|
||||||
|
"""Fetch team members for a specific project"""
|
||||||
|
url = f"{self.base_url}/Projects/{project_id_native}/team?limit=1000"
|
||||||
|
r = requests.get(url, headers=self.headers, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json().get('items') or []
|
||||||
|
|
||||||
|
def fetch_project_tasks(self, project_id_native: int) -> Dict[str, Any]:
|
||||||
|
"""Fetch tasks for a specific project"""
|
||||||
|
url = f"{self.base_url}/Projects/{project_id_native}/tasks"
|
||||||
|
r = requests.get(url, headers=self.headers, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def fetch_client(self, client_id_native: int) -> Dict[str, Any]:
|
||||||
|
"""Fetch client information by client ID"""
|
||||||
|
url = f"{self.base_url}/contacts/{client_id_native}"
|
||||||
|
r = requests.get(url, headers=self.headers, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
def fetch_contacts(self, project_id_native: int) -> Optional[List[Dict[str, Any]]]:
|
||||||
|
"""Fetch contacts for a specific project"""
|
||||||
|
url = f"{self.base_url}/projects/{project_id_native}/contacts"
|
||||||
|
r = requests.get(url, headers=self.headers, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json().get("items")
|
||||||
|
|
||||||
|
def fetch_form(self, project_id_native: int, form: str) -> Dict[str, Any]:
|
||||||
|
"""Fetch a specific form for a project"""
|
||||||
|
try:
|
||||||
|
url = f"{self.base_url}/Projects/{project_id_native}/Forms/{form}"
|
||||||
|
r = requests.get(url, headers=self.headers, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def fetch_collection(self, project_id_native: int, collection: str) -> List[Dict[str, Any]]:
|
||||||
|
"""Fetch a collection for a project"""
|
||||||
|
try:
|
||||||
|
url = f"{self.base_url}/Projects/{project_id_native}/Collections/{collection}"
|
||||||
|
r = requests.get(url, headers=self.headers, timeout=30)
|
||||||
|
r.raise_for_status()
|
||||||
|
return [x.get('dataObject') for x in r.json().get("items")]
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return {}
|
||||||
@@ -3,76 +3,68 @@ import threading
|
|||||||
from typing import List, Any, Callable, Tuple
|
from typing import List, Any, Callable, Tuple
|
||||||
import time
|
import time
|
||||||
|
|
||||||
# Global thread-local storage for bearer token to avoid passing it around
|
# Global thread-local storage for FilevineClient to avoid passing it around
|
||||||
_thread_local = threading.local()
|
_thread_local = threading.local()
|
||||||
|
|
||||||
def get_bearer_token():
|
def get_filevine_client():
|
||||||
"""Get bearer token from thread local storage"""
|
"""Get FilevineClient from thread local storage"""
|
||||||
return getattr(_thread_local, 'bearer', None)
|
return getattr(_thread_local, 'client', None)
|
||||||
|
|
||||||
def set_bearer_token(token):
|
def set_filevine_client(client):
|
||||||
"""Set bearer token in thread local storage"""
|
"""Set FilevineClient in thread local storage"""
|
||||||
_thread_local.bearer = token
|
_thread_local.client = client
|
||||||
|
|
||||||
def worker_init(bearer_token: str):
|
def worker_init(client: 'FilevineClient'):
|
||||||
"""Initialize worker with bearer token"""
|
"""Initialize worker with FilevineClient"""
|
||||||
set_bearer_token(bearer_token)
|
set_filevine_client(client)
|
||||||
|
|
||||||
def process_project(index: int, total: int, project_data: dict, bearer_token: str) -> dict:
|
def process_project(index: int, total: int, project_data: dict, client: 'FilevineClient') -> dict:
|
||||||
"""
|
"""
|
||||||
Process a single project with all its API calls.
|
Process a single project with all its API calls.
|
||||||
This is the function that will be executed by workers in parallel.
|
This is the function that will be executed by workers in parallel.
|
||||||
"""
|
"""
|
||||||
# Set the bearer token for this thread
|
# Set the FilevineClient for this thread
|
||||||
set_bearer_token(bearer_token)
|
set_filevine_client(client)
|
||||||
|
|
||||||
from app import (
|
from app import convert_to_pacific_time
|
||||||
fetch_client,
|
|
||||||
fetch_contacts,
|
|
||||||
fetch_project_detail,
|
|
||||||
fetch_form,
|
|
||||||
fetch_collection,
|
|
||||||
fetch_project_tasks,
|
|
||||||
fetch_project_team,
|
|
||||||
convert_to_pacific_time
|
|
||||||
)
|
|
||||||
|
|
||||||
p = project_data
|
p = project_data
|
||||||
pid = (p.get("projectId") or {}).get("native")
|
pid = (p.get("projectId") or {}).get("native")
|
||||||
print(f"Working on {pid} ({index}/{total})")
|
print(f"Working on {pid} ({index}/{total})")
|
||||||
c = fetch_client(bearer_token, (p.get("clientId") or {}).get("native"))
|
client = get_filevine_client()
|
||||||
cs = fetch_contacts(bearer_token, pid)
|
c = client.fetch_client((p.get("clientId") or {}).get("native"))
|
||||||
|
cs = client.fetch_contacts(pid)
|
||||||
|
|
||||||
if pid is None:
|
if pid is None:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
detail = fetch_project_detail(bearer_token, pid)
|
detail = client.fetch_project_detail(pid)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[WARN] detail fetch failed for {pid}: {e}")
|
print(f"[WARN] detail fetch failed for {pid}: {e}")
|
||||||
detail = {}
|
detail = {}
|
||||||
|
|
||||||
defendant_one = next((c.get('orgContact', {}) for c in cs if "Defendant" in c.get('orgContact', {}).get('personTypes', [])), {})
|
defendant_one = next((c.get('orgContact', {}) for c in cs if "Defendant" in c.get('orgContact', {}).get('personTypes', [])), {})
|
||||||
|
|
||||||
new_file_review = fetch_form(bearer_token, pid, "newFileReview") or {}
|
new_file_review = client.fetch_form(pid, "newFileReview") or {}
|
||||||
dates_and_deadlines = fetch_form(bearer_token, pid, "datesAndDeadlines") or {}
|
dates_and_deadlines = client.fetch_form(pid, "datesAndDeadlines") or {}
|
||||||
service_info = fetch_collection(bearer_token, pid, "serviceInfo") or []
|
service_info = client.fetch_collection(pid, "serviceInfo") or []
|
||||||
property_info = fetch_form(bearer_token, pid, "propertyInfo")
|
property_info = client.fetch_form(pid, "propertyInfo")
|
||||||
matter_overview = fetch_form(bearer_token, pid, "matterOverview")
|
matter_overview = client.fetch_form(pid, "matterOverview")
|
||||||
fees_and_costs = fetch_form(bearer_token, pid, "feesAndCosts") or {}
|
fees_and_costs = client.fetch_form(pid, "feesAndCosts") or {}
|
||||||
property_contacts = fetch_form(bearer_token, pid, "propertyContacts") or {}
|
property_contacts = client.fetch_form(pid, "propertyContacts") or {}
|
||||||
lease_info_np = fetch_form(bearer_token, pid, "leaseInfoNP") or {}
|
lease_info_np = client.fetch_form(pid, "leaseInfoNP") or {}
|
||||||
|
|
||||||
completed_tasks = [{"description": x.get("body"),
|
completed_tasks = [{"description": x.get("body"),
|
||||||
"completed": convert_to_pacific_time(x.get("completedDate"))}
|
"completed": convert_to_pacific_time(x.get("completedDate"))}
|
||||||
for x in fetch_project_tasks(bearer_token, pid).get("items")
|
for x in client.fetch_project_tasks(pid).get("items")
|
||||||
if x.get("isCompleted")]
|
if x.get("isCompleted")]
|
||||||
pending_tasks = [{"description": x.get("body"),
|
pending_tasks = [{"description": x.get("body"),
|
||||||
"completed": convert_to_pacific_time(x.get("completedDate"))}
|
"completed": convert_to_pacific_time(x.get("completedDate"))}
|
||||||
for x in fetch_project_tasks(bearer_token, pid).get("items")
|
for x in client.fetch_project_tasks(pid).get("items")
|
||||||
if not x.get("isCompleted")]
|
if not x.get("isCompleted")]
|
||||||
|
|
||||||
team = fetch_project_team(bearer_token, pid)
|
team = client.fetch_project_team(pid)
|
||||||
assigned_attorney = next((m.get('fullname')
|
assigned_attorney = next((m.get('fullname')
|
||||||
for m in team
|
for m in team
|
||||||
if ('Assigned Attorney' in [r.get('name') for r in m.get('teamOrgRoles')])
|
if ('Assigned Attorney' in [r.get('name') for r in m.get('teamOrgRoles')])
|
||||||
@@ -202,13 +194,13 @@ def process_project(index: int, total: int, project_data: dict, bearer_token: st
|
|||||||
|
|
||||||
return row
|
return row
|
||||||
|
|
||||||
def process_projects_parallel(projects: List[dict], bearer_token: str, max_workers: int = 9) -> List[dict]:
|
def process_projects_parallel(projects: List[dict], client: 'FilevineClient', max_workers: int = 9) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
Process projects in parallel using a worker pool.
|
Process projects in parallel using a worker pool.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
projects: List of project data dictionaries
|
projects: List of project data dictionaries
|
||||||
bearer_token: Filevine API bearer token
|
client: FilevineClient instance
|
||||||
max_workers: Number of concurrent workers (default 20)
|
max_workers: Number of concurrent workers (default 20)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@@ -216,9 +208,9 @@ def process_projects_parallel(projects: List[dict], bearer_token: str, max_worke
|
|||||||
"""
|
"""
|
||||||
# Create a thread pool with specified number of workers
|
# Create a thread pool with specified number of workers
|
||||||
total = len(projects)
|
total = len(projects)
|
||||||
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers, initializer=worker_init, initargs=(bearer_token,)) as executor:
|
with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers, initializer=worker_init, initargs=(client,)) as executor:
|
||||||
# Submit all tasks to the executor
|
# Submit all tasks to the executor
|
||||||
future_to_project = {executor.submit(process_project, indx, total, project, bearer_token): project for indx, project in enumerate(projects)}
|
future_to_project = {executor.submit(process_project, indx, total, project, client): project for indx, project in enumerate(projects)}
|
||||||
|
|
||||||
# Collect results as they complete
|
# Collect results as they complete
|
||||||
results = []
|
results = []
|
||||||
|
|||||||
Reference in New Issue
Block a user