RemoteRF 0.0.6.14__tar.gz → 0.0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {remoterf-0.0.6.14/src/remoteRF.egg-info → remoterf-0.0.7.2}/PKG-INFO +3 -8
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/README.md +1 -5
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/setup.py +3 -2
- remoterf-0.0.7.2/src/remoteRF/core/app.py +543 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2/src/remoteRF.egg-info}/PKG-INFO +3 -8
- remoterf-0.0.6.14/src/remoteRF/core/app.py +0 -270
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/LICENSE +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/MANIFEST.in +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/setup.cfg +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/grpc/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/grpc/grpc_pb2.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/grpc/grpc_pb2_grpc.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/utils/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/utils/ansi_codes.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/utils/api_token.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/utils/list_string.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/common/utils/process_arg.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/acc_login.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/certs/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/certs/ca.crt +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/certs/ca.key +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/certs/cert.pem +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/certs/key.pem +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/certs/server.crt +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/certs/server.key +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/grpc_acc.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/core/grpc_client.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/drivers/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/drivers/adalm_pluto/__init__.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF/drivers/adalm_pluto/pluto_remote.py +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF.egg-info/SOURCES.txt +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF.egg-info/dependency_links.txt +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF.egg-info/entry_points.txt +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF.egg-info/requires.txt +0 -0
- {remoterf-0.0.6.14 → remoterf-0.0.7.2}/src/remoteRF.egg-info/top_level.txt +0 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: remoteRF
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7.2
|
|
4
4
|
Summary: A python API to remotely access signal centric hardware. Client-side only!
|
|
5
5
|
Author: Ethan Ge
|
|
6
6
|
Author-email: ethoGalaxy@gmail.com
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
12
|
Requires-Dist: grpcio
|
|
14
13
|
Requires-Dist: protobuf
|
|
15
14
|
Requires-Dist: numpy
|
|
@@ -106,11 +105,7 @@ perms # To view your permissions
|
|
|
106
105
|
```bash
|
|
107
106
|
resdev # To reserve a device
|
|
108
107
|
|
|
109
|
-
|
|
110
|
-
Reserve Start Time (YYYY-MM-DD HH:MM): 2024-11-20 10:20
|
|
111
|
-
Reserve End Time (YYYY-MM-DD HH:MM): 2024-11-20 11:20
|
|
112
|
-
|
|
113
|
-
# Time is military time. You can also use arrow keys to cycle between previous commands.
|
|
108
|
+
# Input the number of days you want to view, and it will display available reservations in that time span.
|
|
114
109
|
|
|
115
110
|
Reservation successful. Thy Token -> example_token
|
|
116
111
|
|
|
@@ -88,11 +88,7 @@ perms # To view your permissions
|
|
|
88
88
|
```bash
|
|
89
89
|
resdev # To reserve a device
|
|
90
90
|
|
|
91
|
-
|
|
92
|
-
Reserve Start Time (YYYY-MM-DD HH:MM): 2024-11-20 10:20
|
|
93
|
-
Reserve End Time (YYYY-MM-DD HH:MM): 2024-11-20 11:20
|
|
94
|
-
|
|
95
|
-
# Time is military time. You can also use arrow keys to cycle between previous commands.
|
|
91
|
+
# Input the number of days you want to view, and it will display available reservations in that time span.
|
|
96
92
|
|
|
97
93
|
Reservation successful. Thy Token -> example_token
|
|
98
94
|
|
|
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name="remoteRF",
|
|
9
|
-
version="0.0.
|
|
9
|
+
version="0.0.7.2",
|
|
10
10
|
author="Ethan Ge",
|
|
11
11
|
author_email="ethoGalaxy@gmail.com",
|
|
12
12
|
description="A python API to remotely access signal centric hardware. Client-side only!",
|
|
@@ -14,6 +14,7 @@ setup(
|
|
|
14
14
|
long_description_content_type="text/markdown", # Specify that it's Markdown
|
|
15
15
|
packages=find_packages(where="src"), # Automatically finds subpackages like core, deviceA, deviceB
|
|
16
16
|
package_dir={"": "src"},
|
|
17
|
+
license_file='MIT',
|
|
17
18
|
include_package_data=True, # Includes files specified in MANIFEST.in
|
|
18
19
|
install_requires=[
|
|
19
20
|
"grpcio", "protobuf", "numpy", "prompt_toolkit", "python-dotenv"
|
|
@@ -23,7 +24,7 @@ setup(
|
|
|
23
24
|
"License :: OSI Approved :: MIT License",
|
|
24
25
|
"Operating System :: OS Independent",
|
|
25
26
|
],
|
|
26
|
-
python_requires='>=3.
|
|
27
|
+
python_requires='>=3.10',
|
|
27
28
|
entry_points={
|
|
28
29
|
'console_scripts': [
|
|
29
30
|
'remoterf-login=remoteRF.core.acc_login:main',
|
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
from . import *
|
|
2
|
+
from ..common.utils import *
|
|
3
|
+
|
|
4
|
+
import getpass
|
|
5
|
+
import os
|
|
6
|
+
import datetime
|
|
7
|
+
import time
|
|
8
|
+
import ast
|
|
9
|
+
|
|
10
|
+
from prompt_toolkit import PromptSession
|
|
11
|
+
|
|
12
|
+
account = RemoteRFAccount()
|
|
13
|
+
session = PromptSession()
|
|
14
|
+
|
|
15
|
+
def welcome():
|
|
16
|
+
printf("Welcome to Remote RF Account System.", (Sty.BOLD, Sty.BLUE))
|
|
17
|
+
try:
|
|
18
|
+
inpu = session.prompt(stylize("Please ", Sty.DEFAULT, "login", Sty.GREEN, " or ", Sty.DEFAULT, "register", Sty.RED, " to continue. (", Sty.DEFAULT, 'l', Sty.GREEN, "/", Sty.DEFAULT, 'r', Sty.RED, "):", Sty.DEFAULT))
|
|
19
|
+
if inpu == 'r':
|
|
20
|
+
print("Registering new account ...")
|
|
21
|
+
account.username = input("Username: ")
|
|
22
|
+
double_check = True
|
|
23
|
+
while double_check:
|
|
24
|
+
password = getpass.getpass("Password (Hidden): ")
|
|
25
|
+
password2 = getpass.getpass("Confirm Password: ")
|
|
26
|
+
if password == password2:
|
|
27
|
+
double_check = False
|
|
28
|
+
else:
|
|
29
|
+
print("Passwords do not match. Try again")
|
|
30
|
+
|
|
31
|
+
account.password = password
|
|
32
|
+
account.email = input("Email: ") # TODO: Email verification.
|
|
33
|
+
# check if login was valid
|
|
34
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
35
|
+
|
|
36
|
+
if not account.create_user():
|
|
37
|
+
welcome()
|
|
38
|
+
else:
|
|
39
|
+
account.username = input("Username: ")
|
|
40
|
+
account.password = getpass.getpass("Password (Hidden): ")
|
|
41
|
+
# check if login was valid
|
|
42
|
+
if not account.login_user():
|
|
43
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
44
|
+
print("Invalid login. Try again. Contact admin(s) if you forgot your password.")
|
|
45
|
+
welcome()
|
|
46
|
+
except KeyboardInterrupt:
|
|
47
|
+
exit()
|
|
48
|
+
except EOFError:
|
|
49
|
+
exit()
|
|
50
|
+
|
|
51
|
+
def title():
|
|
52
|
+
printf(f"Remote RF Account System", Sty.BOLD)
|
|
53
|
+
# printf(f"Logged in as: ", Sty.DEFAULT, f'{account.username}', Sty.MAGENTA)
|
|
54
|
+
printf(f"Input ", Sty.DEFAULT, "'help' ", Sty.BRIGHT_GREEN, "for avaliable commands.", Sty.DEFAULT)
|
|
55
|
+
|
|
56
|
+
def commands():
|
|
57
|
+
printf("Commands:", Sty.BOLD)
|
|
58
|
+
printf("'clear' ", Sty.MAGENTA, "- Clear Terminal", Sty.DEFAULT)
|
|
59
|
+
printf("'getdev' ", Sty.MAGENTA, "- View Devices", Sty.DEFAULT)
|
|
60
|
+
printf("'help' ", Sty.MAGENTA, "- Show this help message", Sty.DEFAULT)
|
|
61
|
+
printf("'perms' ", Sty.MAGENTA, "- View Permissions", Sty.DEFAULT)
|
|
62
|
+
printf("'exit' ", Sty.MAGENTA, "- Exit", Sty.DEFAULT)
|
|
63
|
+
printf("'getres' ", Sty.MAGENTA, "- View All Reservations", Sty.DEFAULT)
|
|
64
|
+
printf("'myres' ", Sty.MAGENTA, "- View My Reservations", Sty.DEFAULT)
|
|
65
|
+
printf("'cancelres' ", Sty.MAGENTA, "- Cancel a Reservation", Sty.DEFAULT)
|
|
66
|
+
printf("'resdev' ", Sty.MAGENTA, "- Reserve a Device", Sty.DEFAULT)
|
|
67
|
+
printf("'resdev s' ", Sty.MAGENTA, "- Reserve a Device (by single date)", Sty.DEFAULT)
|
|
68
|
+
# check if user is admin
|
|
69
|
+
if account.get_perms().results['UC'] == 'Admin':
|
|
70
|
+
printf("'naiveresdev' ", Sty.MAGENTA, "- Old implementation of reservations", Sty.DEFAULT)
|
|
71
|
+
|
|
72
|
+
def clear():
|
|
73
|
+
os.system('cls' if os.name == 'nt' else 'clear')
|
|
74
|
+
title()
|
|
75
|
+
|
|
76
|
+
def reservations():
|
|
77
|
+
data = account.get_reservations()
|
|
78
|
+
if 'ace' in data.results:
|
|
79
|
+
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
80
|
+
return
|
|
81
|
+
entries = []
|
|
82
|
+
|
|
83
|
+
for key, value in data.results.items():
|
|
84
|
+
parts = unmap_arg(value).split(',')
|
|
85
|
+
# Create a dictionary for each entry with named fields
|
|
86
|
+
entry = {
|
|
87
|
+
'username': parts[0],
|
|
88
|
+
'device_id': int(parts[1]), # Convert device_id to integer for proper numerical sorting
|
|
89
|
+
'start_time': datetime.datetime.strptime(parts[2], '%Y-%m-%d %H:%M:%S'), # Convert start_time to datetime
|
|
90
|
+
'end_time': parts[3]
|
|
91
|
+
}
|
|
92
|
+
entries.append(entry)
|
|
93
|
+
|
|
94
|
+
if (entries == []):
|
|
95
|
+
printf("No reservations found.", Sty.BOLD)
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
printf("Reservations:", Sty.BOLD)
|
|
99
|
+
|
|
100
|
+
# Sort the entries by device_id and then by start_time
|
|
101
|
+
sorted_entries = sorted(entries, key=lambda x: (x['device_id'], x['start_time']))
|
|
102
|
+
|
|
103
|
+
# Format the sorted entries into strings
|
|
104
|
+
for entry in sorted_entries:
|
|
105
|
+
printf(f'Device ID: ', Sty.RED, f'{entry["device_id"]}', Sty.MAGENTA, f', Start Time: ', Sty.RED, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f', End Time: ', Sty.RED, f'{entry["end_time"]}', Sty.BLUE)
|
|
106
|
+
|
|
107
|
+
def my_reservations():
|
|
108
|
+
data = account.get_reservations()
|
|
109
|
+
if 'ace' in data.results:
|
|
110
|
+
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
111
|
+
return
|
|
112
|
+
entries = []
|
|
113
|
+
|
|
114
|
+
for key, value in data.results.items():
|
|
115
|
+
parts = unmap_arg(value).split(',')
|
|
116
|
+
# Create a dictionary for each entry with named fields
|
|
117
|
+
entry = {
|
|
118
|
+
'username': parts[0],
|
|
119
|
+
'device_id': int(parts[1]), # Convert device_id to integer for proper numerical sorting
|
|
120
|
+
'start_time': datetime.datetime.strptime(parts[2], '%Y-%m-%d %H:%M:%S'), # Convert start_time to datetime
|
|
121
|
+
'end_time': parts[3]
|
|
122
|
+
}
|
|
123
|
+
entries.append(entry)
|
|
124
|
+
|
|
125
|
+
if (entries == []):
|
|
126
|
+
printf("No reservations found.", Sty.BOLD)
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
printf("Reservations under: ", Sty.BOLD, f'{account.username}', Sty.MAGENTA)
|
|
130
|
+
|
|
131
|
+
# Sort the entries by device_id and then by start_time
|
|
132
|
+
sorted_entries = sorted(entries, key=lambda x: (x['device_id'], x['start_time']))
|
|
133
|
+
|
|
134
|
+
for entry in sorted_entries:
|
|
135
|
+
if account.username == entry['username']:
|
|
136
|
+
printf(f'Device ID: ', Sty.RED, f'{entry["device_id"]}', Sty.MAGENTA, f', Start Time: ', Sty.RED, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f', End Time: ', Sty.RED, f'{entry["end_time"]}', Sty.BLUE)
|
|
137
|
+
|
|
138
|
+
def cancel_my_reservation():
|
|
139
|
+
## print all of ur reservations and their ids
|
|
140
|
+
## ask for id to cancel
|
|
141
|
+
## remove said reservation
|
|
142
|
+
data = account.get_reservations()
|
|
143
|
+
if 'ace' in data.results:
|
|
144
|
+
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
entries:list = []
|
|
148
|
+
|
|
149
|
+
for key, value in data.results.items():
|
|
150
|
+
parts = unmap_arg(value).split(',')
|
|
151
|
+
# Create a dictionary for each entry with named fields
|
|
152
|
+
entry = {
|
|
153
|
+
'id': -1,
|
|
154
|
+
'internal_id': key,
|
|
155
|
+
'username': parts[0],
|
|
156
|
+
'device_id': int(parts[1]), # Convert device_id to integer for proper numerical sorting
|
|
157
|
+
'start_time': datetime.datetime.strptime(parts[2], '%Y-%m-%d %H:%M:%S'), # Convert start_time to datetime
|
|
158
|
+
'end_time': parts[3]
|
|
159
|
+
}
|
|
160
|
+
if account.username == entry['username']:
|
|
161
|
+
entries.append(entry)
|
|
162
|
+
|
|
163
|
+
printf("Current Reservation(s) under ", Sty.BOLD, f'{account.username}:', Sty.MAGENTA)
|
|
164
|
+
|
|
165
|
+
sorted_entries = sorted(entries, key=lambda x: (x['device_id'], x['start_time'])) # sort by device_id and start_time
|
|
166
|
+
for i, entry in enumerate(sorted_entries): # label all reservations with unique id
|
|
167
|
+
entry['id'] = i
|
|
168
|
+
printf(f'Reservation ID: ', Sty.GRAY, f'{i}', Sty.MAGENTA, f' Device ID: ', Sty.GRAY, f'{entry["device_id"]}', Sty.BRIGHT_GREEN, f' Start Time: ', Sty.GRAY, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f' End Time: ', Sty.GRAY, f'{entry["end_time"]}', Sty.BLUE)
|
|
169
|
+
# print(f"Reservation ID {i}, Device ID: {entry['device_id']}, Start Time: {entry['start_time'].strftime('%Y-%m-%d %H:%M:%S')}, End Time: {entry['end_time']}")
|
|
170
|
+
|
|
171
|
+
if sorted_entries == []:
|
|
172
|
+
printf("No reservations found.", Sty.BOLD)
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
inpu = session.prompt(stylize("Enter the ID of the reservation you would like to cancel ", Sty.BOLD, '(abort with any non number key input)', Sty.RED, ': ', Sty.BOLD))
|
|
176
|
+
|
|
177
|
+
if inpu.isdigit():
|
|
178
|
+
id = int(inpu)
|
|
179
|
+
if id >= len(sorted_entries):
|
|
180
|
+
print("Invalid ID.")
|
|
181
|
+
return
|
|
182
|
+
|
|
183
|
+
# grab the reservation
|
|
184
|
+
for entry in sorted_entries:
|
|
185
|
+
if entry['id'] == id:
|
|
186
|
+
db_id = entry['internal_id']
|
|
187
|
+
if session.prompt(stylize(f'Cancel reservation ID ', Sty.DEFAULT, f'{id}', Sty.MAGENTA, f' Device ID: ', Sty.DEFAULT, f'{entry["device_id"]}', Sty.BRIGHT_GREEN, f' Start Time: ', Sty.GRAY, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f' End Time: ', Sty.DEFAULT, f'{entry["end_time"]}', Sty.BLUE, f' ? (y/n):', Sty.DEFAULT)) == 'y':
|
|
188
|
+
response = account.cancel_reservation(db_id)
|
|
189
|
+
if 'ace' in response.results:
|
|
190
|
+
print(f"Error: {unmap_arg(response.results['ace'])}")
|
|
191
|
+
elif 'UC' in response.results:
|
|
192
|
+
printf(f"Reservation ID ", Sty.DEFAULT, f'{id}', Sty.BRIGHT_BLUE, ' successfully canceled.', Sty.DEFAULT)
|
|
193
|
+
else:
|
|
194
|
+
print("Aborting. User canceled action.")
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
print(f"Error: No reservation found with ID {id}.")
|
|
198
|
+
else:
|
|
199
|
+
print("Aborting. A non integer key was given.")
|
|
200
|
+
|
|
201
|
+
def devices():
|
|
202
|
+
data = account.get_devices()
|
|
203
|
+
if 'ace' in data.results:
|
|
204
|
+
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
205
|
+
return
|
|
206
|
+
printf("Devices:", Sty.BOLD)
|
|
207
|
+
|
|
208
|
+
for key in sorted(data.results, key=int):
|
|
209
|
+
printf(f"Device ID:", Sty.DEFAULT, f' {key}', Sty.MAGENTA, f" Device Name: ", Sty.DEFAULT, f"{unmap_arg(data.results[key])}", Sty.GRAY)
|
|
210
|
+
|
|
211
|
+
def get_datetime(question:str):
|
|
212
|
+
timestamp = session.prompt(stylize(f'{question}', Sty.DEFAULT, ' (YYYY-MM-DD HH:MM): ', Sty.GRAY))
|
|
213
|
+
return datetime.datetime.strptime(timestamp + ':00', '%Y-%m-%d %H:%M:%S')
|
|
214
|
+
|
|
215
|
+
def reserve():
|
|
216
|
+
try:
|
|
217
|
+
id = session.prompt(stylize("Enter the device ID you would like to reserve: ", Sty.DEFAULT))
|
|
218
|
+
token = account.reserve_device(int(id), get_datetime("Reserve Start Time"), get_datetime("Reserve End Time"))
|
|
219
|
+
if token != '':
|
|
220
|
+
printf(f"Reservation successful. Thy Token -> ", Sty.BOLD, f"{token}", Sty.BG_GREEN)
|
|
221
|
+
printf(f"Please keep this token safe, as it is not saved on server side, and cannot be regenerated/reretrieved. ", Sty.DEFAULT)
|
|
222
|
+
except Exception as e:
|
|
223
|
+
printf(f"Error: {e}", Sty.BRIGHT_RED)
|
|
224
|
+
|
|
225
|
+
def perms():
|
|
226
|
+
data = account.get_perms()
|
|
227
|
+
if 'ace' in data.results:
|
|
228
|
+
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
results = ast.literal_eval(unmap_arg(data.results['UC']))[0]
|
|
232
|
+
printf(f'Permission Level: ', Sty.BOLD, f'{results[0]}', Sty.BLUE)
|
|
233
|
+
if results[0] == 'Normal User':
|
|
234
|
+
print(unmap_arg(data.results['details']))
|
|
235
|
+
elif results[0] == 'Power User':
|
|
236
|
+
printf(f'Max Reservations: ', Sty.DEFAULT, f'{results[3]}', Sty.MAGENTA)
|
|
237
|
+
printf(f'Max Reservation Duration (min): ', Sty.DEFAULT, f'{int(results[4]/60)}', Sty.MAGENTA)
|
|
238
|
+
printf(f'Device IDs allowed Access to: ', Sty.DEFAULT, f'{results[5]}', Sty.MAGENTA)
|
|
239
|
+
elif results[0] == 'Admin':
|
|
240
|
+
printf(f'No restrictions on reservation count or duration.', Sty.DEFAULT)
|
|
241
|
+
else:
|
|
242
|
+
printf(f"Error: Unknown permission level {results[0]}", Sty.BRIGHT_RED)
|
|
243
|
+
|
|
244
|
+
# New block scheduling
|
|
245
|
+
|
|
246
|
+
def fetch_all_devices():
|
|
247
|
+
response = rpc_client(
|
|
248
|
+
function_name='ACC:get_dev',
|
|
249
|
+
args={
|
|
250
|
+
"un": map_arg(account.username),
|
|
251
|
+
"pw": map_arg(account.password)
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
devices = []
|
|
255
|
+
# Sort keys using numeric conversion when possible.
|
|
256
|
+
for key in sorted(response.results.keys(), key=lambda k: int(k) if k.isdigit() else k):
|
|
257
|
+
devices.append(key)
|
|
258
|
+
return devices
|
|
259
|
+
|
|
260
|
+
def fetch_all_reservations():
|
|
261
|
+
data = account.get_reservations()
|
|
262
|
+
if 'ace' in data.results:
|
|
263
|
+
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
264
|
+
return []
|
|
265
|
+
entries = []
|
|
266
|
+
|
|
267
|
+
for key, value in data.results.items():
|
|
268
|
+
parts = unmap_arg(value).split(',')
|
|
269
|
+
# Convert both start and end times to datetime objects.
|
|
270
|
+
entry = {
|
|
271
|
+
'username': parts[0],
|
|
272
|
+
'device_id': int(parts[1]), # Stored as an int
|
|
273
|
+
'start_time': datetime.datetime.strptime(parts[2], '%Y-%m-%d %H:%M:%S'),
|
|
274
|
+
'end_time': datetime.datetime.strptime(parts[3], '%Y-%m-%d %H:%M:%S')
|
|
275
|
+
}
|
|
276
|
+
entries.append(entry)
|
|
277
|
+
return entries
|
|
278
|
+
|
|
279
|
+
def fetch_device_reservations_by_date(device_id: str, date: datetime.date):
|
|
280
|
+
"""
|
|
281
|
+
Filter all reservations to those for a specific device (by ID, as a string)
|
|
282
|
+
on the specified date.
|
|
283
|
+
"""
|
|
284
|
+
all_res = fetch_all_reservations()
|
|
285
|
+
device_reservations = []
|
|
286
|
+
for res in all_res:
|
|
287
|
+
# Convert the stored device_id (an int) to a string for comparison.
|
|
288
|
+
if str(res['device_id']) == device_id and res['start_time'].date() == date:
|
|
289
|
+
device_reservations.append((res['start_time'], res['end_time']))
|
|
290
|
+
|
|
291
|
+
return device_reservations
|
|
292
|
+
|
|
293
|
+
def fetch_reservations_for_range(start_day: datetime.date, end_day: datetime.date):
|
|
294
|
+
"""
|
|
295
|
+
Fetch all reservations (via fetch_all_reservations) and filter those whose start_time date falls between start_day and end_day (inclusive).
|
|
296
|
+
Returns a dictionary keyed by (device_id, day) (device_id as string, day as datetime.date) with a list of reservation tuples.
|
|
297
|
+
"""
|
|
298
|
+
all_res = fetch_all_reservations() # This calls the network only once.
|
|
299
|
+
res_dict = {}
|
|
300
|
+
for res in all_res:
|
|
301
|
+
res_day = res['start_time'].date()
|
|
302
|
+
if start_day <= res_day <= end_day:
|
|
303
|
+
key = (str(res['device_id']), res_day)
|
|
304
|
+
res_dict.setdefault(key, []).append((res['start_time'], res['end_time']))
|
|
305
|
+
return res_dict
|
|
306
|
+
|
|
307
|
+
def build_hourly_slots(date: datetime.date, start_hour: int = 0, end_hour: int = 24):
|
|
308
|
+
"""Generate 1-hour time slots for the given date."""
|
|
309
|
+
slots = []
|
|
310
|
+
for hour in range(start_hour, end_hour):
|
|
311
|
+
slot_start = datetime.datetime.combine(date, datetime.time(hour, 0))
|
|
312
|
+
slot_end = slot_start + datetime.timedelta(hours=1)
|
|
313
|
+
slots.append((slot_start, slot_end))
|
|
314
|
+
return slots
|
|
315
|
+
|
|
316
|
+
def is_slot_conflicting(slot: tuple, reservations: list):
|
|
317
|
+
"""Return True if the slot overlaps with any reservation in the provided list."""
|
|
318
|
+
slot_start, slot_end = slot
|
|
319
|
+
for res_start, res_end in reservations:
|
|
320
|
+
if slot_start < res_end and slot_end > res_start:
|
|
321
|
+
return True
|
|
322
|
+
return False
|
|
323
|
+
|
|
324
|
+
def display_free_slots_all(date: datetime.date):
|
|
325
|
+
"""
|
|
326
|
+
Display available 1-hour slots aggregated across all devices on a given date.
|
|
327
|
+
A slot is available if at least one device is free.
|
|
328
|
+
Omits any slots whose end time is in the past.
|
|
329
|
+
Returns a tuple (chosen_slot, chosen_device) where chosen_slot is (start_time, end_time)
|
|
330
|
+
and chosen_device is the ID (converted to int if possible) of an available device,
|
|
331
|
+
or (None, None) if no valid selection.
|
|
332
|
+
"""
|
|
333
|
+
devices = fetch_all_devices()
|
|
334
|
+
# Build a mapping: device id -> its reservations for the specified date.
|
|
335
|
+
device_reservations = {}
|
|
336
|
+
for dev in devices:
|
|
337
|
+
device_reservations[dev] = fetch_device_reservations_by_date(dev, date)
|
|
338
|
+
|
|
339
|
+
all_slots = build_hourly_slots(date)
|
|
340
|
+
now = datetime.datetime.now()
|
|
341
|
+
available_slots = {} # key: slot tuple, value: list of available device IDs
|
|
342
|
+
for slot in all_slots:
|
|
343
|
+
if slot[1] <= now:
|
|
344
|
+
continue
|
|
345
|
+
free_devices = []
|
|
346
|
+
for dev in devices:
|
|
347
|
+
if not is_slot_conflicting(slot, device_reservations[dev]):
|
|
348
|
+
free_devices.append(dev)
|
|
349
|
+
if free_devices:
|
|
350
|
+
available_slots[slot] = free_devices
|
|
351
|
+
|
|
352
|
+
if not available_slots:
|
|
353
|
+
print("No available time slots for any device on that day.")
|
|
354
|
+
return None, None
|
|
355
|
+
|
|
356
|
+
print("Available time slots (aggregated across devices):")
|
|
357
|
+
sorted_slots = sorted(available_slots.keys())
|
|
358
|
+
for idx, slot in enumerate(sorted_slots):
|
|
359
|
+
start_str = slot[0].strftime('%I:%M %p')
|
|
360
|
+
end_str = slot[1].strftime('%I:%M %p')
|
|
361
|
+
num_available = len(available_slots[slot])
|
|
362
|
+
print(f"{idx + 1}: {start_str} - {end_str} (Devices available: {num_available})")
|
|
363
|
+
|
|
364
|
+
try:
|
|
365
|
+
selection = int(input("Select a slot by number: "))
|
|
366
|
+
if selection < 1 or selection > len(sorted_slots):
|
|
367
|
+
print("Invalid selection.")
|
|
368
|
+
return None, None
|
|
369
|
+
except ValueError:
|
|
370
|
+
print("Invalid input. Please enter a number.")
|
|
371
|
+
return None, None
|
|
372
|
+
|
|
373
|
+
chosen_slot = sorted_slots[selection - 1]
|
|
374
|
+
# Automatically choose one device.
|
|
375
|
+
candidate = sorted(available_slots[chosen_slot])[0]
|
|
376
|
+
try:
|
|
377
|
+
chosen_device = int(candidate)
|
|
378
|
+
except ValueError:
|
|
379
|
+
chosen_device = candidate
|
|
380
|
+
return chosen_slot, chosen_device
|
|
381
|
+
|
|
382
|
+
def interactive_reserve_all():
|
|
383
|
+
"""
|
|
384
|
+
Interactive function that prompts for a reservation date,
|
|
385
|
+
displays aggregated free 1-hour slots (with an accurate count of available devices),
|
|
386
|
+
and reserves the chosen slot on one available device.
|
|
387
|
+
"""
|
|
388
|
+
try:
|
|
389
|
+
date_input = input("Enter the date for reservation (YYYY-MM-DD): ")
|
|
390
|
+
reservation_date = datetime.datetime.strptime(date_input, '%Y-%m-%d').date()
|
|
391
|
+
|
|
392
|
+
chosen_slot, chosen_device = display_free_slots_all(reservation_date)
|
|
393
|
+
if chosen_slot is None:
|
|
394
|
+
return
|
|
395
|
+
|
|
396
|
+
token = account.reserve_device(chosen_device, chosen_slot[0], chosen_slot[1])
|
|
397
|
+
if token != '':
|
|
398
|
+
print(f"Reservation successful on device {chosen_device}. Thy Token -> {token}")
|
|
399
|
+
print("Please keep this token safe, as it is not saved on server side, and cannot be regenerated/reretrieved.")
|
|
400
|
+
except Exception as e:
|
|
401
|
+
print(f"Error: {e}")
|
|
402
|
+
|
|
403
|
+
def display_free_slots_next_days(num_days: int):
|
|
404
|
+
"""
|
|
405
|
+
Display available 1-hour slots aggregated across all devices for the next num_days (starting today).
|
|
406
|
+
A slot is available if at least one device is free (i.e. not reserved during that slot).
|
|
407
|
+
Omits any slots whose end time is in the past (for today).
|
|
408
|
+
Returns a tuple (chosen_day, chosen_slot, chosen_device) where:
|
|
409
|
+
- chosen_day is a datetime.date for the reservation day,
|
|
410
|
+
- chosen_slot is a tuple (start_time, end_time), and
|
|
411
|
+
- chosen_device is one available device for that slot.
|
|
412
|
+
If no slot is available, returns (None, None, None).
|
|
413
|
+
"""
|
|
414
|
+
today = datetime.date.today()
|
|
415
|
+
devices = fetch_all_devices()
|
|
416
|
+
now = datetime.datetime.now()
|
|
417
|
+
|
|
418
|
+
# Compute the end day (inclusive)
|
|
419
|
+
end_day = today + datetime.timedelta(days=num_days - 1)
|
|
420
|
+
# Fetch all reservations for the entire range (one network call)
|
|
421
|
+
reservations_range = fetch_reservations_for_range(today, end_day)
|
|
422
|
+
|
|
423
|
+
available_slots = [] # List of tuples: (day, slot, free_devices)
|
|
424
|
+
|
|
425
|
+
for i in range(num_days):
|
|
426
|
+
day = today + datetime.timedelta(days=i)
|
|
427
|
+
all_slots = build_hourly_slots(day)
|
|
428
|
+
for slot in all_slots:
|
|
429
|
+
# If the day is today, skip slots that have already ended.
|
|
430
|
+
if day == today and slot[1] <= now:
|
|
431
|
+
continue
|
|
432
|
+
free_devices = []
|
|
433
|
+
for dev in devices:
|
|
434
|
+
# Look up reservations for this device on this day.
|
|
435
|
+
key = (dev, day)
|
|
436
|
+
current_res = reservations_range.get(key, [])
|
|
437
|
+
if not is_slot_conflicting(slot, current_res):
|
|
438
|
+
free_devices.append(dev)
|
|
439
|
+
if free_devices:
|
|
440
|
+
available_slots.append((day, slot, free_devices))
|
|
441
|
+
|
|
442
|
+
if not available_slots:
|
|
443
|
+
print(f"No available time slots for any device in the next {num_days} days.")
|
|
444
|
+
return None, None, None
|
|
445
|
+
|
|
446
|
+
# Sort by day and slot start time.
|
|
447
|
+
available_slots.sort(key=lambda x: (x[0], x[1][0]))
|
|
448
|
+
|
|
449
|
+
print(f"Available time slots for the next {num_days} days:")
|
|
450
|
+
last_day = None
|
|
451
|
+
for idx, (day, slot, free_devices) in enumerate(available_slots):
|
|
452
|
+
# When a new day starts, print a header line with the date and formatted day.
|
|
453
|
+
if last_day is None or day != last_day:
|
|
454
|
+
# Format: YYYY-MM-DD (Abbreviated Weekday) AbbrevMonth. day
|
|
455
|
+
# Example: "2025-02-04 (Tue) Feb. 4"
|
|
456
|
+
day_header = f"{day.strftime('%Y-%m-%d')} ({day.strftime('%a')}) {day.strftime('%b')}. {day.day}"
|
|
457
|
+
print("\n" + day_header)
|
|
458
|
+
last_day = day
|
|
459
|
+
start_str = slot[0].strftime('%I:%M %p')
|
|
460
|
+
end_str = slot[1].strftime('%I:%M %p')
|
|
461
|
+
print(f" {idx+1}: {start_str} - {end_str} (Devices available: {len(free_devices)})")
|
|
462
|
+
|
|
463
|
+
try:
|
|
464
|
+
selection = int(input("Select a slot by number: "))
|
|
465
|
+
if selection < 1 or selection > len(available_slots):
|
|
466
|
+
print("Invalid selection.")
|
|
467
|
+
return None, None, None
|
|
468
|
+
except ValueError:
|
|
469
|
+
print("Invalid input. Please enter a number.")
|
|
470
|
+
return None, None, None
|
|
471
|
+
|
|
472
|
+
chosen_day, chosen_slot, free_devices_for_slot = available_slots[selection - 1]
|
|
473
|
+
candidate = sorted(free_devices_for_slot)[0]
|
|
474
|
+
try:
|
|
475
|
+
chosen_device = int(candidate)
|
|
476
|
+
except ValueError:
|
|
477
|
+
chosen_device = candidate
|
|
478
|
+
return chosen_day, chosen_slot, chosen_device
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def interactive_reserve_next_days():
|
|
482
|
+
"""
|
|
483
|
+
Interactive function that prompts the user for the number of days (starting today) to check for available reservations.
|
|
484
|
+
It displays aggregated free 1-hour slots over that period and reserves the chosen slot on one available device,
|
|
485
|
+
after confirming with the user.
|
|
486
|
+
"""
|
|
487
|
+
try:
|
|
488
|
+
num_days = int(input("Enter the number of days to check for available reservations (starting today): "))
|
|
489
|
+
chosen_day, chosen_slot, chosen_device = display_free_slots_next_days(num_days)
|
|
490
|
+
if chosen_slot is None:
|
|
491
|
+
return
|
|
492
|
+
|
|
493
|
+
start_time = chosen_slot[0].strftime('%I:%M %p')
|
|
494
|
+
end_time = chosen_slot[1].strftime('%I:%M %p')
|
|
495
|
+
confirmation = input(f"You have selected a reservation on {chosen_day.strftime('%Y-%m-%d')} from {start_time} to {end_time} on device {chosen_device}. Confirm reservation? (y/n): ").strip().lower()
|
|
496
|
+
if confirmation != 'y':
|
|
497
|
+
print("Reservation cancelled.")
|
|
498
|
+
return
|
|
499
|
+
|
|
500
|
+
token = account.reserve_device(chosen_device, chosen_slot[0], chosen_slot[1])
|
|
501
|
+
if token != '':
|
|
502
|
+
print(f"Reservation successful on device {chosen_device} for {chosen_day.strftime('%Y-%m-%d')}. Thy Token -> {token}")
|
|
503
|
+
print("Please keep this token safe, as it is not saved on server side, and cannot be regenerated/reretrieved.")
|
|
504
|
+
except Exception as e:
|
|
505
|
+
print(f"Error: {e}")
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
welcome()
|
|
509
|
+
clear()
|
|
510
|
+
|
|
511
|
+
while True:
|
|
512
|
+
try:
|
|
513
|
+
inpu = session.prompt(stylize(f'{account.username}@remote_rf: ', Sty.BLUE))
|
|
514
|
+
if inpu == "clear":
|
|
515
|
+
clear()
|
|
516
|
+
elif inpu == "getdev":
|
|
517
|
+
devices()
|
|
518
|
+
elif inpu == "help" or inpu == "h":
|
|
519
|
+
commands()
|
|
520
|
+
elif inpu == "perms":
|
|
521
|
+
perms()
|
|
522
|
+
elif inpu == "quit" or inpu == "exit":
|
|
523
|
+
break
|
|
524
|
+
elif inpu == "getres":
|
|
525
|
+
reservations()
|
|
526
|
+
elif inpu == "myres":
|
|
527
|
+
my_reservations()
|
|
528
|
+
elif inpu == "resdev s":
|
|
529
|
+
interactive_reserve_all()
|
|
530
|
+
elif inpu == "resdev":
|
|
531
|
+
interactive_reserve_next_days()
|
|
532
|
+
elif inpu == 'cancelres':
|
|
533
|
+
cancel_my_reservation()
|
|
534
|
+
elif inpu == 'naiveresdev':
|
|
535
|
+
# check if user is admin
|
|
536
|
+
if account.get_perms().results['UC'] == 'Admin':
|
|
537
|
+
reserve()
|
|
538
|
+
else:
|
|
539
|
+
print(f"Unknown command: {inpu}")
|
|
540
|
+
except KeyboardInterrupt:
|
|
541
|
+
break
|
|
542
|
+
except EOFError:
|
|
543
|
+
break
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: remoteRF
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.7.2
|
|
4
4
|
Summary: A python API to remotely access signal centric hardware. Client-side only!
|
|
5
5
|
Author: Ethan Ge
|
|
6
6
|
Author-email: ethoGalaxy@gmail.com
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
|
-
License-File: LICENSE
|
|
13
12
|
Requires-Dist: grpcio
|
|
14
13
|
Requires-Dist: protobuf
|
|
15
14
|
Requires-Dist: numpy
|
|
@@ -106,11 +105,7 @@ perms # To view your permissions
|
|
|
106
105
|
```bash
|
|
107
106
|
resdev # To reserve a device
|
|
108
107
|
|
|
109
|
-
|
|
110
|
-
Reserve Start Time (YYYY-MM-DD HH:MM): 2024-11-20 10:20
|
|
111
|
-
Reserve End Time (YYYY-MM-DD HH:MM): 2024-11-20 11:20
|
|
112
|
-
|
|
113
|
-
# Time is military time. You can also use arrow keys to cycle between previous commands.
|
|
108
|
+
# Input the number of days you want to view, and it will display available reservations in that time span.
|
|
114
109
|
|
|
115
110
|
Reservation successful. Thy Token -> example_token
|
|
116
111
|
|
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
from . import *
|
|
2
|
-
from ..common.utils import *
|
|
3
|
-
|
|
4
|
-
import getpass
|
|
5
|
-
import os
|
|
6
|
-
import datetime
|
|
7
|
-
import time
|
|
8
|
-
import ast
|
|
9
|
-
|
|
10
|
-
from prompt_toolkit import PromptSession
|
|
11
|
-
|
|
12
|
-
account = RemoteRFAccount()
|
|
13
|
-
session = PromptSession()
|
|
14
|
-
|
|
15
|
-
def welcome():
|
|
16
|
-
printf("Welcome to Remote RF Account System.", (Sty.BOLD, Sty.BLUE))
|
|
17
|
-
try:
|
|
18
|
-
inpu = session.prompt(stylize("Please ", Sty.DEFAULT, "login", Sty.GREEN, " or ", Sty.DEFAULT, "register", Sty.RED, " to continue. (", Sty.DEFAULT, 'l', Sty.GREEN, "/", Sty.DEFAULT, 'r', Sty.RED, "):", Sty.DEFAULT))
|
|
19
|
-
if inpu == 'r':
|
|
20
|
-
print("Registering new account ...")
|
|
21
|
-
account.username = input("Username: ")
|
|
22
|
-
double_check = True
|
|
23
|
-
while double_check:
|
|
24
|
-
password = getpass.getpass("Password (Hidden): ")
|
|
25
|
-
password2 = getpass.getpass("Confirm Password: ")
|
|
26
|
-
if password == password2:
|
|
27
|
-
double_check = False
|
|
28
|
-
else:
|
|
29
|
-
print("Passwords do not match. Try again")
|
|
30
|
-
|
|
31
|
-
account.password = password
|
|
32
|
-
account.email = input("Email: ") # TODO: Email verification.
|
|
33
|
-
# check if login was valid
|
|
34
|
-
os.system('cls' if os.name == 'nt' else 'clear')
|
|
35
|
-
|
|
36
|
-
if not account.create_user():
|
|
37
|
-
welcome()
|
|
38
|
-
else:
|
|
39
|
-
account.username = input("Username: ")
|
|
40
|
-
account.password = getpass.getpass("Password (Hidden): ")
|
|
41
|
-
# check if login was valid
|
|
42
|
-
if not account.login_user():
|
|
43
|
-
os.system('cls' if os.name == 'nt' else 'clear')
|
|
44
|
-
print("Invalid login. Try again.")
|
|
45
|
-
welcome()
|
|
46
|
-
except KeyboardInterrupt:
|
|
47
|
-
exit()
|
|
48
|
-
except EOFError:
|
|
49
|
-
exit()
|
|
50
|
-
|
|
51
|
-
def title():
|
|
52
|
-
printf(f"Remote RF Account System", Sty.BOLD)
|
|
53
|
-
# printf(f"Logged in as: ", Sty.DEFAULT, f'{account.username}', Sty.MAGENTA)
|
|
54
|
-
printf(f"Input ", Sty.DEFAULT, "'help' ", Sty.BRIGHT_GREEN, "for avaliable commands.", Sty.DEFAULT)
|
|
55
|
-
|
|
56
|
-
def commands():
|
|
57
|
-
printf("Commands:", Sty.BOLD)
|
|
58
|
-
printf("'clear' ", Sty.MAGENTA, "- Clear Terminal", Sty.DEFAULT)
|
|
59
|
-
printf("'getdev' ", Sty.MAGENTA, "- View Devices", Sty.DEFAULT)
|
|
60
|
-
printf("'help' ", Sty.MAGENTA, "- Show this help message", Sty.DEFAULT)
|
|
61
|
-
printf("'perms' ", Sty.MAGENTA, "- View Permissions", Sty.DEFAULT)
|
|
62
|
-
printf("'exit' ", Sty.MAGENTA, "- Exit", Sty.DEFAULT)
|
|
63
|
-
printf("'getres' ", Sty.MAGENTA, "- View All Reservations", Sty.DEFAULT)
|
|
64
|
-
printf("'myres' ", Sty.MAGENTA, "- View My Reservations", Sty.DEFAULT)
|
|
65
|
-
printf("'cancelres' ", Sty.MAGENTA, "- Cancel a Reservation", Sty.DEFAULT)
|
|
66
|
-
printf("'resdev' ", Sty.MAGENTA, "- Reserve a Device", Sty.DEFAULT)
|
|
67
|
-
|
|
68
|
-
def clear():
|
|
69
|
-
os.system('cls' if os.name == 'nt' else 'clear')
|
|
70
|
-
title()
|
|
71
|
-
|
|
72
|
-
def reservations():
|
|
73
|
-
data = account.get_reservations()
|
|
74
|
-
if 'ace' in data.results:
|
|
75
|
-
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
76
|
-
return
|
|
77
|
-
entries = []
|
|
78
|
-
|
|
79
|
-
for key, value in data.results.items():
|
|
80
|
-
parts = unmap_arg(value).split(',')
|
|
81
|
-
# Create a dictionary for each entry with named fields
|
|
82
|
-
entry = {
|
|
83
|
-
'username': parts[0],
|
|
84
|
-
'device_id': int(parts[1]), # Convert device_id to integer for proper numerical sorting
|
|
85
|
-
'start_time': datetime.datetime.strptime(parts[2], '%Y-%m-%d %H:%M:%S'), # Convert start_time to datetime
|
|
86
|
-
'end_time': parts[3]
|
|
87
|
-
}
|
|
88
|
-
entries.append(entry)
|
|
89
|
-
|
|
90
|
-
if (entries == []):
|
|
91
|
-
printf("No reservations found.", Sty.BOLD)
|
|
92
|
-
return
|
|
93
|
-
|
|
94
|
-
printf("Reservations:", Sty.BOLD)
|
|
95
|
-
|
|
96
|
-
# Sort the entries by device_id and then by start_time
|
|
97
|
-
sorted_entries = sorted(entries, key=lambda x: (x['device_id'], x['start_time']))
|
|
98
|
-
|
|
99
|
-
# Format the sorted entries into strings
|
|
100
|
-
for entry in sorted_entries:
|
|
101
|
-
printf(f'Device ID: ', Sty.RED, f'{entry["device_id"]}', Sty.MAGENTA, f', Start Time: ', Sty.RED, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f', End Time: ', Sty.RED, f'{entry["end_time"]}', Sty.BLUE)
|
|
102
|
-
|
|
103
|
-
def my_reservations():
|
|
104
|
-
data = account.get_reservations()
|
|
105
|
-
if 'ace' in data.results:
|
|
106
|
-
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
107
|
-
return
|
|
108
|
-
entries = []
|
|
109
|
-
|
|
110
|
-
for key, value in data.results.items():
|
|
111
|
-
parts = unmap_arg(value).split(',')
|
|
112
|
-
# Create a dictionary for each entry with named fields
|
|
113
|
-
entry = {
|
|
114
|
-
'username': parts[0],
|
|
115
|
-
'device_id': int(parts[1]), # Convert device_id to integer for proper numerical sorting
|
|
116
|
-
'start_time': datetime.datetime.strptime(parts[2], '%Y-%m-%d %H:%M:%S'), # Convert start_time to datetime
|
|
117
|
-
'end_time': parts[3]
|
|
118
|
-
}
|
|
119
|
-
entries.append(entry)
|
|
120
|
-
|
|
121
|
-
if (entries == []):
|
|
122
|
-
printf("No reservations found.", Sty.BOLD)
|
|
123
|
-
return
|
|
124
|
-
|
|
125
|
-
printf("Reservations under: ", Sty.BOLD, f'{account.username}', Sty.MAGENTA)
|
|
126
|
-
|
|
127
|
-
# Sort the entries by device_id and then by start_time
|
|
128
|
-
sorted_entries = sorted(entries, key=lambda x: (x['device_id'], x['start_time']))
|
|
129
|
-
|
|
130
|
-
for entry in sorted_entries:
|
|
131
|
-
if account.username == entry['username']:
|
|
132
|
-
printf(f'Device ID: ', Sty.RED, f'{entry["device_id"]}', Sty.MAGENTA, f', Start Time: ', Sty.RED, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f', End Time: ', Sty.RED, f'{entry["end_time"]}', Sty.BLUE)
|
|
133
|
-
|
|
134
|
-
def cancel_my_reservation():
|
|
135
|
-
## print all of ur reservations and their ids
|
|
136
|
-
## ask for id to cancel
|
|
137
|
-
## remove said reservation
|
|
138
|
-
data = account.get_reservations()
|
|
139
|
-
if 'ace' in data.results:
|
|
140
|
-
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
141
|
-
return
|
|
142
|
-
|
|
143
|
-
entries:list = []
|
|
144
|
-
|
|
145
|
-
for key, value in data.results.items():
|
|
146
|
-
parts = unmap_arg(value).split(',')
|
|
147
|
-
# Create a dictionary for each entry with named fields
|
|
148
|
-
entry = {
|
|
149
|
-
'id': -1,
|
|
150
|
-
'internal_id': key,
|
|
151
|
-
'username': parts[0],
|
|
152
|
-
'device_id': int(parts[1]), # Convert device_id to integer for proper numerical sorting
|
|
153
|
-
'start_time': datetime.datetime.strptime(parts[2], '%Y-%m-%d %H:%M:%S'), # Convert start_time to datetime
|
|
154
|
-
'end_time': parts[3]
|
|
155
|
-
}
|
|
156
|
-
if account.username == entry['username']:
|
|
157
|
-
entries.append(entry)
|
|
158
|
-
|
|
159
|
-
printf("Current Reservation(s) under ", Sty.BOLD, f'{account.username}:', Sty.MAGENTA)
|
|
160
|
-
|
|
161
|
-
sorted_entries = sorted(entries, key=lambda x: (x['device_id'], x['start_time'])) # sort by device_id and start_time
|
|
162
|
-
for i, entry in enumerate(sorted_entries): # label all reservations with unique id
|
|
163
|
-
entry['id'] = i
|
|
164
|
-
printf(f'Reservation ID: ', Sty.GRAY, f'{i}', Sty.MAGENTA, f' Device ID: ', Sty.GRAY, f'{entry["device_id"]}', Sty.BRIGHT_GREEN, f' Start Time: ', Sty.GRAY, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f' End Time: ', Sty.GRAY, f'{entry["end_time"]}', Sty.BLUE)
|
|
165
|
-
# print(f"Reservation ID {i}, Device ID: {entry['device_id']}, Start Time: {entry['start_time'].strftime('%Y-%m-%d %H:%M:%S')}, End Time: {entry['end_time']}")
|
|
166
|
-
|
|
167
|
-
if sorted_entries == []:
|
|
168
|
-
printf("No reservations found.", Sty.BOLD)
|
|
169
|
-
return
|
|
170
|
-
|
|
171
|
-
inpu = session.prompt(stylize("Enter the ID of the reservation you would like to cancel ", Sty.BOLD, '(abort with any non number key input)', Sty.RED, ': ', Sty.BOLD))
|
|
172
|
-
|
|
173
|
-
if inpu.isdigit():
|
|
174
|
-
id = int(inpu)
|
|
175
|
-
if id >= len(sorted_entries):
|
|
176
|
-
print("Invalid ID.")
|
|
177
|
-
return
|
|
178
|
-
|
|
179
|
-
#grab the reservation
|
|
180
|
-
for entry in sorted_entries:
|
|
181
|
-
if entry['id'] == id:
|
|
182
|
-
db_id = entry['internal_id']
|
|
183
|
-
if session.prompt(stylize(f'Cancel reservation ID ', Sty.DEFAULT, f'{id}', Sty.MAGENTA, f' Device ID: ', Sty.DEFAULT, f'{entry["device_id"]}', Sty.BRIGHT_GREEN, f' Start Time: ', Sty.GRAY, f'{entry["start_time"].strftime("%Y-%m-%d %H:%M:%S")}', Sty.BLUE, f' End Time: ', Sty.DEFAULT, f'{entry["end_time"]}', Sty.BLUE, f' ? (y/n):', Sty.DEFAULT)) == 'y':
|
|
184
|
-
response = account.cancel_reservation(db_id)
|
|
185
|
-
if 'ace' in response.results:
|
|
186
|
-
print(f"Error: {unmap_arg(response.results['ace'])}")
|
|
187
|
-
elif 'UC' in response.results:
|
|
188
|
-
printf(f"Reservation ID ", Sty.DEFAULT, f'{id}', Sty.BRIGHT_BLUE, ' successfully canceled.', Sty.DEFAULT)
|
|
189
|
-
else:
|
|
190
|
-
print("Aborting. User canceled action.")
|
|
191
|
-
return
|
|
192
|
-
|
|
193
|
-
print(f"Error: No reservation found with ID {id}.")
|
|
194
|
-
else:
|
|
195
|
-
print("Aborting. A non integer key was given.")
|
|
196
|
-
|
|
197
|
-
def devices():
|
|
198
|
-
data = account.get_devices()
|
|
199
|
-
if 'ace' in data.results:
|
|
200
|
-
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
201
|
-
return
|
|
202
|
-
printf("Devices:", Sty.BOLD)
|
|
203
|
-
|
|
204
|
-
for key in sorted(data.results, key=int):
|
|
205
|
-
printf(f"Device ID:", Sty.DEFAULT, f' {key}', Sty.MAGENTA, f" Device Name: ", Sty.DEFAULT, f"{unmap_arg(data.results[key])}", Sty.GRAY)
|
|
206
|
-
|
|
207
|
-
def get_datetime(question:str):
|
|
208
|
-
timestamp = session.prompt(stylize(f'{question}', Sty.DEFAULT, ' (YYYY-MM-DD HH:MM): ', Sty.GRAY))
|
|
209
|
-
return datetime.datetime.strptime(timestamp + ':00', '%Y-%m-%d %H:%M:%S')
|
|
210
|
-
|
|
211
|
-
def reserve():
|
|
212
|
-
try:
|
|
213
|
-
id = session.prompt(stylize("Enter the device ID you would like to reserve: ", Sty.DEFAULT))
|
|
214
|
-
token = account.reserve_device(int(id), get_datetime("Reserve Start Time"), get_datetime("Reserve End Time"))
|
|
215
|
-
if token != '':
|
|
216
|
-
printf(f"Reservation successful. Thy Token -> ", Sty.BOLD, f"{token}", Sty.BG_GREEN)
|
|
217
|
-
printf(f"Please keep this token safe, as it is not saved on server side, and cannot be regenerated/reretrieved. ", Sty.DEFAULT)
|
|
218
|
-
except Exception as e:
|
|
219
|
-
printf(f"Error: {e}", Sty.BRIGHT_RED)
|
|
220
|
-
|
|
221
|
-
def perms():
|
|
222
|
-
data = account.get_perms()
|
|
223
|
-
if 'ace' in data.results:
|
|
224
|
-
print(f"Error: {unmap_arg(data.results['ace'])}")
|
|
225
|
-
return
|
|
226
|
-
|
|
227
|
-
results = ast.literal_eval(unmap_arg(data.results['UC']))[0]
|
|
228
|
-
printf(f'Permission Level: ', Sty.BOLD, f'{results[0]}', Sty.BLUE)
|
|
229
|
-
if results[0] == 'Normal User':
|
|
230
|
-
print(unmap_arg(data.results['details']))
|
|
231
|
-
elif results[0] == 'Power User':
|
|
232
|
-
printf(f'Max Reservations: ', Sty.DEFAULT, f'{results[3]}', Sty.MAGENTA)
|
|
233
|
-
printf(f'Max Reservation Duration (min): ', Sty.DEFAULT, f'{int(results[4]/60)}', Sty.MAGENTA)
|
|
234
|
-
printf(f'Device IDs allowed Access to: ', Sty.DEFAULT, f'{results[5]}', Sty.MAGENTA)
|
|
235
|
-
elif results[0] == 'Admin':
|
|
236
|
-
printf(f'No restrictions on reservation count or duration.', Sty.DEFAULT)
|
|
237
|
-
else:
|
|
238
|
-
printf(f"Error: Unknown permission level {results[0]}", Sty.BRIGHT_RED)
|
|
239
|
-
|
|
240
|
-
welcome()
|
|
241
|
-
clear()
|
|
242
|
-
|
|
243
|
-
while True:
|
|
244
|
-
try:
|
|
245
|
-
inpu = session.prompt(stylize(f'{account.username}@remote_rf: ', Sty.BLUE))
|
|
246
|
-
if inpu == "clear":
|
|
247
|
-
clear()
|
|
248
|
-
elif inpu == "getdev":
|
|
249
|
-
devices()
|
|
250
|
-
elif inpu == "help" or inpu == "h":
|
|
251
|
-
commands()
|
|
252
|
-
elif inpu == "perms":
|
|
253
|
-
perms()
|
|
254
|
-
elif inpu == "quit" or inpu == "exit":
|
|
255
|
-
break
|
|
256
|
-
elif inpu == "getres":
|
|
257
|
-
reservations()
|
|
258
|
-
elif inpu == "myres":
|
|
259
|
-
my_reservations()
|
|
260
|
-
elif inpu == "resdev":
|
|
261
|
-
reserve()
|
|
262
|
-
elif inpu == 'cancelres':
|
|
263
|
-
cancel_my_reservation()
|
|
264
|
-
else:
|
|
265
|
-
print(f"Unknown command: {inpu}")
|
|
266
|
-
except KeyboardInterrupt:
|
|
267
|
-
break
|
|
268
|
-
except EOFError:
|
|
269
|
-
break
|
|
270
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|