Files
rothbard/models/project_model.py
Bryce 662be72f6a feat: Implement comprehensive project data model and synchronization system
- Added ProjectModel class in models/project_model.py to define structure for Filevine project data with proper type hints and conversion methods (to_dict/from_dict)
- Implemented get_firestore_document() helper function in app.py for retrieving specific Firestore documents
- Enhanced dashboard pagination in app.py with improved error handling and debugging output for property contacts and project IDs
- Overhauled sync.py with:
  * Parallel processing using ThreadPoolExecutor for efficient project synchronization
  * Comprehensive extraction of project data from Filevine forms (newFileReview, datesAndDeadlines, propertyInfo, etc.)
  * Improved error handling and logging throughout the sync process
  * Proper handling of date conversions and field mappings from Filevine to Firestore
  * Added property contacts email extraction and viewing_emails array population
  * Added support for filtering projects by specific ProjectId (15914808) for targeted sync
- Added proper initialization of Filevine client in worker threads using thread-local storage
- Improved handling of optional fields and default values in ProjectModel
- Added detailed logging for progress tracking during synchronization

This implementation enables reliable synchronization of Filevine project data to Firestore with proper data modeling and error handling, supporting the dashboard's data requirements.
2025-11-09 20:21:53 -08:00

250 lines
12 KiB
Python

