Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
203 changes: 198 additions & 5 deletions pid_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import json
import memcache_keys
import common
import itertools
from model import Manufacturer, Pid, Responder, SUBDEVICE_RANGE_DICT
from utils import StringToInt
from google.appengine.api import memcache
Expand Down Expand Up @@ -112,11 +113,7 @@ def GetResults(self):
return []


class DisplayPid(common.BasePageHandler):
"""Display information about a particular PID."""

TEMPLATE = 'templates/display_pid.tmpl'

class PidRoute(webapp.RequestHandler):
def LookupPIDFromRequest(self):
pid_id = self.request.get('pid')
try:
Expand All @@ -138,6 +135,12 @@ def LookupPIDFromRequest(self):
else:
return None


class DisplayPid(common.BasePageHandler, PidRoute):
"""Display information about a particular PID."""

TEMPLATE = 'templates/display_pid.tmpl'

def CopyToDict(self, input_d, output_d, keys):
for key in keys:
if key in input_d:
Expand Down Expand Up @@ -240,12 +243,202 @@ def GetTemplateData(self):
output['set_command'] = command
return output

class DisplayPidJSON(PidRoute):
"""Fetch E1.37-5 format JSON about a PID."""

CUSTOM_CAPITALIZE_TESTS = [
("dmx_start_address", "DMX Start Address"),
("foo-dmx", "Foo DMX"),
("mini_dmxter_device", "Mini Dmxter Device"),
("this-is_a_test", "This Is A Test"),
("ip_address", "IP Address"),
("controller_ip_address", "Controller IP Address"),
("dazzled_led_type", "Dazzled LED Type"),
("device_rdm_uid", "Device RDM UID"),
("dns_via_ipv4_dhcp", "DNS Via IPv4 DHCP"),
]

@staticmethod
def CustomCapitalizeLabel(label):
TRANSFORMS = {
'asc',
'dhcp',
'dmx',
'dns',
'ip',
'json',
'led',
'nsc',
'pdl',
'pid',
'rdm',
'uid',
'url',
}
TRANSFORMS = {x:x.upper() for x in TRANSFORMS}
TRANSFORMS['ipv4'] = 'IPv4'
TRANSFORMS['ipv6'] = 'IPv6'
TRANSFORMS['mdmx'] = 'mDMX'

label = label.replace('-', ' ').replace('_', ' ').strip()
if len(label) == 0: return ''

end = []
for part in label.split(' '):
if TRANSFORMS.get(part.lower()):
end.append(TRANSFORMS[part.lower()])
elif len(part) > 0:
end.append(part[0].upper() + part[1:])
else:
end.append('')

return ' '.join(end)

@staticmethod
def ConvertItems(items):
out = []
for item in items:
a = {'name':item['name'], 'displayName':DisplayPidJSON.CustomCapitalizeLabel(item['name'])}
if item['type'] == 'bool':
a['type'] = 'boolean'
out.append(a)
elif item['type'] in ['uint8', 'uint16', 'uint32', 'uint64', 'int8', 'int16', 'int32', 'int64']:
a['type'] = item['type']
if item.get('multiplier'):
a['prefixPower'] = item['multiplier']
if item.get('range'):
a['ranges'] = [{'minimum':r[0], 'maximum':r[1]} for r in item['range']]
if item.get('labels'):
a['labels'] = [{'value':l[0], 'name':l[1]} for l in item['labels']]
if not item.get('ranges'):
a['restrictToLabeled'] = True
out.append(a)
elif item['type'] == 'string':
a['type'] = item['type']
if item.get('max_size'): a['maxBytes'] = item['max_size']
if item.get('min_size'): a['minBytes'] = item['min_size']
# could restrictToASCII here??
out.append(a)
elif item['type'] == 'ipv4':
a['type'] = 'bytes'
a['format'] = 'ipv4'
out.append(a)
elif item['type'] == 'ipv6':
a['type'] = 'bytes'
a['format'] = 'ipv6'
out.append(a)
elif item['type'] == 'mac':
a['type'] = 'bytes'
a['format'] = 'mac-address'
out.append(a)
elif item['type'] == 'uid':
a['type'] = 'bytes'
a['format'] = 'uid'
out.append(a)
elif item['type'] == 'group':
a['type'] = 'list'
if item.get('max_size'): a['minItems'] = item['max_size']
if item.get('min_size'): a['maxItems'] = item['min_size']
converted = DisplayPidJSON.ConvertItems(item['items'])
if len(converted) == 1:
a['itemType'] = converted[0]
else:
a['itemType'] = {
'type': 'compound',
'subtypes': converted
}
out.append(a)
else:
# WARNING: invalid item type??
pass
return out

@staticmethod
def SubDeviceRange(subs):
if subs == 0:
return ['root']
elif subs == 1:
return ['root', 'subdevices', 'broadcast']
elif subs == 2:
return ['root', 'subdevices']
elif subs == 3:
return ['subdevices']
else: # should never happen
return []

@staticmethod
def BuildCommand(output, command, prefix):
request = eval(command.request)
response = eval(command.response)
output[prefix+'_request'] = DisplayPidJSON.ConvertItems(request['items'])
output[prefix+'_response'] = DisplayPidJSON.ConvertItems(response['items'])
output[prefix+'_request_subdevice_range'] = DisplayPidJSON.SubDeviceRange(command.sub_device_range)
# response subdevices are always 'match' which is the default

@staticmethod
def CollapseMessages(output, message_a, message_b):
if isinstance(output.get(message_a), list) and isinstance(output.get(message_b), list):
if output[message_a] == output[message_b] and len(output[message_a]) != 0:
output[message_b] = message_a

@staticmethod
def ConvertPid(pid):
output = {
'name': pid.name,
'manufacturer_id': pid.manufacturer.esta_id,
'pid': pid.pid_id,
'version': 0,
}
if pid.notes:
output['notes'] = pid.notes
if pid.link:
output['resources'] = [pid.link]

# add get information
if pid.get_command:
DisplayPidJSON.BuildCommand(output, pid.get_command, 'get')
if pid.set_command:
DisplayPidJSON.BuildCommand(output, pid.set_command, 'set')

# collapse repeated commands
message_names = ['get_request', 'get_response', 'set_request', 'set_response']
for (a, b) in itertools.combinations(message_names, 2):
DisplayPidJSON.CollapseMessages(output, a, b)

# collapse repeated properties
# we get a list of them all
# and because python objects are references,
# we can just update this list in-place
prop_list = []
for m in message_names:
if output.get(m) and isinstance(output[m], list):
prop_list += [('#/'+m+'/'+str(i), prop) for (i, prop) in enumerate(output[m])]
for i in xrange(1, len(prop_list)):
for j in xrange(0, i):
if prop_list[i][1] == prop_list[j][1]:
prop_list[i][1].clear()
prop_list[i][1]['$ref'] = prop_list[j][0]

return output

def get(self):
pid = self.LookupPIDFromRequest()
if not pid:
self.error(404)
return

converted = DisplayPidJSON.ConvertPid(pid)

# use the Content-Type recommended in E1.37-5
self.response.headers['Content-Type'] = 'application/schema-instance+json'
self.response.out.write(json.dumps(converted, indent=2))

pid_application = webapp.WSGIApplication(
[
('/pid/manufacturer', SearchByManufacturer),
('/pid/name', SearchByName),
('/pid/id', SearchById),
('/pid/display', DisplayPid),
('/pid/display.json', DisplayPidJSON),
],
debug=True)