Passed
Push — main ( d517ed...ce71f4 )
by Rahn20
03:21
created

src.scooter.Scooter.__str__()   A

Complexity

Conditions 1

Size

Total Lines 8
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 7
nop 1
dl 0
loc 8
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
#!/usr/bin/python3
2
3 1
"""
4
File for handling scooter data with Scooter class.
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,           # % (Float)
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 1
class Scooter():
39
    """ Scooter class """
40
41
    ## scooter's data
42 1
    data = {}
43
44
    ## city's data
45 1
    city = {}
46
47
    ## scooter's new coordinates
48 1
    location = ""
49
50
51 1
    def __init__(self) -> None:
52
        """ Initialize class """
53
54
55 1
    def __str__(self) -> str:
56
        """ Returns scooters data """
57 1
        return "Scooter id: {0}\nLocation: {1}, {2}\nSpeed: {3}km/h\nBattery: {4}%".format(
58
            self.data["id"],
59
            self.data["lat"],
60
            self.data["lon"],
61
            self.data["speed"],
62
            self.data["battery"],
63
        )
64
65
66 1
    def check_scooter_status(self, scooter: dict) -> bool:
67
        """
68
        Returns true if the scooter is available and adds the scooter's
69
        data to data dictionary. Status id 7 means 'Running' and 1 'Available'
70
        """
71 1
        if scooter["status"]["id"] == "1":
72 1
            self.data["id"] = scooter["id"]
73 1
            self.data["lat"] = float(scooter['latitude'])
74 1
            self.data["lon"] = float(scooter['longitude'])
75 1
            self.data["speed"] = int(scooter['speed'])
76 1
            self.data["battery"] = float(scooter['battery'])
77 1
            self.data["status"] =  "7"
78 1
            self.data["station"] = None
79
80 1
            return True
81 1
        return False
82
83
84 1
    def move_scooter(self) -> None:
85
        """
86
        Move the scooter from one position to another and reduce battery level.
87
        Max scooter speed is 20km/h.
88
        """
89
        ## get random speed
90 1
        speed = random.randrange(1, 21)
91 1
        points = re.split("Point|, ", self.location)
92
93 1
        self.data["lat"] = float(points[1][1:])
94 1
        self.data["lon"] = float(points[2])
95 1
        self.data["speed"] = speed
96 1
        self.data["battery"] -= 1/2
97
98
99 1
    def change_location(self) -> None:
100
        """
101
        Get random location inside the city zone.
102
        Speed = distance ÷ time => distance = speed * time
103
        Bearing in degrees: North: 0, East: 90, South: 180, West: 270.
104
        """
105
        ## 5 seconds is sleep time, scooter moves every 5 seconds
106
        ## but for better simulation I increase it to 15 seconds
107 1
        distance_km = self.data["speed"] * (15 / 3600)
108
109
        ## get random position
110 1
        bearing = random.randint(0, 3)
111 1
        degrees = [0, 90, 180, 270]
112
113 1
        new_location = repr(distance(kilometers = distance_km).destination(
114
            (self.data["lat"], self.data["lon"]),
115
            bearing = degrees[bearing])
116
        )
117
118 1
        self.location = new_location
119
120
121 1
    def stop_scooter(self, status = "7") -> None:
122
        """ Stop the scooter from running. Change status and speed. """
123 1
        self.data["status"] = status
124 1
        self.data["speed"] = 0
125
126
127 1
    def check_battery(self) -> bool:
128
        """ Returns True if the battery level < 20%. """
129 1
        return self.data["battery"] < 20
130
131
132 1
    def move_to_station(self, station: dict) -> None:
133
        """ Move the scooter to charging/maintenance station. """
134 1
        self.data["lat"] = station["latitude"]
135 1
        self.data["lon"] = station["longitude"]
136 1
        self.data["station"] = station["id"]
137
138 1
    @staticmethod
139 1
    def check_maintenance() -> bool:
140
        """
141
        Returns true if the random number is 1 otherwise False, since scooters are not
142
        real, the maintenance check will be randomly.
143
        The probability that the scooter receives maintenance is 10%.
144
        """
145 1
        probability = random.randint(1, 10)
146 1
        return probability == 1
147
148
149 1
    def check_scooter_in_city(self) -> bool:
150
        """
151
        Check if scooter is inside the city zone. If the distance between
152
        two points 'city center and scooter' <= circle radius return True.
153
        """
154
        ## Circle Area = pi * r^2 => r^2 = A/pi
155 1
        radius = math.sqrt((self.city["area"] / math.pi))
156 1
        scooter = (self.data["lat"], self.data["lon"])
157 1
        city = (self.city["lat"], self.city["lon"])
158
159 1
        calculate = geodesic(scooter, city).km
160
161 1
        if calculate <= radius:
162 1
            return True
163
        return False
164