"""
Shared data model for Project entities used across the application.
This defines the structure of project data that is fetched from Filevine and stored in Firestore.
"""
from typing import List, Dict, Any, Optional
from datetime import datetime
import pytz
class ProjectModel:
"""
Data model for a Filevine project with all its associated fields.
This model defines the structure that will be used for Firestore storage
and API responses.
"""
def __init__(self,
client: str = "",
matter_description: str = "",
defendant_1: str = "",
matter_open: str = "",
notice_type: str = "",
case_number: str = "",
premises_address: str = "",
premises_city: str = "",
responsible_attorney: str = "",
staff_person: str = "",
staff_person_2: str = "",
phase_name: str = "",
completed_tasks: List[Dict[str, Any]] = None,
pending_tasks: List[Dict[str, Any]] = None,
notice_service_date: str = "",
notice_expiration_date: str = "",
case_field_date: str = "",
daily_rent_damages: str = "",
default_date: str = "",
demurrer_hearing_date: str = "",
motion_to_strike_hearing_date: str = "",
motion_to_quash_hearing_date: str = "",
other_motion_hearing_date: str = "",
msc_date: str = "",
msc_time: str = "",
msc_address: str = "",
msc_div_dept_room: str = "",
trial_date: str = "",
trial_time: str = "",
trial_address: str = "",
trial_div_dept_room: str = "",
final_result: str = "",
date_of_settlement: str = "",
final_obligation: str = "",
def_comply_stip: str = "",
judgment_date: str = "",
writ_issued_date: str = "",
scheduled_lockout: str = "",
oppose_stays: str = "",
premises_safety: str = "",
matter_gate_code: str = "",
date_possession_recovered: str = "",
attorney_fees: str = "",
costs: str = "",
documents_url: str = "",
service_attempt_date_1: str = "",
contacts: List[Dict[str, Any]] = None,
project_email_address: str = "",
number: str = "",
incident_date: str = "",
project_id: str = "",
project_name: str = "",
project_url: str = "",
property_contacts: Dict[str, Any] = None,
viewing_emails: List[str] = None
):
self.client = client
self.matter_description = matter_description
self.defendant_1 = defendant_1
self.matter_open = matter_open
self.notice_type = notice_type
self.case_number = case_number
self.premises_address = premises_address
self.premises_city = premises_city
self.responsible_attorney = responsible_attorney
self.staff_person = staff_person
self.staff_person_2 = staff_person_2
self.phase_name = phase_name
self.completed_tasks = completed_tasks or []
self.pending_tasks = pending_tasks or []
self.notice_service_date = notice_service_date
self.notice_expiration_date = notice_expiration_date
self.case_field_date = case_field_date
self.daily_rent_damages = daily_rent_damages
self.default_date = default_date
self.demurrer_hearing_date = demurrer_hearing_date
self.motion_to_strike_hearing_date = motion_to_strike_hearing_date
self.motion_to_quash_hearing_date = motion_to_quash_hearing_date
self.other_motion_hearing_date = other_motion_hearing_date
self.msc_date = msc_date
self.msc_time = msc_time
self.msc_address = msc_address
self.msc_div_dept_room = msc_div_dept_room
self.trial_date = trial_date
self.trial_time = trial_time
self.trial_address = trial_address
self.trial_div_dept_room = trial_div_dept_room
self.final_result = final_result
self.date_of_settlement = date_of_settlement
self.final_obligation = final_obligation
self.def_comply_stip = def_comply_stip
self.judgment_date = judgment_date
self.writ_issued_date = writ_issued_date
self.scheduled_lockout = scheduled_lockout
self.oppose_stays = oppose_stays
self.premises_safety = premises_safety
self.matter_gate_code = matter_gate_code
self.date_possession_recovered = date_possession_recovered
self.attorney_fees = attorney_fees
self.costs = costs
self.documents_url = documents_url
self.service_attempt_date_1 = service_attempt_date_1
self.contacts = contacts or []
self.project_email_address = project_email_address
self.number = number
self.incident_date = incident_date
self.project_id = project_id
self.project_name = project_name
self.project_url = project_url
self.property_contacts = property_contacts or {}
self.viewing_emails = viewing_emails or []
def to_dict(self) -> Dict[str, Any]:
"""Convert the ProjectModel to a dictionary for Firestore storage."""
return {
"client": self.client,
"matter_description": self.matter_description,
"defendant_1": self.defendant_1,
"matter_open": self.matter_open,
"notice_type": self.notice_type,
"case_number": self.case_number,
"premises_address": self.premises_address,
"premises_city": self.premises_city,
"responsible_attorney": self.responsible_attorney,
"staff_person": self.staff_person,
"staff_person_2": self.staff_person_2,
"phase_name": self.phase_name,
"completed_tasks": self.completed_tasks,
"pending_tasks": self.pending_tasks,
"notice_service_date": self.notice_service_date,
"notice_expiration_date": self.notice_expiration_date,
"case_field_date": self.case_field_date,
"daily_rent_damages": self.daily_rent_damages,
"default_date": self.default_date,
"demurrer_hearing_date": self.demurrer_hearing_date,
"motion_to_strike_hearing_date": self.motion_to_strike_hearing_date,
"motion_to_quash_hearing_date": self.motion_to_quash_hearing_date,
"other_motion_hearing_date": self.other_motion_hearing_date,
"msc_date": self.msc_date,
"msc_time": self.msc_time,
"msc_address": self.msc_address,
"msc_div_dept_room": self.msc_div_dept_room,
"trial_date": self.trial_date,
"trial_time": self.trial_time,
"trial_address": self.trial_address,
"trial_div_dept_room": self.trial_div_dept_room,
"final_result": self.final_result,
"date_of_settlement": self.date_of_settlement,
"final_obligation": self.final_obligation,
"def_comply_stip": self.def_comply_stip,
"judgment_date": self.judgment_date,
"writ_issued_date": self.writ_issued_date,
"scheduled_lockout": self.scheduled_lockout,
"oppose_stays": self.oppose_stays,
"premises_safety": self.premises_safety,
"matter_gate_code": self.matter_gate_code,
"date_possession_recovered": self.date_possession_recovered,
"attorney_fees": self.attorney_fees,
"costs": self.costs,
"documents_url": self.documents_url,
"service_attempt_date_1": self.service_attempt_date_1,
"contacts": self.contacts,
"ProjectEmailAddress": self.project_email_address,
"Number": self.number,
"IncidentDate": self.incident_date,
"ProjectId": self.project_id,
"ProjectName": self.project_name,
"ProjectUrl": self.project_url,
"property_contacts": self.property_contacts,
"viewing_emails": self.viewing_emails
}
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'ProjectModel':
"""Create a ProjectModel instance from a dictionary (e.g., from Firestore)."""
return cls(
client=data.get("client", ""),
matter_description=data.get("matter_description", ""),
defendant_1=data.get("defendant_1", ""),
matter_open=data.get("matter_open", ""),
notice_type=data.get("notice_type", ""),
case_number=data.get("case_number", ""),
premises_address=data.get("premises_address", ""),
premises_city=data.get("premises_city", ""),
responsible_attorney=data.get("responsible_attorney", ""),
staff_person=data.get("staff_person", ""),
staff_person_2=data.get("staff_person_2", ""),
phase_name=data.get("phase_name", ""),
completed_tasks=data.get("completed_tasks", []),
pending_tasks=data.get("pending_tasks", []),
notice_service_date=data.get("notice_service_date", ""),
notice_expiration_date=data.get("notice_expiration_date", ""),
case_field_date=data.get("case_field_date", ""),
daily_rent_damages=data.get("daily_rent_damages", ""),
default_date=data.get("default_date", ""),
demurrer_hearing_date=data.get("demurrer_hearing_date", ""),
motion_to_strike_hearing_date=data.get("motion_to_strike_hearing_date", ""),
motion_to_quash_hearing_date=data.get("motion_to_quash_hearing_date", ""),
other_motion_hearing_date=data.get("other_motion_hearing_date", ""),
msc_date=data.get("msc_date", ""),
msc_time=data.get("msc_time", ""),
msc_address=data.get("msc_address", ""),
msc_div_dept_room=data.get("msc_div_dept_room", ""),
trial_date=data.get("trial_date", ""),
trial_time=data.get("trial_time", ""),
trial_address=data.get("trial_address", ""),
trial_div_dept_room=data.get("trial_div_dept_room", ""),
final_result=data.get("final_result", ""),
date_of_settlement=data.get("date_of_settlement", ""),
final_obligation=data.get("final_obligation", ""),
def_comply_stip=data.get("def_comply_stip", ""),
judgment_date=data.get("judgment_date", ""),
writ_issued_date=data.get("writ_issued_date", ""),
scheduled_lockout=data.get("scheduled_lockout", ""),
oppose_stays=data.get("oppose_stays", ""),
premises_safety=data.get("premises_safety", ""),
matter_gate_code=data.get("matter_gate_code", ""),
date_possession_recovered=data.get("date_possession_recovered", ""),
attorney_fees=data.get("attorney_fees", ""),
costs=data.get("costs", ""),
documents_url=data.get("documents_url", ""),
service_attempt_date_1=data.get("service_attempt_date_1", ""),
contacts=data.get("contacts", []),
project_email_address=data.get("ProjectEmailAddress", ""),
number=data.get("Number", ""),
incident_date=data.get("IncidentDate", ""),
project_id=data.get("ProjectId", ""),
project_name=data.get("ProjectName", ""),
project_url=data.get("ProjectUrl", ""),
property_contacts=data.get("property_contacts", {}),
viewing_emails=data.get("viewing_emails", [])
)