implements cache
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,5 +7,6 @@ terraform/.terraform/**
|
|||||||
**/terraform.tfstate
|
**/terraform.tfstate
|
||||||
**/terraform.tfstate.backup
|
**/terraform.tfstate.backup
|
||||||
.terraform/*
|
.terraform/*
|
||||||
|
__pycache__/**
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
110
app.py
110
app.py
@@ -38,6 +38,9 @@ FV_USER_ID = os.environ.get("FILEVINE_USER_ID")
|
|||||||
if not all([FV_CLIENT_ID, FV_CLIENT_SECRET, FV_PAT, FV_ORG_ID, FV_USER_ID]):
|
if not all([FV_CLIENT_ID, FV_CLIENT_SECRET, FV_PAT, FV_ORG_ID, FV_USER_ID]):
|
||||||
print("[WARN] Missing one or more Filevine env vars — dashboard will fail until set.")
|
print("[WARN] Missing one or more Filevine env vars — dashboard will fail until set.")
|
||||||
|
|
||||||
|
# --- Cache ---
|
||||||
|
from cache import project_cache
|
||||||
|
|
||||||
# --- Helpers ---
|
# --- Helpers ---
|
||||||
|
|
||||||
def login_required(view):
|
def login_required(view):
|
||||||
@@ -62,6 +65,55 @@ def get_user_profile(uid: str):
|
|||||||
return {"enabled": bool(data.get("enabled", False)), "caseEmail": data.get("caseEmail")}
|
return {"enabled": bool(data.get("enabled", False)), "caseEmail": data.get("caseEmail")}
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_all_projects_for_user(uid: str):
|
||||||
|
"""Fetch all projects for a user and cache them"""
|
||||||
|
# Get bearer token
|
||||||
|
bearer = get_filevine_bearer()
|
||||||
|
|
||||||
|
# List projects (all pages)
|
||||||
|
projects = list_all_projects(bearer)
|
||||||
|
|
||||||
|
# Filter to ProjectEmailAddress == caseEmail
|
||||||
|
profile = get_user_profile(uid)
|
||||||
|
case_email = profile.get("caseEmail")
|
||||||
|
# if case_email:
|
||||||
|
# filtered = [p for p in projects if str(p.get("ProjectEmailAddress", "")).lower() == str(case_email).lower()]
|
||||||
|
# else:
|
||||||
|
# filtered = []
|
||||||
|
filtered = projects
|
||||||
|
|
||||||
|
# Fetch details for each
|
||||||
|
detailed_rows = []
|
||||||
|
for p in filtered:
|
||||||
|
pid = (p.get("projectId") or {}).get("native")
|
||||||
|
c = fetch_client(bearer, (p.get("clientId") or {}).get("native"))
|
||||||
|
cs = fetch_contacts(bearer, pid)
|
||||||
|
print("CS IS", cs)
|
||||||
|
|
||||||
|
if pid is None:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
detail = fetch_project_detail(bearer, pid)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WARN] detail fetch failed for {pid}: {e}")
|
||||||
|
detail = {}
|
||||||
|
row = {
|
||||||
|
"client": c.get("firstName"),
|
||||||
|
"matter_description": p.get("projectName"),
|
||||||
|
"contacts": cs,
|
||||||
|
"ProjectEmailAddress": p.get("projectEmailAddress"),
|
||||||
|
"Number": p.get("number"),
|
||||||
|
"IncidentDate": (p.get("incidentDate") or detail.get("incidentDate")),
|
||||||
|
"ProjectId": pid,
|
||||||
|
"ProjectName": p.get("projectName") or detail.get("projectName"),
|
||||||
|
"ProjectUrl": p.get("projectUrl") or detail.get("projectUrl"),
|
||||||
|
}
|
||||||
|
detailed_rows.append(row)
|
||||||
|
|
||||||
|
# Cache the results
|
||||||
|
project_cache.set_projects(detailed_rows)
|
||||||
|
return detailed_rows
|
||||||
|
|
||||||
# --- Routes ---
|
# --- Routes ---
|
||||||
@app.route("/")
|
@app.route("/")
|
||||||
def index():
|
def index():
|
||||||
@@ -99,6 +151,13 @@ def session_login():
|
|||||||
session["uid"] = uid
|
session["uid"] = uid
|
||||||
# Optional: short session
|
# Optional: short session
|
||||||
session["expires_at"] = (datetime.utcnow() + timedelta(hours=8)).isoformat()
|
session["expires_at"] = (datetime.utcnow() + timedelta(hours=8)).isoformat()
|
||||||
|
|
||||||
|
# Async update cache after login
|
||||||
|
from threading import Thread
|
||||||
|
thread = Thread(target=fetch_all_projects_for_user, args=(uid,))
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
|
||||||
return jsonify({"ok": True})
|
return jsonify({"ok": True})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("[ERR] session_login:", e)
|
print("[ERR] session_login:", e)
|
||||||
@@ -224,48 +283,21 @@ def dashboard():
|
|||||||
if not case_email:
|
if not case_email:
|
||||||
return redirect(url_for("welcome"))
|
return redirect(url_for("welcome"))
|
||||||
|
|
||||||
# 1) Bearer token
|
# Check cache first
|
||||||
bearer = get_filevine_bearer()
|
cached_projects = project_cache.get_projects()
|
||||||
|
if cached_projects is not None:
|
||||||
# 2) List projects (all pages)
|
detailed_rows = cached_projects
|
||||||
projects = list_all_projects(bearer)
|
print("USING CACHE")
|
||||||
|
else:
|
||||||
# 3) Filter to ProjectEmailAddress == caseEmail
|
# Fetch and cache projects
|
||||||
#filtered = [p for p in projects if str(p.get("ProjectEmailAddress", "")).lower() == str(case_email).lower()]
|
detailed_rows = fetch_all_projects_for_user(uid)
|
||||||
|
print("FETCHING")
|
||||||
filtered = projects
|
|
||||||
# 4) Fetch details for each
|
|
||||||
detailed_rows = []
|
|
||||||
for p in filtered:
|
|
||||||
pid = (p.get("projectId") or {}).get("native")
|
|
||||||
c = fetch_client(bearer, (p.get("clientId") or {}).get("native"))
|
|
||||||
cs = fetch_contacts(bearer, pid)
|
|
||||||
print("CS IS", cs)
|
|
||||||
|
|
||||||
if pid is None:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
detail = fetch_project_detail(bearer, pid)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[WARN] detail fetch failed for {pid}: {e}")
|
|
||||||
detail = {}
|
|
||||||
row = {
|
|
||||||
"client": c.get("firstName"),
|
|
||||||
"matter_description": p.get("projectName"),
|
|
||||||
"contacts": cs,
|
|
||||||
"ProjectEmailAddress": p.get("projectEmailAddress"),
|
|
||||||
"Number": p.get("number"),
|
|
||||||
"IncidentDate": (p.get("incidentDate") or detail.get("incidentDate")),
|
|
||||||
"ProjectId": pid,
|
|
||||||
"ProjectName": p.get("projectName") or detail.get("projectName"),
|
|
||||||
"ProjectUrl": p.get("projectUrl") or detail.get("projectUrl"),
|
|
||||||
}
|
|
||||||
detailed_rows.append(row)
|
|
||||||
|
|
||||||
|
print("HI", len(detailed_rows))
|
||||||
# 5) Render table
|
# 5) Render table
|
||||||
return render_template("dashboard.html", rows=detailed_rows, case_email=case_email)
|
return render_template("dashboard.html", rows=detailed_rows, case_email=case_email)
|
||||||
|
|
||||||
|
|
||||||
# GAE compatibility
|
# GAE compatibility
|
||||||
if __name__ == "__main__"
|
if __name__ == "__main__":
|
||||||
app.run(debug=True, host="0.0.0.0", port=8080)
|
app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", "5004")))
|
||||||
|
|||||||
42
cache.py
Normal file
42
cache.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# In-memory cache for Filevine projects
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
class ProjectCache:
|
||||||
|
def __init__(self):
|
||||||
|
self._cache = {}
|
||||||
|
self._lock = threading.Lock()
|
||||||
|
self._last_updated = None
|
||||||
|
self._is_updating = False
|
||||||
|
|
||||||
|
def get_projects(self):
|
||||||
|
"""Get cached projects if they exist and are not expired (15 minutes)"""
|
||||||
|
with self._lock:
|
||||||
|
if not self._cache or not self._last_updated:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Check if cache is older than 15 minutes
|
||||||
|
if datetime.now() - self._last_updated > timedelta(minutes=15):
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self._cache.copy()
|
||||||
|
|
||||||
|
def set_projects(self, projects):
|
||||||
|
"""Set projects in cache with current timestamp"""
|
||||||
|
with self._lock:
|
||||||
|
self._cache = projects.copy() if projects else {}
|
||||||
|
self._last_updated = datetime.now()
|
||||||
|
|
||||||
|
def is_updating(self):
|
||||||
|
"""Check if cache is currently being updated"""
|
||||||
|
with self._lock:
|
||||||
|
return self._is_updating
|
||||||
|
|
||||||
|
def set_updating(self, updating):
|
||||||
|
"""Set the updating status"""
|
||||||
|
with self._lock:
|
||||||
|
self._is_updating = updating
|
||||||
|
|
||||||
|
# Global cache instance
|
||||||
|
project_cache = ProjectCache()
|
||||||
Reference in New Issue
Block a user