forked from kingsawyer/python_lift_api
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboxlift_api.py
147 lines (127 loc) · 5.35 KB
/
boxlift_api.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import json
# This is to keep this imports to a minimum and work on Python 2.x and 3.x
try:
import urllib.request as urllib2
except ImportError:
import urllib2
PYCON2015_EVENT_NAME = 'pycon2015'
class Command(object):
def __init__(self, id, direction, speed):
"""A command to an elevator
:param id:
The elevator id. This is returned in the state where all elevators are identified.
It is typically '0' for the first elevator, '1' for the next, etc.
:type id:
`basestring`
:param direction:
1 to indicate up, -1 to indicate down. Even when stationary an elevator has a direction.
Passengers wishing to go up will not board an elevator with a down indicator.
:type direction:
`int`
:param speed:
0 to halt on a floor, 1 to move. Sorry, no greater speeds allowed!
:type speed:
`int`
"""
self.id = id
self.direction = direction
self.speed = speed
def __str__(self):
return "set car {} (direction={}, speed={})".format(self.id, self.direction, self.speed)
class BoxLift(object):
HOST = 'http://codelift.org'
def __init__(self, bot_name, plan, email, registration_id, event_name='', sandbox_mode=False):
"""An object that provides an interface to the Lift System.
:param bot_name:
The name for your entry, such as "Mega Elevator 3000"
:type bot_name:
`basestring`
:param plan:
The plan to run, such as "training_1"
:type plan:
`basestring`
:param email:
A contact email. This helps us contact you if there are problems or to inform you if you
have one of the top 10 finishers. Prizes will be distributed in the Expo Hall on Sunday
breakfast and lunch.
:type email:
`basestring`
:param registration_id:
A unique id for the event. Required for prize pickup. e.g. your Pycon 2015 registration number
:type registration_id:
`basestring`
:param event_name:
The name of the event being run. e.g. "pycon2015"
:type event_name:
`basestring`
:param sandbox_mode:
set to True to opt out of leader board, recent runs, and competition. Useful for testing and for event
organizers who want to play, but don't want to be on the leader board. Tokens will not expire, allowing
you to step through code slowly.
:type sandbox_mode:
`bool`
"""
self.email = email
initialization_data = {
'username': bot_name,
'email': email,
'plan': plan,
'event_name' : event_name,
'event_id': registration_id,
'sandbox': sandbox_mode,
}
state = self._get_world_state(initialization_data)
self.game_id = state['id']
self.token = state['token']
self.status = state['status']
self.building_url = state['building']
# fix building_url
self.building_url = self.url_root() + "/" + self.game_id
self.visualization_url = state['visualization']
print(state['message'])
print('building url: {}'.format(self.building_url))
print('visualization url: {}'.format(self.visualization_url))
def _get_world_state(self, initialization_data):
"""Initialize and gets the state of the world without sending any commands to advance the clock"""
return self._post(self.url_root(), initialization_data)
def send_commands(self, commands=None):
"""Send commands to advance the clock. Returns the new state of the world.
:param commands:
list of commands to elevators. If no commands are sent the clock does not advance.
:type commands:
`iterable` of `Command`
:return:
The new state of the world
:rtype:
`dict`
"""
commands = commands or []
command_list = {}
for command in commands:
command_list[command.id] = {'speed': command.speed, 'direction': command.direction}
data = {'token': self.token, 'commands': command_list}
state = self._post(self.building_url, data)
print("status: {}".format(state['status']))
if 'token' in state:
self.token = state['token']
if 'requests' not in state:
state['requests'] = []
for elevator_data in state.get('elevators', []):
if 'buttons_pressed' not in elevator_data:
elevator_data['buttons_pressed'] = []
return state
@classmethod
def url_root(cls):
return cls.HOST + "/v1/buildings"
def _post(self, url, data):
"""wrapper to encode/decode our data and the return value"""
req = urllib2.Request(url, json.dumps(data).encode('utf-8'))
response = urllib2.urlopen(req)
res = response.read()
if isinstance(res, bytes):
res = res.decode('utf-8')
try:
return json.loads(res)
except ValueError:
# probably empty response so no JSON to decode
return res