Passed
Push — main ( ce71f4...8ca48e )
by Rahn20
03:51
created

main.Handler.start()   A

Complexity

Conditions 1

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nop 1
dl 0
loc 18
rs 9.95
c 0
b 0
f 0
1
#!/usr/bin/python3
2
#flake8 --extend-ignore=R1723
3
4
""" Main file for scooter program with Handler class. """
5
6
import sys
7
import inspect
8
import time
9
from threading import Thread
10
from datetime import datetime, timedelta
11
12
from src.api import ApiData
13
from src.scooter import Scooter
14
15
class Handler():
16
    """ Handler class """
17
18
    ## menu options
19
    _OPTIONS = {
20
        "1": "start_scooter",
21
        "2": "stop_running",
22
        "3": "get_scooter_info",
23
        "4": "return_scooter",
24
        "5": "charge_scooter",
25
    }
26
27
    ## scooter status
28
    _running = False
29
30
    ## Leave the scooter, stop the thread
31
    _return = False
32
33
    ## start renting time, a list of integers [H:M:S]
34
    _start_time = None
35
36
37
    def __init__(self) -> None:
38
        """ Initialize class """
39
        self.scooter = Scooter()
40
        self.api = ApiData(user_id = 1)   ## user id (random user)
41
42
        ## create a Thread
43
        self._thread = Thread(target=self.run, name="Move scooter")
44
45
46
    def _get_method(self, method_name):
47
        """ Uses function getattr() to dynamically get value of an attribute. """
48
        return getattr(self, self._OPTIONS[method_name])
49
50
51
    def _print_menu(self) -> None:
52
        """ Prints options for the program. """
53
        menu = ""
54
55
        for key in sorted(self._OPTIONS):
56
            method = self._get_method(key)
57
            docstring = inspect.getdoc(method)
58
59
            menu += "{choice}: {explanation}\n".format(
60
                choice = key,
61
                explanation = docstring
62
            )
63
64
        print(chr(27) + "[2J" + chr(27) + "[;H")
65
        print(menu)
66
67
68
    def run(self) -> None:
69
        """ Start a Thread. """
70
        while True:
71
            if self._return:
72
                break
73
            if self.scooter.check_scooter_in_city() is False:
74
                print("\nScooter is outside of the city\n")
75
                print("You can't use the scooter anymore. Press 4 to cancel the rental.")
76
77
                self.stop_running()
78
                time.sleep(10)
79
            elif self._running:
80
                self.battery_check()
81
                time.sleep(5)
82
83
84
    def battery_check(self) -> None:
85
        """ Check battery level, if battery < 20% print warning message and stop the scooter. """
86
        if self.scooter.check_battery():
87
            self.stop_running()
88
            print("\n\033[1;31m*\033[1;0m Low battery!! the scooter needs to be charged.")
89
        else:
90
            self.scooter.change_location()
91
            self.scooter.move_scooter()
92
93
            ## update API
94
            self.api.update_scooter()
95
96
97
    def start_scooter(self) -> None:
98
        """ Move the scooter to a random location. """
99
        if self.scooter.check_battery():
100
            print("\n\033[1;31m*\033[1;0m Low battery!! the scooter needs to be charged.")
101
            print("\nPress 4 to end the rental and leave the scooter at charging station.")
102
            print("Or you can press 5 to fully charge the scooter and end the rental.")
103
        else:
104
            self._running = True
105
106
107
    def stop_running(self) -> None:
108
        """ Stop the scooter. """
109
        if self._running is True:
110
            self._running = False
111
            self.scooter.stop_scooter()
112
113
            ## update API
114
            self.api.update_scooter()
115
116
117
    def rental_time(self) -> timedelta:
118
        """ Returns the time the scooter has been rented by a user. """
119
        current = datetime.now().time().strftime("%H:%M:%S")
120
121
        current_time = timedelta(
122
            hours = int(current[0:2]),
123
            minutes = int(current[3:5]),
124
            seconds=int(current[6:8])
125
        )
