This commit is contained in:
2025-11-07 10:02:33 -08:00
parent 1ad8c988de
commit 30c5613937
4 changed files with 70 additions and 33 deletions

80
app.py
View File

@@ -2,8 +2,9 @@ import json
import os import os
from functools import wraps from functools import wraps
from datetime import datetime, timedelta from datetime import datetime, timedelta
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
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
@@ -92,6 +93,35 @@ 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 convert_to_pacific_time(date_str):
"""Convert UTC date string to Pacific Time and format as YYYY-MM-DD.
Args:
date_str (str): UTC date string in ISO 8601 format (e.g., "2025-10-24T19:20:22.377Z")
Returns:
str: Date formatted as YYYY-MM-DD in Pacific Time, or empty string if input is empty
"""
if not date_str:
return ''
try:
# Parse the UTC datetime
utc_time = datetime.fromisoformat(date_str.replace('Z', '+00:00'))
# Set timezone to UTC
utc_time = utc_time.replace(tzinfo=pytz.UTC)
# Convert to Pacific Time
pacific_time = utc_time.astimezone(pytz.timezone('America/Los_Angeles'))
# Format as YYYY-MM-DD
return pacific_time.strftime('%Y-%m-%d')
except (ValueError, AttributeError) as e:
print(f"[WARN] Date conversion failed for '{date_str}': {e}")
return ''
def fetch_all_projects(): 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"""
@@ -130,12 +160,12 @@ def fetch_all_projects():
lease_info_np = fetch_form(bearer, pid, "leaseInfoNP") or {} lease_info_np = fetch_form(bearer, pid, "leaseInfoNP") or {}
completed_tasks = [{"description": x.get("body") , completed_tasks = [{"description": x.get("body") ,
"completed": x.get("completedDate")} "completed": convert_to_pacific_time(x.get("completedDate"))}
for x in fetch_project_tasks(bearer, pid).get("items") for x in fetch_project_tasks(bearer, pid).get("items")
if x.get("isCompleted")] if x.get("isCompleted")]
pending_tasks = [{"description": x.get("body") , pending_tasks = [{"description": x.get("body") ,
"completed": x.get("completedDate")} "completed": convert_to_pacific_time(x.get("completedDate"))}
for x in fetch_project_tasks(bearer, pid).get("items") for x in fetch_project_tasks(bearer, pid).get("items")
if not x.get("isCompleted")] if not x.get("isCompleted")]
team = fetch_project_team(bearer, pid) team = fetch_project_team(bearer, pid)
@@ -153,31 +183,31 @@ def fetch_all_projects():
), '') ), '')
# Extract notice service and expiration dates # Extract notice service and expiration dates
notice_service_date = new_file_review.get("noticeServiceDate") or '' notice_service_date = convert_to_pacific_time(new_file_review.get("noticeServiceDate")) or ''
notice_expiration_date = new_file_review.get("noticeExpirationDate") or '' notice_expiration_date = convert_to_pacific_time(new_file_review.get("noticeExpirationDate")) or ''
# Extract daily rent damages # Extract daily rent damages
daily_rent_damages = lease_info_np.get("dailyRentDamages") or dates_and_deadlines.get("dailyRentDamages") or '' daily_rent_damages = lease_info_np.get("dailyRentDamages") or dates_and_deadlines.get("dailyRentDamages") or ''
# Extract default date # Extract default date
default_date = dates_and_deadlines.get("defaultDate") or '' default_date = convert_to_pacific_time(dates_and_deadlines.get("defaultDate")) or ''
case_filed_date = dates_and_deadlines.get("dateCaseFiled") or '' case_filed_date = convert_to_pacific_time(dates_and_deadlines.get("dateCaseFiled")) or ''
# Extract motion hearing dates # Extract motion hearing dates
demurrer_hearing_date = dates_and_deadlines.get("demurrerHearingDate") or '' demurrer_hearing_date = convert_to_pacific_time(dates_and_deadlines.get("demurrerHearingDate")) or ''
motion_to_strike_hearing_date = dates_and_deadlines.get("mTSHearingDate") or '' motion_to_strike_hearing_date = convert_to_pacific_time(dates_and_deadlines.get("mTSHearingDate")) or ''
motion_to_quash_hearing_date = dates_and_deadlines.get("mTQHearingDate") or '' motion_to_quash_hearing_date = convert_to_pacific_time(dates_and_deadlines.get("mTQHearingDate")) or ''
other_motion_hearing_date = dates_and_deadlines.get("otherMotion1HearingDate") or '' other_motion_hearing_date = convert_to_pacific_time(dates_and_deadlines.get("otherMotion1HearingDate")) or ''
# Extract MSC details # Extract MSC details
msc_date = dates_and_deadlines.get("mSCDate") or '' msc_date = convert_to_pacific_time(dates_and_deadlines.get("mSCDate")) or ''
msc_time = dates_and_deadlines.get("mSCTime") or '' msc_time = dates_and_deadlines.get("mSCTime") or '' # Time field, not converting
msc_address = dates_and_deadlines.get("mSCAddress") or '' msc_address = dates_and_deadlines.get("mSCAddress") or ''
msc_div_dept_room = dates_and_deadlines.get("mSCDeptDiv") or '' msc_div_dept_room = dates_and_deadlines.get("mSCDeptDiv") or ''
# Extract trial details # Extract trial details
trial_date = dates_and_deadlines.get("trialDate") or '' trial_date = convert_to_pacific_time(dates_and_deadlines.get("trialDate")) or ''
trial_time = dates_and_deadlines.get("trialTime") or '' trial_time = dates_and_deadlines.get("trialTime") or '' # Time field, not converting
trial_address = dates_and_deadlines.get("trialAddress") or '' trial_address = dates_and_deadlines.get("trialAddress") or ''
trial_div_dept_room = dates_and_deadlines.get("trialDeptDivRoom") or '' trial_div_dept_room = dates_and_deadlines.get("trialDeptDivRoom") or ''
@@ -185,16 +215,16 @@ def fetch_all_projects():
final_result = dates_and_deadlines.get("finalResultOfTrialMSCCa") or '' final_result = dates_and_deadlines.get("finalResultOfTrialMSCCa") or ''
# Extract settlement details # Extract settlement details
date_of_settlement = dates_and_deadlines.get("dateOfStipulation") or '' date_of_settlement = convert_to_pacific_time(dates_and_deadlines.get("dateOfStipulation")) or ''
final_obligation = dates_and_deadlines.get("finalObligationUnderTheStip") or '' final_obligation = dates_and_deadlines.get("finalObligationUnderTheStip") or ''
def_comply_stip = dates_and_deadlines.get("defendantsComplyWithStip") or '' def_comply_stip = dates_and_deadlines.get("defendantsComplyWithStip") or ''
# Extract judgment and writ details # Extract judgment and writ details
judgment_date = dates_and_deadlines.get("dateOfJudgment") or '' judgment_date = convert_to_pacific_time(dates_and_deadlines.get("dateOfJudgment")) or ''
writ_issued_date = dates_and_deadlines.get("writIssuedDate") or '' writ_issued_date = convert_to_pacific_time(dates_and_deadlines.get("writIssuedDate")) or ''
# Extract lockout and stay details # Extract lockout and stay details
scheduled_lockout = dates_and_deadlines.get("sheriffScheduledDate") or '' scheduled_lockout = convert_to_pacific_time(dates_and_deadlines.get("sheriffScheduledDate")) or ''
oppose_stays = dates_and_deadlines.get("opposeStays") or '' oppose_stays = dates_and_deadlines.get("opposeStays") or ''
# Extract premises safety and entry code # Extract premises safety and entry code
@@ -202,7 +232,7 @@ def fetch_all_projects():
matter_gate_code = property_info.get("propertyEntryCodeOrInstructions") or '' matter_gate_code = property_info.get("propertyEntryCodeOrInstructions") or ''
# Extract possession recovered date # Extract possession recovered date
date_possession_recovered = dates_and_deadlines.get("datePossessionRecovered") or '' date_possession_recovered = convert_to_pacific_time(dates_and_deadlines.get("datePossessionRecovered")) or ''
# Extract attorney fees and costs # Extract attorney fees and costs
attorney_fees = fees_and_costs.get("attorneyFeesTotal") or '' attorney_fees = fees_and_costs.get("attorneyFeesTotal") or ''
@@ -212,7 +242,7 @@ def fetch_all_projects():
"client": c.get("firstName"), "client": c.get("firstName"),
"matter_description": p.get("projectName"), "matter_description": p.get("projectName"),
"defendant_1": defendant_one.get('fullName', 'Unknown'), "defendant_1": defendant_one.get('fullName', 'Unknown'),
"matter_open": dates_and_deadlines.get("dateCaseFiled") or p.get("createdDate"), "matter_open": convert_to_pacific_time(dates_and_deadlines.get("dateCaseFiled") or p.get("createdDate")),
"notice_type": new_file_review.get("noticeType", '') or '', "notice_type": new_file_review.get("noticeType", '') or '',
"case_number": dates_and_deadlines.get('caseNumber', '') or '', "case_number": dates_and_deadlines.get('caseNumber', '') or '',
"premises_address": property_info.get("premisesAddressWithUnit") or '', "premises_address": property_info.get("premisesAddressWithUnit") or '',
@@ -254,11 +284,11 @@ def fetch_all_projects():
"attorney_fees": attorney_fees, "attorney_fees": attorney_fees,
"costs": costs, "costs": costs,
"documents_url": matter_overview.get('documentShareFolderURL') or '', "documents_url": matter_overview.get('documentShareFolderURL') or '',
"service_attempt_date_1": next(iter(service_info), {}).get('serviceDate'), "service_attempt_date_1": convert_to_pacific_time(next(iter(service_info), {}).get('serviceDate')),
"contacts": cs, "contacts": cs,
"ProjectEmailAddress": p.get("projectEmailAddress"), "ProjectEmailAddress": p.get("projectEmailAddress"),
"Number": p.get("number"), "Number": p.get("number"),
"IncidentDate": (p.get("incidentDate") or detail.get("incidentDate")), "IncidentDate": convert_to_pacific_time(p.get("incidentDate") or detail.get("incidentDate")),
"ProjectId": pid, "ProjectId": pid,
"ProjectName": p.get("projectName") or detail.get("projectName"), "ProjectName": p.get("projectName") or detail.get("projectName"),
"ProjectUrl": p.get("projectUrl") or detail.get("projectUrl"), "ProjectUrl": p.get("projectUrl") or detail.get("projectUrl"),

View File

@@ -4,3 +4,4 @@ python-dotenv==1.0.1
requests==2.32.3 requests==2.32.3
itsdangerous==2.2.0 itsdangerous==2.2.0
gunicorn==23.0.0 gunicorn==23.0.0
pytz

View File

@@ -9,10 +9,10 @@
<!-- Alpine.js --> <!-- Alpine.js -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head> </head>
<body class="min-h-screen bg-slate-50 text-slate-900"> <body class="h-screen flex flex-col m-0 bg-slate-50 text-slate-900">
<header class="border-b bg-white"> <header class="border-b bg-white">
<div class="max-w-5xl mx-auto p-4 flex items-center justify-between"> <div class="max-w-5xl mx-auto p-4 flex items-center justify-between">
<a href="/" class="font-semibold">Filevine Demo</a> <a href="/" class="font-semibold">Rothbard Law Group - Cases</a>
<nav class="space-x-4"> <nav class="space-x-4">
{% if session.uid %} {% if session.uid %}
<a href="/dashboard" class="text-sm text-slate-600 hover:text-slate-900">Dashboard</a> <a href="/dashboard" class="text-sm text-slate-600 hover:text-slate-900">Dashboard</a>
@@ -23,7 +23,7 @@
</nav> </nav>
</div> </div>
</header> </header>
<main class="max-w-full mx-auto p-6 px-4 lg:px-6"> <main class="flex-1 overflow-auto p-6 px-4 lg:px-6">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</main> </main>
</body> </body>

View File

@@ -1,10 +1,12 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="h-[80vh] flex flex-col m-4"> <div class="h-full flex flex-col">
<h1 class="text-xl font-semibold mb-4">Projects for {{ case_email }}</h1> <h1 class="text-xl font-semibold mb-4">Projects for {{ case_email }}</h1>
<div class=" w-full flex-grow overflow-scroll rounded-2xl overflow-hidden">
<div class="overflow-scroll">
<table class="w-full whitespace-nowrap shadow-md border border-slate-200"> <table class="w-full whitespace-nowrap shadow-md border border-slate-200">
<thead class="bg-gradient-to-r from-blue-600 to-blue-700 text-left text-sm sticky top-0 z-10 border-b border-blue-800 text-white font-medium"> <thead class=" text-left text-sm sticky top-0 z-10 border-b border-blue-800 text-white font-medium"
style="background-color: rgb(89, 121, 142);">
<tr> <tr>
<th class="px-4 py-3">Matter Num</th> <th class="px-4 py-3">Matter Num</th>
<th class="px-4 py-3">Client / Property</th> <th class="px-4 py-3">Client / Property</th>
@@ -235,7 +237,11 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
<!-- <!--
<div class="bg-white shadow rounded-2xl overflow-scroll"> <div class="flex flex-col m-4">
<div class=" w-full flex-grow overflow-scroll rounded-2xl overflow-hidden">
</div>
--> -->
{% endblock %} {% endblock %}