-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathnew_format.py
More file actions
188 lines (149 loc) · 6.78 KB
/
new_format.py
File metadata and controls
188 lines (149 loc) · 6.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#!/usr/bin/env python3
"""
New Format Parser - Reads the new spreadsheet format.
"""
from datetime import date
from typing import Optional, List, Dict, Set, Tuple
from sheets_client import GoogleSheetsClient
from data_models import AttendanceRecord, MembershipRecord
# Google Sheets Configuration
SPREADSHEET_ID = "1GcFgxK6xaJdJoqRhfeGAHvXA2SjTCyG08Dbs7aGE8_4"
ATTENDANCE_SHEET = "ATTENDANCE"
MEMBERSHIP_SHEET = "MEMBERSHIP"
class NewFormatParser:
"""Main class for managing attendance and membership data in the new format."""
def __init__(self, client: Optional[GoogleSheetsClient] = None):
"""
Initialize the new format parser.
Args:
client: GoogleSheetsClient instance. If None, a new one will be created.
"""
self.client = client if client is not None else GoogleSheetsClient()
self.members_by_eid: Dict[str, MembershipRecord] = {}
self.unique_attendance: Set[Tuple[date, str, str, str]] = set()
def authenticate(self) -> None:
"""
Authenticate with Google Sheets API using OAuth2 flow.
Opens browser for user to login with Google account.
"""
if not self.client.is_authenticated:
self.client.authenticate()
def get_attendance_records(self) -> List[AttendanceRecord]:
"""
Fetch all attendance records from the spreadsheet.
Also populates unique_attendance set with (date, eid, activity, subactivity) tuples.
Returns:
List of AttendanceRecord objects
"""
if not self.client.is_authenticated:
raise RuntimeError("Must authenticate before fetching records")
all_values = self.client.get_worksheet_data(SPREADSHEET_ID, ATTENDANCE_SHEET)
# Skip header row
records = []
self.unique_attendance.clear()
for row in all_values[1:]:
if row[0]: # Skip empty rows
try:
record = AttendanceRecord.from_row(row)
records.append(record)
# Add to unique attendance set as (date, eid, activity, subactivity)
attendance_tuple = (
record.timestamp.date(),
record.eid,
record.activity,
record.subactivity
)
self.unique_attendance.add(attendance_tuple)
except (ValueError, IndexError) as e:
print(f"Warning: Skipping invalid row: {row}. Error: {e}")
return records
def get_membership_records(self) -> List[MembershipRecord]:
"""
Fetch all membership records from the spreadsheet.
Also populates members_by_eid dictionary with EID as key.
Returns:
List of MembershipRecord objects
"""
if not self.client.is_authenticated:
raise RuntimeError("Must authenticate before fetching records")
all_values = self.client.get_worksheet_data(SPREADSHEET_ID, MEMBERSHIP_SHEET)
# Skip header row
records = []
self.members_by_eid.clear()
for row in all_values[1:]:
if row[0]: # Skip empty rows
try:
record = MembershipRecord.from_row(row)
records.append(record)
# Store in dictionary by EID (latest record wins if duplicates)
self.members_by_eid[record.eid] = record
except (ValueError, IndexError) as e:
print(f"Warning: Skipping invalid row: {row}. Error: {e}")
return records
def get_member_by_eid(self, eid: str) -> Optional[MembershipRecord]:
"""
Get a member's record by their EID from the cached dictionary.
Args:
eid: The member's EID
Returns:
MembershipRecord if found, None otherwise
"""
# Use cached dictionary if available
if self.members_by_eid:
return self.members_by_eid.get(eid)
# Otherwise fetch and cache
self.get_membership_records()
return self.members_by_eid.get(eid)
def get_attendance_by_eid(self, eid: str) -> List[AttendanceRecord]:
"""
Get all attendance records for a specific EID.
Args:
eid: The member's EID
Returns:
List of AttendanceRecord objects for the given EID
"""
all_attendance = self.get_attendance_records()
return [record for record in all_attendance if record.eid == eid]
def get_attendance_by_activity(self, activity: str) -> List[AttendanceRecord]:
"""
Get all attendance records for a specific activity.
Args:
activity: The activity name
Returns:
List of AttendanceRecord objects for the given activity
"""
all_attendance = self.get_attendance_records()
return [record for record in all_attendance if record.activity == activity]
def main() -> None:
"""Main function to demonstrate usage."""
# Initialize the parser (uses credentials.json by default)
parser = NewFormatParser()
try:
# Authenticate
print("Authenticating with Google Sheets...")
parser.authenticate()
print("✓ Authentication successful\n")
# Fetch and display attendance records
print("Fetching attendance records...")
attendance_records = parser.get_attendance_records()
print(f"✓ Found {len(attendance_records)} attendance records")
print("\nFirst 5 attendance records:")
for record in attendance_records[:5]:
print(f" {record.eid} - {record.activity} - {record.timestamp}")
# Fetch and display membership records
print("\nFetching membership records...")
membership_records = parser.get_membership_records()
print(f"✓ Found {len(membership_records)} membership records")
print("\nFirst 5 membership records:")
for record in membership_records[:5]:
print(f" {record.eid} - {record.firstname} {record.lastname} - {record.committees}")
# Example: Get specific member's attendance
print("\n--- Example: Get attendance for km54774 ---")
member_attendance = parser.get_attendance_by_eid("km54774")
print(f"Found {len(member_attendance)} attendance records for km54774")
for record in member_attendance:
print(f" {record.activity} - {record.timestamp}")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()