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

src.scooter.Scooter.get_zone_id()   A

Complexity

Conditions 4

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 10
nop 1
dl 0
loc 17
ccs 10
cts 10
cp 1
crap 4
rs 9.9
c 0
b 0
f 0
1
#!/usr/bin/python3
2
3 1
"""
4
File for handling scooter data.
5
6
Scooter's status id:
7
1- Available
8
2- Unavailable
9
3- Maintenance
10
4- Charging
11
7- Running
12
13
scooter's data
14
data = {
15
    "id": "1",              # scooter id (str)
16
    "lat": 59.174586,       # coordinates (float)
17
    "lon": 17.602334,       # coordinates   (float)
18
    "speed": 0,             # km/h  (int)
19
    "battery": 0,           # % (int)
20
    "status": "7"           # status id (str)
21
    "station": "1"          # station id (str)
22
}
23
24
city's data
25
city = {
26
    "id": "2",          # citys id (str)
27
    "area": 25.84,      # km²   (float)
28
    "lat": 59.19554,    # coordinates (float)
29
    "lon": 17.62525     # coordinates (float)
30
}
31
"""
32
33 1
import math
34 1
import random
35 1
import re
36 1
from geopy.distance import geodesic, distance
37
38
39
40 1
class Scooter():
41
    """ Scooter class """
42
43
    ## scooter's data
44 1
    data = {}
45
46
    ## city's data
47 1
    city = {}
48
49
    ## scooter's new coordinates
50 1
    location = ""
51
52
    ## station name
53 1
    station = ""
54
55 1
    def __init__(self) -> None:
56
        """ Initialize class """
57
58
59 1
    def __str__(self) -> str:
60
        """ Returns scooters data """
61 1
        return "Scooter id: {0}\nLocation: {1}, {2}\nSpeed: {3}km/h\nBattery: {4}%".format(
62
            self.data["id"],
63
            self.data["lat"],
64
            self.data["lon"],
65
            self.data["speed"],
66
            self.data["battery"],
67
        )
68
69
70 1
    def check_scooter_status(self, scooter: dict) -> bool:
71
        """
72
        Returns true if the scooter is available and adds the scooter's
73
        data to data dictionary. Status id 7 means 'Running'.
74
        """
75 1
        if scooter["status"]["status"] == "Available":
76 1
            self.add_scooter_to_dict(scooter)
77 1
            return True
78 1
        return False
79
80
81 1
    def add_scooter_to_dict(self, scooter: dict) -> None:
82
        """ Adds scooter's data to the dictionary. Status id 7 means 'Running'. """
83 1
        self.data["id"] = scooter["id"]
84 1
        self.data["lat"] = float(scooter['latitude'])
85 1
        self.data["lon"] = float(scooter['longitude'])
86 1
        self.data["speed"] = int(scooter['speed'])
87 1
        self.data["battery"] = int(scooter['battery'])
88 1
        self.data["status"] =  "7"
89
90 1
        self.station = scooter["station"]["station_name"]
91
92
93 1
    def add_city_to_dict(self, city: dict):
94
        """ Adds scooter's data to the dictionary. Status id 7 means 'Running'. """
95 1
        self.city["id"] = city["id"]
96 1
        self.city["lat"] = float(city['latitude'])
97 1
        self.city["lon"] = float(city['longitude'])
98 1
        self.city["area"] = float(city['area'])
99
100
101 1
    def get_zone_id(self) -> str:
102
        """
103
        Returns zone id depending on the station's name type.
104
        id => 1- Charging, 2- Parking, 3-Bike, 4- Maintenance zones
105
        """
106 1
        result = ""
107
108 1
        if "Charging" in self.station:
109 1
            result = "1"
110 1
        elif "Parking" in self.station:
111 1
            result = "2"
112 1
        elif "Bike" in self.station:
113 1
            result = "3"
114
        else:
115 1
            result = "4"
116
117 1
        return result
118
119
120 1
    def set_station_id(self, station: dict) -> None:
121
        """ Add stations id to scooters data dictionary. """
122 1
        self.data["station"] = station["id"]
123
124
125
126 1
    def move_scooter(self) -> None:
127
        """
128
        Move the scooter from one position to another and reduce battery level.
129
        Max scooter speed is 20km/h.
130
        """
131
        ## get random speed
132 1
        speed = random.randrange(1, 21)
133 1
        points = re.split("Point|, ", self.location)
134
135 1
        self.data["lat"] = float(points[1][1:])
136 1
        self.data["lon"] = float(points[2])
137 1
        self.data["speed"] = speed
138 1
        self.data["battery"] -= 1
139
140
141 1
    def change_location(self) -> None:
142
        """
143
        Get random location inside the city zone.
144
        Speed = distance ÷ time => distance = speed * time
145
        Bearing in degrees: North: 0, East: 90, South: 180, West: 270.
146
        """
147
        ## 5 seconds is sleep time, scooter moves every 5 seconds
148
        ## but for better simulation I increase it to 15 seconds
149 1
        distance_km = self.data["speed"] * (15 / 3600)
150
151
        ## get random position
152 1
        bearing = random.randint(0, 3)
153 1
        degrees = [0, 90, 180, 270]
154
155 1
        new_location = repr(distance(kilometers = distance_km).destination(
156
            (self.data["lat"], self.data["lon"]),
157
            bearing = degrees[bearing])
158
        )
159
160 1
        self.location = new_location
161
162
163 1
    def stop_scooter(self, status = "7") -> None:
164
        """ Stop the scooter from running. Change status and speed. """
165 1
        self.data["status"] = status
166 1
        self.data["speed"] = 0
167
168
169 1
    def check_battery(self) -> bool:
170
        """ Returns True if the battery level < 20%. """
171 1
        return self.data["battery"] < 20
172
173
174 1
    def move_to_station(self, station: dict) -> None:
175
        """ Move the scooter to charging/maintenance station. """
176 1
        self.data["lat"] = float(station["latitude"])
177 1
        self.data["lon"] = float(station["longitude"])
178 1
        self.data["station"] = station["id"]
179
180 1
    @staticmethod
181 1
    def check_maintenance() -> bool:
182
        """
183
        Returns true if the random number is 1 otherwise False, since scooters are not
184
        real, the maintenance check will be randomly.
185
        The probability that the scooter receives maintenance is 10%.
186
        """
187 1
        probability = random.randint(1, 10)
188 1
        return probability == 1
189
190
191 1
    def check_scooter_in_city(self) -> bool:
192
        """
193
        Check if scooter is inside the city zone. If the distance between
194
        two points 'city center and scooter' <= circle radius return True.
195
        """
196
        ## Circle Area = pi * r^2 => r^2 = A/pi
197 1
        radius = math.sqrt((self.city["area"] / math.pi))
198 1
        scooter = (self.data["lat"], self.data["lon"])
199 1
        city = (self.city["lat"], self.city["lon"])
200
201 1
        calculate = geodesic(scooter, city).km
202
203 1
        if calculate <= radius:
204 1
            return True
205
        return False
206