KamalShahid commited on
Commit
ebea19e
·
verified ·
1 Parent(s): 181280f

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +941 -0
app.py ADDED
@@ -0,0 +1,941 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # app.py
2
+ import streamlit as st
3
+ import pandas as pd
4
+ import sqlite3
5
+ import smtplib
6
+ from email.mime.text import MIMEText
7
+ from email.mime.multipart import MIMEMultipart
8
+ from datetime import datetime as dt, timedelta
9
+ import calendar
10
+ import plotly.graph_objects as go
11
+ from PIL import Image
12
+ import datetime
13
+ from google.oauth2 import service_account
14
+ from googleapiclient.discovery import build
15
+ from googleapiclient.errors import HttpError
16
+ from streamlit_calendar import calendar
17
+ import pytz
18
+
19
+ # ---------------------------------
20
+ # Meeting room details
21
+ # ---------------------------------
22
+ meeting_rooms = [
23
+ "Meeting Room (Floor 1)",
24
+ "Meeting Room (Floor 2)",
25
+ "Meeting Room (Floor 3)"
26
+ ]
27
+
28
+ # ---------------------------------
29
+ # Reset points at the beginning of each month
30
+ # ---------------------------------
31
+ def reset_points(username):
32
+ current_month = dt.now().strftime("%Y-%m")
33
+ cursor.execute('SELECT last_reset FROM members WHERE username = ?', (username,))
34
+ last_reset = cursor.fetchone()
35
+
36
+ if last_reset is None or last_reset[0] != current_month:
37
+ cursor.execute('UPDATE members SET points = 50, last_reset = ? WHERE username = ?', (current_month, username))
38
+ conn.commit()
39
+
40
+ # ---------------------------------
41
+ # CONFIGURATION (Set your values here)
42
+ # ---------------------------------
43
+ CREDENTIALS_FILE = "credentials.json"
44
+ CALENDAR_ID = "growork3@gmail.com"
45
+ TIME_ZONE = "GMT+5"
46
+
47
+ # Super Admin credentials
48
+ SUPER_ADMIN_USERNAME = "superadmin"
49
+ SUPER_ADMIN_PASSWORD = "superadmin123"
50
+
51
+ # Admin credentials
52
+ ADMIN_USERNAME = "admin"
53
+ ADMIN_PASSWORD = "admin123"
54
+
55
+ # Custom CSS to hide the sidebar on the login page
56
+ hide_sidebar_css = """
57
+ <style>
58
+ section[data-testid="stSidebar"] {
59
+ display: none;
60
+ }
61
+ </style>
62
+ """
63
+
64
+ st.set_page_config(page_title="GroWork", page_icon="Logo1.png")
65
+
66
+ # Database connection
67
+ conn = sqlite3.connect('growork.db', check_same_thread=False)
68
+ cursor = conn.cursor()
69
+
70
+ # ---------------------------------
71
+ # GOOGLE CALENDAR API AUTHENTICATION
72
+ # ---------------------------------
73
+ def authenticate_google():
74
+ creds = service_account.Credentials.from_service_account_file(
75
+ CREDENTIALS_FILE, scopes=["https://www.googleapis.com/auth/calendar"]
76
+ )
77
+ return build("calendar", "v3", credentials=creds)
78
+
79
+ calendar_service = authenticate_google()
80
+
81
+ # ---------------------------------
82
+ # FETCH BOOKINGS FROM GOOGLE CALENDAR
83
+ # ---------------------------------
84
+ def get_calendar_events():
85
+ try:
86
+ now = datetime.datetime.utcnow().isoformat() + "Z"
87
+ events_result = calendar_service.events().list(
88
+ calendarId=CALENDAR_ID, timeMin=now, maxResults=100, singleEvents=True, orderBy="startTime"
89
+ ).execute()
90
+ events = events_result.get("items", [])
91
+ bookings = []
92
+ for event in events:
93
+ start_time = event["start"].get("dateTime", event["start"].get("date"))
94
+ end_time = event["end"].get("dateTime", event["end"].get("date"))
95
+ summary = event.get("summary", "No Title")
96
+ event_id = event.get("id")
97
+ # Extract room from summary (format: "username (room)")
98
+ room = summary.split("(")[-1].rstrip(")") if "(" in summary else "Unknown Room"
99
+ bookings.append({"Start": start_time, "End": end_time, "Booked By": summary.split(" ")[0], "Event ID": event_id, "Room": room})
100
+ return pd.DataFrame(bookings)
101
+ except HttpError as error:
102
+ st.error(f"An error occurred: {error}")
103
+ return pd.DataFrame(columns=["Start", "End", "Booked By", "Event ID", "Room"])
104
+
105
+ # ---------------------------------
106
+ # FETCH MEMBER CREDENTIALS FROM DATABASE
107
+ # ---------------------------------
108
+ def fetch_member_credentials():
109
+ """
110
+ Fetch all member usernames and passwords from the database.
111
+ """
112
+ conn = sqlite3.connect("growork.db")
113
+ cursor = conn.cursor()
114
+ cursor.execute("SELECT username, password, email_id FROM members")
115
+ members = cursor.fetchall()
116
+ conn.close()
117
+
118
+ # Convert the result into a dictionary for easy lookup
119
+ member_credentials = {username: password for username, password, _ in members}
120
+ member_emails = {username: email for username, _, email in members}
121
+
122
+ return member_credentials, member_emails
123
+
124
+ # ---------------------------------
125
+ # INITIALIZE DATABASE
126
+ # ---------------------------------
127
+ def initialize_db():
128
+ # Create the members table if it doesn't exist
129
+ cursor.execute('''CREATE TABLE IF NOT EXISTS members (
130
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
131
+ full_name TEXT,
132
+ cnic TEXT,
133
+ company_name TEXT,
134
+ contact_number TEXT,
135
+ email_id TEXT,
136
+ rooms_booked INTEGER,
137
+ room_id_floor TEXT,
138
+ username TEXT UNIQUE,
139
+ password TEXT,
140
+ points INTEGER DEFAULT 50,
141
+ last_reset TEXT,
142
+ date_of_occupancy TEXT,
143
+ cost_per_seat REAL
144
+ )''')
145
+
146
+ # Create the rooms table if it doesn't exist
147
+ cursor.execute('''CREATE TABLE IF NOT EXISTS rooms (
148
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
149
+ room_id TEXT,
150
+ floor TEXT,
151
+ seats INTEGER)''') # Add seats column
152
+
153
+ # Create the announcements table if it doesn't exist
154
+ cursor.execute("""
155
+ CREATE TABLE IF NOT EXISTS announcements (
156
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
157
+ recipient TEXT,
158
+ message TEXT,
159
+ timestamp TEXT)""")
160
+
161
+ # Create the bookings table if it doesn't exist
162
+ cursor.execute('''
163
+ CREATE TABLE IF NOT EXISTS bookings (
164
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
165
+ username TEXT,
166
+ room TEXT,
167
+ time_slot TEXT,
168
+ booking_date TEXT)''')
169
+
170
+ # Create the complaints table if it doesn't exist
171
+ cursor.execute('''
172
+ CREATE TABLE IF NOT EXISTS complaints (
173
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
174
+ username TEXT,
175
+ room_id TEXT,
176
+ complaint TEXT,
177
+ timestamp TEXT
178
+ )''')
179
+
180
+ # Add the `last_reset` column to the members table if it doesn't exist
181
+ cursor.execute('PRAGMA table_info(members)')
182
+ columns = cursor.fetchall()
183
+ column_names = [column[1] for column in columns]
184
+ if 'last_reset' not in column_names:
185
+ cursor.execute('ALTER TABLE members ADD COLUMN last_reset TEXT')
186
+
187
+ # Add the `date_of_occupancy` column to the members table if it doesn't exist
188
+ if 'date_of_occupancy' not in column_names:
189
+ cursor.execute('ALTER TABLE members ADD COLUMN date_of_occupancy TEXT')
190
+
191
+ # Add the `cost_per_seat` column to the members table if it doesn't exist
192
+ if 'cost_per_seat' not in column_names:
193
+ cursor.execute('ALTER TABLE members ADD COLUMN cost_per_seat REAL')
194
+
195
+ # Add the `seats` column to the rooms table if it doesn't exist
196
+ cursor.execute('PRAGMA table_info(rooms)')
197
+ columns = cursor.fetchall()
198
+ column_names = [column[1] for column in columns]
199
+ if 'seats' not in column_names:
200
+ cursor.execute('ALTER TABLE rooms ADD COLUMN seats INTEGER')
201
+
202
+ conn.commit() # Commit the changes
203
+
204
+ # ---------------------------------
205
+ # SAVE MEMBER INFO
206
+ # ---------------------------------
207
+ def save_member_info(member_info):
208
+ conn = sqlite3.connect("growork.db")
209
+ cursor = conn.cursor()
210
+ cursor.execute("""
211
+ INSERT INTO members (full_name, cnic, company_name, contact_number, email_id, rooms_booked, room_id_floor, username, password, points, last_reset, date_of_occupancy, cost_per_seat)
212
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 50, NULL, ?, ?)""", member_info)
213
+ conn.commit() # Commit the changes
214
+ conn.close() # Close the connection
215
+ st.success("Member added successfully!")
216
+
217
+ # ---------------------------------
218
+ # REMOVE MEMBER
219
+ # ---------------------------------
220
+ def remove_member(member_name):
221
+ conn = sqlite3.connect("growork.db")
222
+ cursor = conn.cursor()
223
+ cursor.execute("DELETE FROM members WHERE full_name = ?", (member_name,))
224
+ if cursor.rowcount > 0:
225
+ st.success(f"Member {member_name} removed successfully!")
226
+ else:
227
+ st.error("Member not found.")
228
+ conn.commit()
229
+ conn.close()
230
+
231
+ # ---------------------------------
232
+ # FETCH MEMBER DETAILS
233
+ # ---------------------------------
234
+ def fetch_member_details():
235
+ conn = sqlite3.connect("growork.db")
236
+ cursor = conn.cursor()
237
+ cursor.execute('''
238
+ SELECT full_name, company_name, contact_number, email_id, rooms_booked, room_id_floor, date_of_occupancy, cost_per_seat
239
+ FROM members
240
+ ''')
241
+ members = cursor.fetchall()
242
+ conn.close()
243
+ return members
244
+
245
+ # ---------------------------------
246
+ # CALCULATE CHARGES
247
+ # ---------------------------------
248
+ def calculate_charges(rooms_booked, cost_per_seat, date_of_occupancy):
249
+ if date_of_occupancy:
250
+ occupancy_date = dt.strptime(date_of_occupancy, "%Y-%m-%d")
251
+ days_occupied = (dt.now() - occupancy_date).days
252
+ return rooms_booked * cost_per_seat * days_occupied
253
+ return 0
254
+
255
+ # ---------------------------------
256
+ # SAVE ROOM INFO
257
+ # ---------------------------------
258
+ def save_room_info(room_info):
259
+ conn = sqlite3.connect("growork.db")
260
+ cursor = conn.cursor()
261
+ cursor.execute("INSERT INTO rooms (room_id, floor, seats) VALUES (?, ?, ?)", room_info)
262
+ conn.commit()
263
+ conn.close()
264
+ st.success("Room added successfully!")
265
+
266
+ # ---------------------------------
267
+ # REMOVE ROOM
268
+ # ---------------------------------
269
+ def remove_room(room_id, floor):
270
+ conn = sqlite3.connect("growork.db")
271
+ cursor = conn.cursor()
272
+ cursor.execute("DELETE FROM rooms WHERE room_id = ? AND floor = ?", (room_id, floor))
273
+ if cursor.rowcount > 0:
274
+ st.success(f"Room {room_id} on Floor {floor} removed successfully!")
275
+ else:
276
+ st.error("Room not found.")
277
+ conn.commit()
278
+ conn.close()
279
+
280
+ # ---------------------------------
281
+ # SAVE ANNOUNCEMENT
282
+ # ---------------------------------
283
+ def save_announcement(recipient, message):
284
+ conn = sqlite3.connect("growork.db")
285
+ cursor = conn.cursor()
286
+ timestamp = dt.now().strftime("%Y-%m-%d %H:%M:%S")
287
+ cursor.execute("INSERT INTO announcements (recipient, message, timestamp) VALUES (?, ?, ?)",
288
+ (recipient, message, timestamp))
289
+ conn.commit()
290
+ conn.close()
291
+
292
+ # ---------------------------------
293
+ # GET ANNOUNCEMENTS
294
+ # ---------------------------------
295
+ def get_announcements(username):
296
+ conn = sqlite3.connect("growork.db")
297
+ cursor = conn.cursor()
298
+ cursor.execute("SELECT message, timestamp FROM announcements WHERE recipient = ? OR recipient = 'all' ORDER BY timestamp DESC", (username,))
299
+ announcements = cursor.fetchall()
300
+ conn.close()
301
+ return announcements
302
+
303
+ # ---------------------------------
304
+ # SEND EMAIL
305
+ # ---------------------------------
306
+ def send_email(recipient_email, subject, message):
307
+ # Gmail account details
308
+ sender_email = "growork3@gmail.com" # Your Gmail address
309
+ sender_password = "fswitclgutgyibni" # Use the 16-character App Password
310
+
311
+ # Create the email
312
+ msg = MIMEMultipart()
313
+ msg["From"] = sender_email
314
+ msg["To"] = recipient_email
315
+ msg["Subject"] = subject
316
+ msg.attach(MIMEText(message, "plain"))
317
+
318
+ try:
319
+ # Use Gmail's SMTP server
320
+ server = smtplib.SMTP("smtp.gmail.com", 587) # Gmail's SMTP server and port
321
+ server.starttls() # Upgrade the connection to secure
322
+ server.login(sender_email, sender_password) # Log in to your Gmail account
323
+ server.sendmail(sender_email, recipient_email, msg.as_string()) # Send the email
324
+ server.quit() # Close the connection
325
+ print("Email sent successfully!")
326
+ except Exception as e:
327
+ print(f"Failed to send email: {str(e)}")
328
+
329
+
330
+ # Initialize the database when the script runs
331
+ initialize_db()
332
+
333
+
334
+ # ---------------------------------
335
+ # LOGIN PAGE
336
+ # ---------------------------------
337
+ def login_page():
338
+ # Inject custom CSS to hide the sidebar
339
+ st.markdown(hide_sidebar_css, unsafe_allow_html=True)
340
+ st.image("LongLogo.png", width=500)
341
+ col1, col2, col3 = st.columns([1, 2, 1]) # Creates three columns
342
+ with col2: # Puts the image in the middle column
343
+ st.title("GroWork Login")
344
+ st.write("Welcome to GroWork! Please log in to continue.")
345
+
346
+ # Fetch member credentials from the database
347
+ member_credentials, member_emails = fetch_member_credentials()
348
+
349
+ # Login form
350
+ username = st.text_input("Username")
351
+ password = st.text_input("Password", type="password")
352
+ login_button = st.button("Login")
353
+
354
+ if login_button:
355
+ if username == SUPER_ADMIN_USERNAME and password == SUPER_ADMIN_PASSWORD:
356
+ st.session_state["role"] = "superadmin"
357
+ st.session_state["page"] = "superadmin"
358
+ elif username == ADMIN_USERNAME and password == ADMIN_PASSWORD:
359
+ st.session_state["role"] = "admin"
360
+ st.session_state["page"] = "admin"
361
+ elif username in member_credentials and member_credentials[username] == password:
362
+ st.session_state["role"] = "member"
363
+ st.session_state["page"] = "member"
364
+ st.session_state["username"] = username # Store the member's username
365
+ else:
366
+ st.error("Invalid username or password")
367
+
368
+ # ---------------------------------
369
+ # SUPERADMIN PAGE
370
+ # ---------------------------------
371
+ def superadmin_page():
372
+ st.image("LongLogo.png", width=500)
373
+ st.title("Super Admin Dashboard")
374
+
375
+ # Sidebar for navigation
376
+ st.sidebar.title("Navigation")
377
+ page = st.sidebar.radio("Go to", ["Add/Remove Members", "View Member Details", "Add/Remove Rooms", "Announcements", "See All Bookings", "Complaints"])
378
+
379
+ if page == "Add/Remove Members":
380
+ st.header("Add/Remove Members")
381
+
382
+ # Fetch available rooms
383
+ conn = sqlite3.connect("growork.db")
384
+ cursor = conn.cursor()
385
+ cursor.execute("SELECT room_id, floor, seats FROM rooms")
386
+ rooms = cursor.fetchall()
387
+ conn.close()
388
+
389
+ available_rooms = [f"{room[0]} ({room[1]})" for room in rooms]
390
+
391
+ full_name = st.text_input("Full Name")
392
+ cnic = st.text_input("CNIC")
393
+ company_name = st.text_input("Company Name")
394
+ contact_number = st.text_input("Contact Number")
395
+ email_id = st.text_input("Email ID")
396
+ num_rooms = st.number_input("Number of Rooms Booked", min_value=0, step=1, value=0)
397
+ room_info = st.selectbox("Select Room", available_rooms) # Dropdown for available rooms
398
+ username = st.text_input("Member Username")
399
+ password = st.text_input("Member Password", type="password")
400
+ date_of_occupancy = st.date_input("Date of Occupancy")
401
+ cost_per_seat = st.number_input("Cost per Seat (Rs.)", min_value=0.0, step=100.0, value=1000.0)
402
+
403
+ if st.button("Add Member"):
404
+ if full_name and username and password: # Ensure required fields are filled
405
+ member_info = [full_name, cnic, company_name, contact_number, email_id, num_rooms, room_info, username, password, date_of_occupancy.strftime("%Y-%m-%d"), cost_per_seat]
406
+ save_member_info(member_info)
407
+
408
+ # Remove the allocated room from the available rooms list
409
+ room_id_floor = room_info.split(" (")[0]
410
+ conn = sqlite3.connect("growork.db")
411
+ cursor = conn.cursor()
412
+ cursor.execute("DELETE FROM rooms WHERE room_id = ?", (room_id_floor,))
413
+ conn.commit()
414
+ conn.close()
415
+
416
+ st.success(f"Member {full_name} added successfully and room {room_info} allocated!")
417
+ st.session_state["refresh_member_details"] = True # Trigger refresh
418
+ else:
419
+ st.error("Please fill in all required fields (Full Name, Username, and Password).")
420
+
421
+ remove_member_name = st.text_input("Enter Member Name to Remove")
422
+ if st.button("Remove Member"):
423
+ remove_member(remove_member_name)
424
+ st.session_state["refresh_member_details"] = True # Trigger refresh
425
+
426
+ elif page == "View Member Details":
427
+ st.header("View Member Details")
428
+
429
+ # Fetch all members from the database
430
+ members = fetch_member_details()
431
+
432
+ if members:
433
+ # Create a DataFrame to display the member details
434
+ member_details = []
435
+ for member in members:
436
+ full_name, company_name, contact_number, email_id, rooms_booked, room_id_floor, date_of_occupancy, cost_per_seat = member
437
+
438
+ # Handle NULL values for rooms_booked and cost_per_seat
439
+ if rooms_booked is None:
440
+ rooms_booked = 0 # Replace NULL with 0
441
+ if cost_per_seat is None:
442
+ cost_per_seat = 0 # Replace NULL with 0
443
+
444
+ # Calculate charges per seat
445
+ charges_per_seat = calculate_charges(rooms_booked, cost_per_seat, date_of_occupancy)
446
+
447
+ member_details.append({
448
+ "Name": full_name,
449
+ "Company Name": company_name,
450
+ "Contact Number": contact_number,
451
+ "Email ID": email_id,
452
+ "Rooms Booked": rooms_booked,
453
+ "Room ID and Floor": room_id_floor,
454
+ "Date of Occupancy": date_of_occupancy,
455
+ "Cost per Seat (Rs.)": cost_per_seat,
456
+ "Total Charges (Rs.)": charges_per_seat
457
+ })
458
+
459
+ # Convert to DataFrame
460
+ df = pd.DataFrame(member_details)
461
+
462
+ # Display the DataFrame
463
+ st.write("### Member Details")
464
+ st.dataframe(df) # Use st.dataframe to display the DataFrame
465
+
466
+ # Option to download the data as a CSV file
467
+ csv = df.to_csv(index=False).encode('utf-8')
468
+ st.download_button(
469
+ label="Download Member Details as CSV",
470
+ data=csv,
471
+ file_name="member_details.csv",
472
+ mime="text/csv",
473
+ )
474
+ else:
475
+ st.write("No members found in the database.")
476
+
477
+
478
+ elif page == "Add/Remove Rooms":
479
+ st.header("Add/Remove Rooms")
480
+
481
+ # Add Room Form
482
+ room_id = st.text_input("Room ID")
483
+ floor = st.text_input("Floor")
484
+ seats = st.number_input("Number of Seats", min_value=1, step=1, value=1)
485
+
486
+ if st.button("Add Room"):
487
+ room_info = [room_id, floor, seats]
488
+ save_room_info(room_info)
489
+ st.success(f"Room {room_id} on {floor} with {seats} seats added successfully!")
490
+
491
+ # Remove Room Form
492
+ remove_room_id = st.text_input("Enter Room ID to Remove")
493
+ remove_floor = st.text_input("Enter Floor Number of the Room to Remove")
494
+ if st.button("Remove Room"):
495
+ remove_room(remove_room_id, remove_floor)
496
+
497
+ # Display Available Rooms and Seats
498
+ st.header("Available Rooms and Seats")
499
+ conn = sqlite3.connect("growork.db")
500
+ cursor = conn.cursor()
501
+ cursor.execute("SELECT room_id, floor, seats FROM rooms")
502
+ rooms = cursor.fetchall()
503
+ conn.close()
504
+
505
+ if rooms:
506
+ room_details = [{"Room ID": room[0], "Floor": room[1], "Seats": room[2]} for room in rooms]
507
+ df = pd.DataFrame(room_details)
508
+ st.write(df) # Display the table of available rooms
509
+ else:
510
+ st.write("No rooms found.")
511
+
512
+ elif page == "See All Bookings":
513
+ st.header("All Bookings")
514
+
515
+ # Fetch bookings from Google Calendar
516
+ with st.spinner("Fetching bookings..."):
517
+ df = get_calendar_events()
518
+
519
+ if not df.empty:
520
+ # Display the calendar view
521
+ st.write("### Calendar View of Bookings")
522
+ events = [{"title": f"Booked by {row['Booked By']} ({row['Room']})", "start": row["Start"], "end": row["End"], "color": "red"} for _, row in df.iterrows()]
523
+ calendar_options = {
524
+ "editable": False,
525
+ "defaultView": "month",
526
+ "timeZone": TIME_ZONE,
527
+ }
528
+ calendar(events=events, options=calendar_options)
529
+
530
+ # Display the bookings in a table
531
+ st.write("### Bookings Table")
532
+ st.write(df)
533
+ else:
534
+ st.write("No bookings found.")
535
+
536
+ elif page == "Announcements":
537
+ st.header("Make Announcements")
538
+ announcement = st.text_area("Announcement Text")
539
+
540
+ # Fetch member usernames and emails from the database
541
+ member_credentials, member_emails = fetch_member_credentials()
542
+
543
+ recipient_option = st.radio("Select Recipients", ["Single Member", "Multiple Members", "All Members"])
544
+
545
+ if recipient_option == "Single Member":
546
+ recipient = st.selectbox("Select Member", list(member_credentials.keys()))
547
+ elif recipient_option == "Multiple Members":
548
+ recipient = st.multiselect("Select Members", list(member_credentials.keys()))
549
+ else:
550
+ recipient = "all"
551
+
552
+ if st.button("Post Announcement"):
553
+ if announcement:
554
+ if recipient == "all":
555
+ for email in member_emails.values():
556
+ send_email(email, "New Announcement", announcement)
557
+ save_announcement("all", announcement)
558
+ elif isinstance(recipient, list):
559
+ for user in recipient:
560
+ send_email(member_emails[user], "New Announcement", announcement)
561
+ save_announcement(user, announcement)
562
+ else:
563
+ send_email(member_emails[recipient], "New Announcement", announcement)
564
+ save_announcement(recipient, announcement)
565
+
566
+ st.success("Announcement posted and emailed successfully!")
567
+ else:
568
+ st.error("Please enter an announcement before posting.")
569
+
570
+ elif page == "Complaints":
571
+ st.header("Complaints")
572
+
573
+ # Fetch all complaints from the database
574
+ conn = sqlite3.connect("growork.db")
575
+ cursor = conn.cursor()
576
+ cursor.execute("SELECT username, room_id, complaint, timestamp FROM complaints ORDER BY timestamp DESC")
577
+ complaints = cursor.fetchall()
578
+ conn.close()
579
+
580
+ if complaints:
581
+ # Create a DataFrame to display the complaints
582
+ complaint_details = []
583
+ for complaint in complaints:
584
+ username, room_id, complaint_text, timestamp = complaint
585
+ complaint_details.append({
586
+ "Username": username,
587
+ "Room ID": room_id,
588
+ "Complaint": complaint_text,
589
+ "Timestamp": timestamp
590
+ })
591
+
592
+ # Convert to DataFrame
593
+ df = pd.DataFrame(complaint_details)
594
+
595
+ # Display the DataFrame
596
+ st.write("### Complaints")
597
+ st.dataframe(df) # Use st.dataframe to display the DataFrame
598
+
599
+ # Option to download the data as a CSV file
600
+ csv = df.to_csv(index=False).encode('utf-8')
601
+ st.download_button(
602
+ label="Download Complaints as CSV",
603
+ data=csv,
604
+ file_name="complaints.csv",
605
+ mime="text/csv",
606
+ )
607
+ else:
608
+ st.write("No complaints found.")
609
+
610
+
611
+ # Logout button
612
+ if st.button("Logout"):
613
+ st.session_state.clear() # Clear the session state
614
+ st.session_state["page"] = "login" # Redirect to the login page
615
+ st.rerun() # Rerun the app to reflect changes
616
+
617
+ # ---------------------------------
618
+ # ADMIN PAGE
619
+ # ---------------------------------
620
+ def admin_page():
621
+ st.image("LongLogo.png", width=500)
622
+ st.title("Admin Dashboard")
623
+
624
+ # Sidebar for navigation
625
+ st.sidebar.title("Navigation")
626
+ page = st.sidebar.radio("Go to", ["Add/Remove Members", "See All Bookings", "Announcements", "Complaints"])
627
+
628
+ if page == "Add/Remove Members":
629
+ st.header("Add/Remove Members")
630
+ full_name = st.text_input("Full Name")
631
+ cnic = st.text_input("CNIC")
632
+ company_name = st.text_input("Company Name")
633
+ contact_number = st.text_input("Contact Number")
634
+ email_id = st.text_input("Email ID")
635
+ num_rooms = st.number_input("Number of Rooms Booked", min_value=1, step=1)
636
+ room_info = st.text_area("Room ID and Floor (e.g., 101-2nd Floor, 202-3rd Floor)")
637
+ username = st.text_input("Member Username")
638
+ password = st.text_input("Member Password", type="password")
639
+
640
+ if st.button("Add Member"):
641
+ member_info = [full_name, cnic, company_name, contact_number, email_id, num_rooms, room_info, username, password]
642
+ save_member_info(member_info)
643
+ st.success(f"Member {full_name} added successfully!")
644
+
645
+ remove_member_name = st.text_input("Enter Member Name to Remove")
646
+ if st.button("Remove Member"):
647
+ remove_member(remove_member_name)
648
+
649
+ elif page == "See All Bookings":
650
+ st.header("All Bookings")
651
+
652
+ # Fetch bookings from Google Calendar
653
+ with st.spinner("Fetching bookings..."):
654
+ df = get_calendar_events()
655
+
656
+ if not df.empty:
657
+ # Display the calendar view
658
+ st.write("### Calendar View of Bookings")
659
+ events = [{"title": f"Booked by {row['Booked By']} ({row['Room']})", "start": row["Start"], "end": row["End"], "color": "red"} for _, row in df.iterrows()]
660
+ calendar_options = {
661
+ "editable": False,
662
+ "defaultView": "month",
663
+ "timeZone": TIME_ZONE,
664
+ }
665
+ calendar(events=events, options=calendar_options)
666
+
667
+ # Display the bookings in a table
668
+ st.write("### Bookings Table")
669
+ st.write(df)
670
+ else:
671
+ st.write("No bookings found.")
672
+
673
+ elif page == "Announcements":
674
+ st.header("Make Announcements")
675
+ announcement = st.text_area("Announcement Text")
676
+
677
+ # Fetch member usernames and emails from the database
678
+ member_credentials, member_emails = fetch_member_credentials()
679
+
680
+ recipient_option = st.radio("Select Recipients", ["Single Member", "Multiple Members", "All Members"])
681
+
682
+ if recipient_option == "Single Member":
683
+ recipient = st.selectbox("Select Member", list(member_credentials.keys()))
684
+ elif recipient_option == "Multiple Members":
685
+ recipient = st.multiselect("Select Members", list(member_credentials.keys()))
686
+ else:
687
+ recipient = "all"
688
+
689
+ if st.button("Post Announcement"):
690
+ if announcement:
691
+ if recipient == "all":
692
+ for email in member_emails.values():
693
+ send_email(email, "New Announcement", announcement)
694
+ save_announcement("all", announcement)
695
+ elif isinstance(recipient, list):
696
+ for user in recipient:
697
+ send_email(member_emails[user], "New Announcement", announcement)
698
+ save_announcement(user, announcement)
699
+ else:
700
+ send_email(member_emails[recipient], "New Announcement", announcement)
701
+ save_announcement(recipient, announcement)
702
+
703
+ st.success("Announcement posted and emailed successfully!")
704
+ else:
705
+ st.error("Please enter an announcement before posting.")
706
+
707
+ elif page == "Complaints":
708
+ st.header("Complaints")
709
+
710
+ # Fetch all complaints from the database
711
+ conn = sqlite3.connect("growork.db")
712
+ cursor = conn.cursor()
713
+ cursor.execute("SELECT username, room_id, complaint, timestamp FROM complaints ORDER BY timestamp DESC")
714
+ complaints = cursor.fetchall()
715
+ conn.close()
716
+
717
+ if complaints:
718
+ # Create a DataFrame to display the complaints
719
+ complaint_details = []
720
+ for complaint in complaints:
721
+ username, room_id, complaint_text, timestamp = complaint
722
+ complaint_details.append({
723
+ "Username": username,
724
+ "Room ID": room_id,
725
+ "Complaint": complaint_text,
726
+ "Timestamp": timestamp
727
+ })
728
+
729
+ # Convert to DataFrame
730
+ df = pd.DataFrame(complaint_details)
731
+
732
+ # Display the DataFrame
733
+ st.write("### Complaints")
734
+ st.dataframe(df) # Use st.dataframe to display the DataFrame
735
+
736
+ # Option to download the data as a CSV file
737
+ csv = df.to_csv(index=False).encode('utf-8')
738
+ st.download_button(
739
+ label="Download Complaints as CSV",
740
+ data=csv,
741
+ file_name="complaints.csv",
742
+ mime="text/csv",
743
+ )
744
+ else:
745
+ st.write("No complaints found.")
746
+
747
+
748
+ # Logout button
749
+ if st.button("Logout"):
750
+ st.session_state.clear() # Clear the session state
751
+ st.session_state["page"] = "login" # Redirect to the login page
752
+ st.rerun() # Rerun the app to reflect changes
753
+
754
+ # ---------------------------------
755
+ # MEMBER PAGE
756
+ # ---------------------------------
757
+ def member_page(cursor):
758
+ st.image("LongLogo.png", width=500)
759
+ st.title("Member Dashboard")
760
+
761
+ # Sidebar for navigation
762
+ st.sidebar.title("Navigation")
763
+ page = st.sidebar.radio("Go to", ["Meeting Room Reservation", "Complain Registration", "See Announcements"])
764
+
765
+ username = st.session_state["username"]
766
+ reset_points(username)
767
+
768
+ # Room Reservation Page
769
+ if page == "Meeting Room Reservation":
770
+ st.header("Meeting Room Reservation")
771
+
772
+ # Fetch member points
773
+ cursor.execute('SELECT points FROM members WHERE username = ?', (username,))
774
+ result = cursor.fetchone()
775
+ if result:
776
+ points = result[0]
777
+ else:
778
+ st.error("Member data not found. Please contact the admin.")
779
+ return
780
+
781
+ # Fetch and display bookings
782
+ with st.spinner("Fetching bookings..."):
783
+ df = get_calendar_events()
784
+
785
+ # Ensure the DataFrame has the required columns
786
+ if df.empty:
787
+ df = pd.DataFrame(columns=["Start", "End", "Booked By", "Event ID", "Room"])
788
+
789
+ # Display interactive calendar
790
+ st.write("### Available & Booked Slots")
791
+ events = [{"title": f"Booked by {row['Booked By']} ({row['Room']})", "start": row["Start"], "end": row["End"], "color": "red"} for _, row in df.iterrows()]
792
+ calendar_options = {
793
+ "editable": False,
794
+ "defaultView": "month",
795
+ "timeZone": TIME_ZONE,
796
+ }
797
+ calendar(events=events, options=calendar_options)
798
+
799
+ # Booking Form
800
+ st.write("### Book a Room")
801
+ date = st.date_input("Select Date", min_value=datetime.date.today())
802
+ start_time = st.time_input("Start Time")
803
+ end_time = st.time_input("End Time")
804
+
805
+ # Filter out rooms that are already booked for the selected time slot
806
+ start_datetime = datetime.datetime.combine(date, start_time).astimezone(pytz.UTC).isoformat()
807
+ end_datetime = datetime.datetime.combine(date, end_time).astimezone(pytz.UTC).isoformat()
808
+
809
+ # Get the list of rooms already booked for the selected time slot
810
+ booked_rooms = df[(df["Start"] <= end_datetime) & (df["End"] >= start_datetime)]["Room"].tolist()
811
+
812
+ # Available rooms are those not in the booked_rooms list
813
+ available_rooms = [room for room in meeting_rooms if room not in booked_rooms]
814
+
815
+ # If no rooms are available, show a message
816
+ if not available_rooms:
817
+ st.warning("No rooms available for the selected time slot.")
818
+ else:
819
+ room = st.selectbox("Select Room", available_rooms) # Allow room selection
820
+
821
+ if st.button("Book Now"):
822
+ if start_time >= end_time:
823
+ st.error("End time must be after start time.")
824
+ else:
825
+ event = {
826
+ "summary": f"{username} ({room})", # Include room in the event summary
827
+ "start": {"dateTime": start_datetime, "timeZone": TIME_ZONE},
828
+ "end": {"dateTime": end_datetime, "timeZone": TIME_ZONE},
829
+ }
830
+ try:
831
+ calendar_service.events().insert(calendarId=CALENDAR_ID, body=event).execute()
832
+ st.success(f"Room {room} booked successfully!")
833
+ st.rerun() # Refresh the page to show the updated calendar
834
+ except HttpError as error:
835
+ st.error(f"An error occurred: {error}")
836
+
837
+ elif page == "See Announcements":
838
+ st.header("Announcements")
839
+ announcements = get_announcements(st.session_state["username"])
840
+ if announcements:
841
+ for msg, timestamp in announcements:
842
+ st.write(f"{timestamp}: {msg}")
843
+ else:
844
+ st.write("No announcements yet.")
845
+
846
+ elif page == "Complain Registration":
847
+ st.header("Register a Complaint")
848
+
849
+ subjects = [
850
+ "Noise Levels", "Temperature", "Cleanliness", "Distractions", "Lack of Privacy",
851
+ "Internet Connectivity", "Equipment Malfunction", "Limited Resources",
852
+ "Inadequate Furniture", "Parking Issues"
853
+ ]
854
+
855
+ subject = st.selectbox("Select Subject", subjects)
856
+ complaint = st.text_area("Complaint Details")
857
+
858
+ if st.button("Submit Complaint"):
859
+ if subject and complaint:
860
+ # Save the complaint to the database
861
+ conn = sqlite3.connect("growork.db")
862
+ cursor = conn.cursor()
863
+ timestamp = dt.now().strftime("%Y-%m-%d %H:%M:%S")
864
+ cursor.execute("""
865
+ INSERT INTO complaints (username, room_id, complaint, timestamp)
866
+ VALUES (?, ?, ?, ?)
867
+ """, (username, "N/A", f"{subject}: {complaint}", timestamp))
868
+ conn.commit()
869
+ conn.close()
870
+
871
+ # Send an email notification (optional)
872
+ send_email("admin@growork.com", "New Complaint", f"Complaint from {username}: {subject}: {complaint}")
873
+
874
+ st.success("Complaint submitted successfully!")
875
+ else:
876
+ st.warning("Please fill in all fields before submitting.")
877
+
878
+ # Logout button
879
+ if st.button("Logout"):
880
+ st.session_state.clear() # Clear the session state
881
+ st.session_state["page"] = "login" # Redirect to the login page
882
+ st.rerun() # Rerun the app to reflect changes
883
+
884
+
885
+ # ---------------------------------
886
+ # MAIN APP LOGIC
887
+ # ---------------------------------
888
+ def main():
889
+ # Initialize session state
890
+ if "page" not in st.session_state:
891
+ st.session_state["page"] = "login"
892
+
893
+ # Initialize the database connection and cursor
894
+ conn = sqlite3.connect('growork.db', check_same_thread=False)
895
+ cursor = conn.cursor()
896
+
897
+ # Render the appropriate page based on session state
898
+ if st.session_state["page"] == "login":
899
+ login_page()
900
+ elif st.session_state["page"] == "superadmin":
901
+ superadmin_page()
902
+ elif st.session_state["page"] == "admin":
903
+ admin_page()
904
+ elif st.session_state["page"] == "member":
905
+ member_page(cursor) # Pass the cursor to the member_page function
906
+
907
+ # Footer
908
+ st.markdown(
909
+ """
910
+ <div style="display: flex; justify-content: space-between; align-items: center; padding: 20px; border-top: 2px solid #ddd;">
911
+ <!-- Left Section: Contact Information -->
912
+ <div style="text-align: left;">
913
+ <b>📧 Email:</b> <a href="mailto:info@example.com">info@growork.pk</a>
914
+ <br><b>📞 Phone:</b> <a href="tel:+92 304 4865868">+92 304 4865868</a>
915
+ <br><b>📍 Location:</b> <a href="https://www.google.com/maps/place/GroWork+Coworking/data=!4m2!3m1!1s0x0:0x5068422a19bab54d?sa=X&ved=1t:2428&ictx=111" target="_blank">View on Google Maps</a>
916
+ </div>
917
+ <div style="text-align: right;">
918
+ <b>Scan to visit our website:</b>
919
+ <br>
920
+ <img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=https://yourwebsite.com" width="120">
921
+ </div>
922
+ </div>
923
+ """,
924
+ unsafe_allow_html=True
925
+ )
926
+
927
+ st.markdown(
928
+ """
929
+ <div style="text-align: center; padding: 10px;">
930
+ © 2025 <b>GroWork</b>. All rights reserved.
931
+ </div>
932
+ """,
933
+ unsafe_allow_html=True
934
+ )
935
+
936
+ # Close the database connection when done
937
+ conn.close()
938
+
939
+ # Run the app
940
+ if __name__ == "__main__":
941
+ main()