126
127
        start_time = timedelta(
128
            hours = self._start_time[0],
129
            minutes = self._start_time[1],
130
            seconds = self._start_time[2]
131
        )
132
133
        return current_time - start_time
134
135
136
    def get_scooter_info(self) -> None:
137
        """ Get the scooter information. """
138
        if self._running:
139
            print("\nScooter is running.")
140
        else:
141
            print("\nScooter is in sleep mode.")
142
143
        print(self.scooter.__str__())
144
        print("Rent time: " + str(self.rental_time()))
145
146
147
    def charge_scooter(self):
148
        """Charge the scooter and end the rental. """
149
        ## Stop thread
150
        self._return = True
151
        self._running = False
152
        self._thread.join()
153
154
        ## Fully charges the battery and leave it at the charging Station
155
        charging = self.api.get_station("1")
156
157
        self.scooter.data["battery"] = 100
158
        self.scooter.stop_scooter("1")          ## Available status
159
        self.scooter.move_to_station(charging)
160
161
        ## update API
162
        self.api.update_scooter()
163
        self.api.update_log()
164
        sys.exit()
165
166
167
    def return_scooter(self):
168
        """ Stop the rental and leave the scooter. """
169
        ## Stop thread
170
        self._return = True
171
        self._running = False
172
        self._thread.join()
173
        self.end_rental()
174
175
        ## update API
176
        self.api.update_scooter()
177
        self.api.update_log()
178
        sys.exit()
179
180
181
    def end_rental(self) -> None:
182
        """ Checks scooter's battery/maintenance/zone and stops the scooter. """
183
        if self.scooter.check_scooter_in_city() is False:
184
            self.scooter.stop_scooter("2")                  ## Unavailable status
185
        elif self.scooter.check_battery():
186
            charging = self.api.get_station("1")            ## Charging Station
187
188
            self.scooter.stop_scooter("4")                  ## Charging status
189
            self.scooter.move_to_station(charging)
190
        elif self.scooter.check_maintenance():
191
            maintenance = self.api.get_station("4")         ## Maintenance Station
192
193
            self.scooter.stop_scooter("3")                  ## Maintenance status
194
            self.scooter.move_to_station(maintenance)
195
        else:
196
            self.scooter.stop_scooter("1")                  ## Available status
197
198
199
    def rent_scooter(self):
200
        """ Print menu """
201
        while True:
202
            self._print_menu()
203
            choice = input("What do you want to do: ")
204
205
            try:
206
                self._get_method(choice.lower())()
207
            except KeyError:
208
                print("\nInvalid choice!")
209
210
            input("\nPress any key to continue ...")
211
212
213
    def start(self):
214
        """
215
        Makes requests to the API, creates log, gets cities and
216
        stations data. start a thread and view rent menu.
217
        """
218
        ## get info from api
219
        self.api.create_log()
220
        city = self.api.get_city_data()
221
        self.scooter.add_city_to_dict(city)
222
223
        ## get station
224
        zone = self.scooter.get_zone_id()
225
        station = self.api.get_station(zone)
226
        self.scooter.set_station_id(station)
227
228
        ## start a Thread and show a rent menu
229
        self._thread.start()
230
        self.rent_scooter()
231
232
233
    def main(self):
234
        """ Main method """
235
        try:
236
            print("\n************ Welcome to Scooter program **************\n")
237
238
            while True:
239
                scooter = int(input("Enter scooter id: "))
240
                data = self.api.get_scooter_data(scooter)
241
242
                try:
243
                    if self.scooter.check_scooter_status(data):
244
                        current = datetime.now().time().strftime("%H:%M:%S")
245
                        self._start_time = [int(current[:2]), int(current[3:5]), int(current[6:8])]
246
                        break
247
248
                    print("\n\033[1;31m*\033[1;0m Scooter is not available.\n")
249
                except TypeError:
250
                    print("\nScooter does not exist.\n")
251
252
            self.start()
253
        except ValueError:
254
            print("\nScooter id must be a number.")
255
256
257
if __name__ == "__main__":
258
    Handler().main()
259