source: uc_reserve.py@ 95cf703

main
Last change on this file since 95cf703 was 95cf703, checked in by teo <teodorbogoeski@…>, 4 months ago

Initial commit: FRRUAS CLI prototype

  • Property mode set to 100644
File size: 7.1 KB
Line 
1import datetime
2
3from db import get_connection, pick_from_list, print_table, input_date, input_time
4
5
6def make_reservation(user_id):
7 """UC0002: Make a Resource Reservation."""
8 print("\n=== Make a Resource Reservation ===\n")
9
10 # Step 1: Select resource
11 resources = _fetch_all_resources()
12 if not resources:
13 print(" No resources available.")
14 return
15
16 items = []
17 for r in resources:
18 loc = f"{r['building']} / {r['room']}" if r["building"] else "Digital"
19 items.append(f"{r['name']} [{r['type_name']}] - {loc}")
20 idx = pick_from_list(" Select a resource to reserve: ", items)
21 if idx is None:
22 return
23
24 resource = resources[idx]
25 rid = resource["resource_id"]
26
27 # Step 2: Show resource details and upcoming reservations
28 print(f"\n === {resource['name']} ===")
29 print(f" Available: {resource['available_from'].strftime('%H:%M')} - {resource['available_to'].strftime('%H:%M')}")
30 print(f" Weekends: {'Yes' if resource['available_weekends'] else 'No'}")
31
32 _show_upcoming(rid)
33
34 # Step 3: Enter reservation details
35 while True:
36 print()
37 date = input_date(" Reservation date (YYYY-MM-DD): ")
38 start = input_time(" Start time (HH:MM): ")
39 end = input_time(" End time (HH:MM): ")
40
41 if end <= start:
42 print(" End time must be after start time.")
43 continue
44
45 purpose = input(" Purpose: ").strip()
46 if not purpose:
47 print(" Purpose is required.")
48 continue
49 if len(purpose) > 512:
50 purpose = purpose[:512]
51
52 # Step 4: Validate availability window
53 day_of_week = date.isoweekday()
54 if day_of_week > 5 and not resource["available_weekends"]:
55 print(f"\n ERROR: {resource['name']} is not available on weekends.")
56 if not _retry():
57 return
58 continue
59
60 if start < resource["available_from"] or end > resource["available_to"]:
61 print(
62 f"\n ERROR: {resource['name']} is only available "
63 f"{resource['available_from'].strftime('%H:%M')} - {resource['available_to'].strftime('%H:%M')}."
64 )
65 if not _retry():
66 return
67 continue
68
69 # Step 5: Check conflicts
70 start_dt = datetime.datetime.combine(date, start)
71 end_dt = datetime.datetime.combine(date, end)
72 conflicts = _check_conflicts(rid, start_dt, end_dt)
73
74 if conflicts:
75 print(f"\n CONFLICT: {len(conflicts)} overlapping reservation(s) found:")
76 print_table(
77 ["ID", "Start", "End", "Purpose", "Reserved by"],
78 [
79 (c[0], c[1].strftime("%Y-%m-%d %H:%M"), c[2].strftime("%Y-%m-%d %H:%M"), c[3][:30], c[4])
80 for c in conflicts
81 ],
82 )
83 if not _retry():
84 return
85 continue
86
87 # Step 6: Confirm
88 print(f"\n --- Reservation Summary ---")
89 print(f" Resource: {resource['name']}")
90 print(f" Date: {date}")
91 print(f" Time: {start.strftime('%H:%M')} - {end.strftime('%H:%M')}")
92 print(f" Purpose: {purpose}")
93 confirm = input("\n Confirm reservation? (y/n): ").strip().lower()
94 if confirm not in ("y", "yes"):
95 print(" Reservation cancelled.")
96 return
97
98 # Step 7: INSERT
99 conn = get_connection()
100 try:
101 with conn.cursor() as cur:
102 cur.execute(
103 """
104 INSERT INTO reservations
105 (start_time, end_time, status, purpose, created_at, user_id, resource_id)
106 VALUES (%s, %s, 'pending', %s, CURRENT_TIMESTAMP, %s, %s)
107 RETURNING reservation_id, status, created_at
108 """,
109 (start_dt, end_dt, purpose, user_id, rid),
110 )
111 result = cur.fetchone()
112 conn.commit()
113 print(f"\n Reservation created successfully!")
114 print(f" Reservation ID: {result[0]}")
115 print(f" Status: {result[1]} (awaiting administrator approval)")
116 print(f" Created at: {result[2].strftime('%Y-%m-%d %H:%M')}")
117 return
118 except Exception as e:
119 conn.rollback()
120 print(f"\n Error creating reservation: {e}")
121 return
122 finally:
123 conn.close()
124
125
126def _fetch_all_resources():
127 with get_connection() as conn:
128 with conn.cursor() as cur:
129 cur.execute("""
130 SELECT r.resource_id, r.name, r.description,
131 r.available_from, r.available_to, r.available_weekends,
132 rt.type_name, l.building, l.room
133 FROM resources r
134 JOIN resource_types rt ON r.type_id = rt.type_id
135 LEFT JOIN locations l ON r.location_id = l.location_id
136 ORDER BY rt.type_name, r.name
137 """)
138 cols = [desc[0] for desc in cur.description]
139 return [dict(zip(cols, row)) for row in cur.fetchall()]
140
141
142def _show_upcoming(resource_id):
143 today = datetime.date.today()
144 week_end = today + datetime.timedelta(days=7)
145 with get_connection() as conn:
146 with conn.cursor() as cur:
147 cur.execute(
148 """
149 SELECT res.start_time, res.end_time, res.status, res.purpose,
150 u.first_name || ' ' || u.last_name AS reserved_by
151 FROM reservations res
152 JOIN users u ON res.user_id = u.user_id
153 WHERE res.resource_id = %s
154 AND res.status IN ('approved', 'pending')
155 AND res.start_time >= %s AND res.start_time < %s
156 ORDER BY res.start_time
157 """,
158 (resource_id, today, week_end),
159 )
160 rows = cur.fetchall()
161 if rows:
162 print(f"\n Reservations in the next 7 days:")
163 print_table(
164 ["Start", "End", "Status", "Purpose", "By"],
165 [
166 (r[0].strftime("%Y-%m-%d %H:%M"), r[1].strftime("%Y-%m-%d %H:%M"), r[2], r[3][:30], r[4])
167 for r in rows
168 ],
169 )
170 else:
171 print("\n No reservations in the next 7 days.")
172
173
174def _check_conflicts(resource_id, start_dt, end_dt):
175 with get_connection() as conn:
176 with conn.cursor() as cur:
177 cur.execute(
178 """
179 SELECT res.reservation_id, res.start_time, res.end_time,
180 res.purpose,
181 u.first_name || ' ' || u.last_name AS reserved_by
182 FROM reservations res
183 JOIN users u ON res.user_id = u.user_id
184 WHERE res.resource_id = %s
185 AND res.status IN ('approved', 'pending')
186 AND res.start_time < %s
187 AND res.end_time > %s
188 """,
189 (resource_id, end_dt, start_dt),
190 )
191 return cur.fetchall()
192
193
194def _retry():
195 choice = input(" Try a different time? (y/n): ").strip().lower()
196 return choice in ("y", "yes")
Note: See TracBrowser for help on using the repository browser.