From c3108ff68c32538e617b8d2edefd8559814668f4 Mon Sep 17 00:00:00 2001 From: Bryce Date: Fri, 5 Dec 2025 00:10:08 -0800 Subject: [PATCH] Improvement --- app.py | 147 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 78 insertions(+), 69 deletions(-) diff --git a/app.py b/app.py index 2198bcf..7b256cc 100644 --- a/app.py +++ b/app.py @@ -29,6 +29,70 @@ def login_required(view): return view(*args, **kwargs) return wrapped +def projects_for(profile, case_email_match, per_page, offset): + """ + Filter projects based on user profile and case_email query string argument. + + Args: + profile (dict): User profile containing 'enabled', 'is_admin', and 'case_email' fields + case_email_match (str): Case email from query string argument, or None + + Returns: + list: List of project dictionaries that match the filtering criteria + """ + is_admin = profile.get("is_admin", False) + + if not profile.get("enabled"): + return ([], 0) + + # Query Firestore for projects where case_email is in viewing_emails array + try: + cnt = 0 + if is_admin: + if case_email_match: + projects_ref = db.collection("projects").where("viewing_emails", "array_contains", case_email_match.lower()) + # Get filtered document IDs using client-side filtering with partial match + z = db.collection("projects").select(["viewing_emails", "matter_description", "id"]) + # Get all matching documents with their IDs and descriptions + matching_docs = [(x.id, x.to_dict().get('matter_description', '')) for x in z.stream() + if any(case_email_match.lower() in email.lower() for email in x.to_dict().get('viewing_emails', []))] + count = len(matching_docs) + + # Sort by matter_description + matching_docs.sort(key=lambda x: x[1].lower()) + + # Extract just the IDs after sorting + filtered_ids = [doc_id for doc_id, _ in matching_docs] + + # Apply client-side pagination + filtered_ids = filtered_ids[offset:offset + per_page] + + print(f"Filtered document IDs (partial match, sorted, paginated): {filtered_ids}") + projects_ref = db.collection("projects") + projects = [] + for doc_id in filtered_ids: + doc = projects_ref.document(doc_id).get() + if doc.exists: + projects.append(doc.to_dict()) + + return (projects, count) + + else: + projects_ref = db.collection("projects") + + else: + if not profile.get("case_email"): + return ([], 0) + projects_ref = db.collection("projects").where("viewing_emails", "array_contains", profile.get("case_email").to_lower()) + cnt = int(projects_ref.count().get()[0][0].value) + projects = [] + for doc in projects_ref.order_by("matter_description").limit(per_page).offset(offset).stream(): + projects.append(doc.to_dict()) + return (projects, cnt) + except Exception as e: + print(f"[ERROR] Failed to query projects: {e}") + return ([], 0) + @app.context_processor @@ -127,73 +191,36 @@ def dashboard(page=1): return redirect(url_for("welcome")) is_admin = profile.get("is_admin") - case_email = None - if not is_admin: - case_email = profile.get("case_email") - if not case_email: - return redirect(url_for("welcome")) - if is_admin and request.args.get('case_email'): - case_email = request.args.get('case_email').lower() - # Validate email format - if '@' not in case_email: - return abort(400, "Invalid email format") - # Pagination settings per_page = int(request.args.get('per_page', 25)) offset = (page - 1) * per_page - query = None - - # Get total count efficiently using a count aggregation query - try: - # Firestore doesn't have a direct count() method, so we need to count documents - import time - start_time = time.time() - projects_ref = db.collection("projects") - # Filter projects where case_email is in viewing_emails array - if case_email: - query = projects_ref.where("viewing_emails", "array_contains", case_email.lower()) - else: - query = projects_ref - - total_projects = int(query.count().get()[0][0].value) - end_time = time.time() - print(f"Filtered projects count: {total_projects} (took {end_time - start_time:.2f}s)") - except Exception as e: - print(f"[WARN] Failed to get filtered count: {e}") - total_projects = 0 + case_email_match = None + if is_admin and request.args.get('case_email'): + case_email_match = request.args.get('case_email') + if not is_admin and not profile.get('case_email'): + return redirect(url_for("welcome")) + paginated_rows, total_projects = projects_for(profile, case_email_match, per_page, offset) # Calculate pagination total_pages = (total_projects + per_page - 1) // per_page # Ceiling division # Read only the current page from Firestore using limit() and offset() import time - start_time = time.time() - # Filter projects where case_email is in viewing_emails array - if case_email: - projects_ref = db.collection("projects").where("viewing_emails", "array_contains", case_email.lower()).order_by("matter_description").limit(per_page).offset(offset) - else: - projects_ref = db.collection("projects").order_by("matter_description").limit(per_page).offset(offset) - - docs = projects_ref.stream() - paginated_rows = [] - - for doc in docs: - paginated_rows.append(doc.to_dict()) - - end_time = time.time() - print(f"Retrieved {len(paginated_rows)} projects from Firestore (page {page} of {total_pages}) in {end_time - start_time:.2f}s") + print(f"Retrieved {len(paginated_rows)} projects from Firestore") from pprint import pprint pprint([p['property_contacts'] for p in paginated_rows if p['property_contacts'].get('propertyManager1', None)]) pprint([p['ProjectId'] for p in paginated_rows ]) # Render table with pagination data return render_template("dashboard.html", rows=paginated_rows, - case_email=case_email, + case_email=case_email_match, current_page=page, total_pages=total_pages, total_projects=total_projects, per_page=per_page) + + @app.route("/dashboard/export_xls") @login_required @@ -206,33 +233,15 @@ def dashboard_export_xls(): is_admin = profile.get("is_admin") case_email = None - if not is_admin: - case_email = profile.get("case_email") - if not case_email: - return redirect(url_for("welcome")) - if is_admin and request.args.get('case_email'): - case_email = request.args.get('case_email').lower() - # Validate email format - if '@' not in case_email: - return abort(400, "Invalid email format") + if not is_admin and not profile.get('case_email'): + return redirect(url_for("welcome")) # Get all projects without pagination try: - projects_ref = db.collection("projects") + all_rows, cnt = projects_for(profile, case_email, 10000, 0) # Filter projects where case_email is in viewing_emails array - if case_email: - projects_ref = projects_ref.where("viewing_emails", "array_contains", case_email.lower()) - # Order by matter_description to maintain consistent ordering - projects_ref = projects_ref.order_by("matter_description") - - docs = projects_ref.stream() - all_rows = [] - - for doc in docs: - all_rows.append(doc.to_dict()) - - print(f"Retrieved {len(all_rows)} projects from Firestore for XLS export") + print(f"Retrieved {cnt} projects from Firestore for XLS export") # Create workbook and worksheet wb = Workbook()