mirror of
https://github.com/mamedev/mame.git
synced 2024-11-16 07:48:32 +01:00
minimaws updates:
* reduce network operations when initially loading a machine page * add rudimentary software lists to machine pages
This commit is contained in:
parent
f9d27d2104
commit
c10ef269c5
3 changed files with 185 additions and 54 deletions
|
@ -771,6 +771,14 @@ class QueryCursor(object):
|
|||
'ORDER BY ramoption.size',
|
||||
(machine, ))
|
||||
|
||||
def get_machine_softwarelists(self, machine):
|
||||
return self.dbcurs.execute(
|
||||
'SELECT machinesoftwarelist.tag AS tag, machinesoftwareliststatustype.value AS status, softwarelist.shortname AS shortname, softwarelist.description AS description, COUNT(software.id) AS total, COUNT(CASE software.supported WHEN 0 THEN 1 ELSE NULL END) AS supported, COUNT(CASE software.supported WHEN 1 THEN 1 ELSE NULL END) AS partiallysupported, COUNT(CASE software.supported WHEN 2 THEN 1 ELSE NULL END) AS unsupported ' \
|
||||
'FROM machinesoftwarelist LEFT JOIN machinesoftwareliststatustype ON machinesoftwarelist.status = machinesoftwareliststatustype.id LEFT JOIN softwarelist ON machinesoftwarelist.softwarelist = softwarelist.id LEFT JOIN software ON softwarelist.id = software.softwarelist ' \
|
||||
'WHERE machinesoftwarelist.machine = ? ' \
|
||||
'GROUP BY machinesoftwarelist.id',
|
||||
(machine, ))
|
||||
|
||||
def get_softwarelist_id(self, shortname):
|
||||
return (self.dbcurs.execute('SELECT id FROM softwarelist WHERE shortname = ?', (shortname, )).fetchone() or (None, ))[0]
|
||||
|
||||
|
@ -1075,6 +1083,8 @@ class UpdateConnection(object):
|
|||
def prepare_for_load(self):
|
||||
# here be dragons - this is a poor man's DROP ALL TABLES etc.
|
||||
self.dbconn.execute('PRAGMA foreign_keys = OFF')
|
||||
for query in self.dbconn.execute('SELECT \'DROP VIEW \' || name FROM sqlite_master WHERE type = \'view\'').fetchall():
|
||||
self.dbconn.execute(query[0])
|
||||
for query in self.dbconn.execute('SELECT \'DROP INDEX \' || name FROM sqlite_master WHERE type = \'index\' AND NOT name GLOB \'sqlite_autoindex_*\'').fetchall():
|
||||
self.dbconn.execute(query[0])
|
||||
for query in self.dbconn.execute('SELECT \'DROP TABLE \' || name FROM sqlite_master WHERE type = \'table\'').fetchall():
|
||||
|
|
|
@ -70,6 +70,45 @@ MACHINE_CLONES_ROW = string.Template(
|
|||
' <td>${manufacturer}</td>\n' \
|
||||
' </tr>\n')
|
||||
|
||||
MACHINE_SOFTWARELISTS_TABLE_PROLOGUE = string.Template(
|
||||
'<h2 id="heading-softwarelists">Software Lists</h2>\n' \
|
||||
'<table id="tbl-softwarelists">\n' \
|
||||
' <thead>\n' \
|
||||
' <tr>\n' \
|
||||
' <th>Short name</th>\n' \
|
||||
' <th>Description</th>\n' \
|
||||
' <th>Status</th>\n' \
|
||||
' <th class="numeric">Total</th>\n' \
|
||||
' <th class="numeric">Supported</th>\n' \
|
||||
' <th class="numeric">Partially supported</th>\n' \
|
||||
' <th class="numeric">Unsupported</th>\n' \
|
||||
' </tr>\n' \
|
||||
' </thead>\n' \
|
||||
' <tbody>\n')
|
||||
|
||||
MACHINE_SOFTWARELISTS_TABLE_ROW = string.Template(
|
||||
' <tr>\n' \
|
||||
' <td><a href="${href}">${shortname}</a></td>\n' \
|
||||
' <td><a href="${href}">${description}</a></td>\n' \
|
||||
' <td>${status}</td>\n' \
|
||||
' <td style="text-align: right">${total}</td>\n' \
|
||||
' <td style="text-align: right">${supported}</td>\n' \
|
||||
' <td style="text-align: right">${partiallysupported}</td>\n' \
|
||||
' <td style="text-align: right">${unsupported}</td>\n' \
|
||||
' </tr>\n')
|
||||
|
||||
MACHINE_SOFTWARELISTS_TABLE_EPILOGUE = string.Template(
|
||||
' </tbody>\n' \
|
||||
'</table>\n' \
|
||||
'<script>\n' \
|
||||
' make_table_sortable(document.getElementById("tbl-softwarelists"));\n' \
|
||||
' if (!document.getElementById("tbl-softwarelists").tBodies[0].rows.length)\n' \
|
||||
' {\n' \
|
||||
' document.getElementById("heading-softwarelists").style.display = "none";\n' \
|
||||
' document.getElementById("tbl-softwarelists").style.display = "none";\n' \
|
||||
' }\n' \
|
||||
'</script>\n')
|
||||
|
||||
MACHINE_OPTIONS_HEADING = string.Template(
|
||||
'<h2>Options</h2>\n' \
|
||||
'<p>\n' \
|
||||
|
@ -92,10 +131,14 @@ MACHINE_RAM_PROLOGUE = string.Template(
|
|||
MACHINE_RAM_OPTION = string.Template(
|
||||
' <option value="${name}" data-isdefault="${isdefault}">${name} (${size})</option>\n')
|
||||
|
||||
MACHINE_SLOTS_PLACEHOLDER = string.Template(
|
||||
MACHINE_SLOTS_PLACEHOLDER_PROLOGUE = string.Template(
|
||||
'<h3>Slots</h3>\n' \
|
||||
'<p id="para-slots-placeholder">Loading slot information…<p>\n' \
|
||||
'<script>fetch_slots("${machine}");</script>\n')
|
||||
'<script>\n')
|
||||
|
||||
MACHINE_SLOTS_PLACEHOLDER_EPILOGUE = string.Template(
|
||||
' populate_slots(${machine});\n'
|
||||
'</script>\n')
|
||||
|
||||
MACHINE_ROW = string.Template(
|
||||
' <tr>\n' \
|
||||
|
|
|
@ -109,6 +109,76 @@ class QueryPageHandler(HandlerBase):
|
|||
def software_href(self, softwarelist, software):
|
||||
return cgi.escape(urlparse.urljoin(self.application_uri, 'softwarelist/%s/%s' % (urlquote(softwarelist), urlquote(software))), True)
|
||||
|
||||
def bios_data(self, machine):
|
||||
result = { }
|
||||
for name, description, isdefault in self.dbcurs.get_biossets(machine):
|
||||
result[name] = { 'description': description, 'isdefault': True if isdefault else False }
|
||||
return result
|
||||
|
||||
def flags_data(self, machine):
|
||||
result = { 'features': { } }
|
||||
for feature, status, overall in self.dbcurs.get_feature_flags(machine):
|
||||
detail = { }
|
||||
if status == 1:
|
||||
detail['status'] = 'imperfect'
|
||||
elif status > 1:
|
||||
detail['status'] = 'unemulated'
|
||||
if overall == 1:
|
||||
detail['overall'] = 'imperfect'
|
||||
elif overall > 1:
|
||||
detail['overall'] = 'unemulated'
|
||||
result['features'][feature] = detail
|
||||
return result
|
||||
|
||||
def slot_data(self, machine):
|
||||
result = { 'defaults': { }, 'slots': { } }
|
||||
|
||||
# get defaults and slot options
|
||||
for slot, default in self.dbcurs.get_slot_defaults(machine):
|
||||
result['defaults'][slot] = default
|
||||
prev = None
|
||||
for slot, option, shortname, description in self.dbcurs.get_slot_options(machine):
|
||||
if slot != prev:
|
||||
if slot in result['slots']:
|
||||
options = result['slots'][slot]
|
||||
else:
|
||||
options = { }
|
||||
result['slots'][slot] = options
|
||||
prev = slot
|
||||
options[option] = { 'device': shortname, 'description': description }
|
||||
|
||||
# remove slots that come from default cards in other slots
|
||||
for slot in tuple(result['slots'].keys()):
|
||||
slot += ':'
|
||||
for candidate in tuple(result['slots'].keys()):
|
||||
if candidate.startswith(slot):
|
||||
del result['slots'][candidate]
|
||||
|
||||
return result
|
||||
|
||||
def softwarelist_data(self, machine):
|
||||
result = { }
|
||||
|
||||
# get software lists referenced by machine
|
||||
for softwarelist in self.dbcurs.get_machine_softwarelists(machine):
|
||||
result[softwarelist['tag']] = {
|
||||
'status': softwarelist['status'],
|
||||
'shortname': softwarelist['shortname'],
|
||||
'description': softwarelist['description'],
|
||||
'total': softwarelist['total'],
|
||||
'supported': softwarelist['supported'],
|
||||
'partiallysupported': softwarelist['partiallysupported'],
|
||||
'unsupported': softwarelist['unsupported'] }
|
||||
|
||||
# remove software lists that come from default cards in slots
|
||||
for slot, default in self.dbcurs.get_slot_defaults(machine):
|
||||
slot += ':'
|
||||
for candidate in tuple(result.keys()):
|
||||
if candidate.startswith(slot):
|
||||
del result[candidate]
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class MachineRpcHandlerBase(QueryPageHandler):
|
||||
def __init__(self, app, application_uri, environ, start_response, **kwargs):
|
||||
|
@ -215,6 +285,7 @@ class MachineHandler(QueryPageHandler):
|
|||
tuple(imperfect)).encode('utf-8');
|
||||
yield '</table>\n'.encode('utf-8')
|
||||
|
||||
# make a table of clones
|
||||
first = True
|
||||
for clone, clonedescription, cloneyear, clonemanufacturer in self.dbcurs.get_clones(self.shortname):
|
||||
if first:
|
||||
|
@ -229,6 +300,21 @@ class MachineHandler(QueryPageHandler):
|
|||
if not first:
|
||||
yield htmltmpl.SORTABLE_TABLE_EPILOGUE.substitute(id='tbl-clones').encode('utf-8')
|
||||
|
||||
# make a table of software lists
|
||||
yield htmltmpl.MACHINE_SOFTWARELISTS_TABLE_PROLOGUE.substitute().encode('utf-8')
|
||||
for softwarelist in self.dbcurs.get_machine_softwarelists(id):
|
||||
total = softwarelist['total']
|
||||
yield htmltmpl.MACHINE_SOFTWARELISTS_TABLE_ROW.substitute(
|
||||
href=self.softwarelist_href(softwarelist['shortname']),
|
||||
shortname=cgi.escape(softwarelist['shortname']),
|
||||
description=cgi.escape(softwarelist['description']),
|
||||
status=cgi.escape(softwarelist['status']),
|
||||
total=cgi.escape('%d' % (total, )),
|
||||
supported=cgi.escape('%.1f%%' % (softwarelist['supported'] * 100.0 / (total or 1), )),
|
||||
partiallysupported=cgi.escape('%.1f%%' % (softwarelist['partiallysupported'] * 100.0 / (total or 1), )),
|
||||
unsupported=cgi.escape('%.1f%%' % (softwarelist['unsupported'] * 100.0 / (total or 1), ))).encode('utf-8')
|
||||
yield htmltmpl.MACHINE_SOFTWARELISTS_TABLE_EPILOGUE.substitute().encode('utf-8')
|
||||
|
||||
# allow system BIOS selection
|
||||
haveoptions = False
|
||||
for name, desc, isdef in self.dbcurs.get_biossets(id):
|
||||
|
@ -264,8 +350,28 @@ class MachineHandler(QueryPageHandler):
|
|||
if not haveoptions:
|
||||
haveoptions = True
|
||||
yield htmltmpl.MACHINE_OPTIONS_HEADING.substitute().encode('utf-8')
|
||||
yield htmltmpl.MACHINE_SLOTS_PLACEHOLDER.substitute(
|
||||
machine=self.js_escape(self.shortname)).encode('utf=8')
|
||||
yield htmltmpl.MACHINE_SLOTS_PLACEHOLDER_PROLOGUE.substitute().encode('utf=8')
|
||||
pending = set((self.shortname, ))
|
||||
added = set((self.shortname, ))
|
||||
haveextra = set()
|
||||
while pending:
|
||||
requested = pending.pop()
|
||||
slots = self.slot_data(self.dbcurs.get_machine_id(requested))
|
||||
yield (' slot_info[%s] = %s;\n' % (self.sanitised_json(requested), self.sanitised_json(slots))).encode('utf-8')
|
||||
for slotname, slot in slots['slots'].items():
|
||||
for choice, card in slot.items():
|
||||
carddev = card['device']
|
||||
if carddev not in added:
|
||||
pending.add(carddev)
|
||||
added.add(carddev)
|
||||
if (carddev not in haveextra) and (slots['defaults'].get(slotname) == choice):
|
||||
haveextra.add(carddev)
|
||||
cardid = self.dbcurs.get_machine_id(carddev)
|
||||
carddev = self.sanitised_json(carddev)
|
||||
yield (' bios_sets[%s] = %s;\n' % (carddev, self.sanitised_json(self.bios_data(cardid)))).encode('utf-8')
|
||||
yield (' machine_flags[%s] = %s;\n' % (carddev, self.sanitised_json(self.flags_data(cardid)))).encode('utf-8')
|
||||
yield htmltmpl.MACHINE_SLOTS_PLACEHOLDER_EPILOGUE.substitute(
|
||||
machine=self.sanitised_json(self.shortname)).encode('utf=8')
|
||||
|
||||
# list devices referenced by this system/device
|
||||
first = True
|
||||
|
@ -332,6 +438,10 @@ class MachineHandler(QueryPageHandler):
|
|||
description=cgi.escape(description or ''),
|
||||
sourcefile=cgi.escape(sourcefile or '')).encode('utf-8')
|
||||
|
||||
@staticmethod
|
||||
def sanitised_json(data):
|
||||
return json.dumps(data).replace('<', '\\u003c').replace('>', '\\u003e')
|
||||
|
||||
|
||||
class SourceFileHandler(QueryPageHandler):
|
||||
def __init__(self, app, application_uri, environ, start_response, **kwargs):
|
||||
|
@ -531,7 +641,13 @@ class SoftwareListHandler(QueryPageHandler):
|
|||
if first:
|
||||
yield htmltmpl.SOFTWARELIST_MACHINE_TABLE_HEADER.substitute().encode('utf-8')
|
||||
first = False
|
||||
yield self.machine_row(machine_info)
|
||||
yield htmltmpl.SOFTWARELIST_MACHINE_TABLE_ROW.substitute(
|
||||
machinehref=self.machine_href(machine_info['shortname']),
|
||||
shortname=cgi.escape(machine_info['shortname']),
|
||||
description=cgi.escape(machine_info['description']),
|
||||
year=cgi.escape(machine_info['year'] or ''),
|
||||
manufacturer=cgi.escape(machine_info['manufacturer'] or ''),
|
||||
status=cgi.escape(machine_info['status'])).encode('utf-8')
|
||||
if not first:
|
||||
yield htmltmpl.SORTABLE_TABLE_EPILOGUE.substitute(id='tbl-machines').encode('utf-8')
|
||||
|
||||
|
@ -613,15 +729,6 @@ class SoftwareListHandler(QueryPageHandler):
|
|||
publisher=cgi.escape(clone_info['publisher']),
|
||||
supported=self.format_supported(clone_info['supported'])).encode('utf-8')
|
||||
|
||||
def machine_row(self, machine_info):
|
||||
return htmltmpl.SOFTWARELIST_MACHINE_TABLE_ROW.substitute(
|
||||
machinehref=self.machine_href(machine_info['shortname']),
|
||||
shortname=cgi.escape(machine_info['shortname']),
|
||||
description=cgi.escape(machine_info['description']),
|
||||
year=cgi.escape(machine_info['year'] or ''),
|
||||
manufacturer=cgi.escape(machine_info['manufacturer'] or ''),
|
||||
status=cgi.escape(machine_info['status'])).encode('utf-8')
|
||||
|
||||
@staticmethod
|
||||
def format_supported(supported):
|
||||
return 'Yes' if supported == 0 else 'Partial' if supported == 1 else 'No'
|
||||
|
@ -659,47 +766,17 @@ class BiosRpcHandler(MachineRpcHandlerBase):
|
|||
|
||||
class FlagsRpcHandler(MachineRpcHandlerBase):
|
||||
def data_page(self, machine):
|
||||
result = { 'features': { } }
|
||||
for feature, status, overall in self.dbcurs.get_feature_flags(machine):
|
||||
detail = { }
|
||||
if status == 1:
|
||||
detail['status'] = 'imperfect'
|
||||
elif status > 1:
|
||||
detail['status'] = 'unemulated'
|
||||
if overall == 1:
|
||||
detail['overall'] = 'imperfect'
|
||||
elif overall > 1:
|
||||
detail['overall'] = 'unemulated'
|
||||
result['features'][feature] = detail
|
||||
yield json.dumps(result).encode('utf-8')
|
||||
yield json.dumps(self.flags_data(machine)).encode('utf-8')
|
||||
|
||||
|
||||
class SlotsRpcHandler(MachineRpcHandlerBase):
|
||||
def data_page(self, machine):
|
||||
result = { 'defaults': { }, 'slots': { } }
|
||||
yield json.dumps(self.slot_data(machine)).encode('utf-8')
|
||||
|
||||
# get defaults and slot options
|
||||
for slot, default in self.dbcurs.get_slot_defaults(machine):
|
||||
result['defaults'][slot] = default
|
||||
prev = None
|
||||
for slot, option, shortname, description in self.dbcurs.get_slot_options(machine):
|
||||
if slot != prev:
|
||||
if slot in result['slots']:
|
||||
options = result['slots'][slot]
|
||||
else:
|
||||
options = { }
|
||||
result['slots'][slot] = options
|
||||
prev = slot
|
||||
options[option] = { 'device': shortname, 'description': description }
|
||||
|
||||
# remove slots that come from default cards in other slots
|
||||
for slot in tuple(result['slots'].keys()):
|
||||
slot += ':'
|
||||
for candidate in tuple(result['slots'].keys()):
|
||||
if candidate.startswith(slot):
|
||||
del result['slots'][candidate]
|
||||
|
||||
yield json.dumps(result).encode('utf-8')
|
||||
class SoftwareListsRpcHandler(MachineRpcHandlerBase):
|
||||
def data_page(self, machine):
|
||||
yield json.dumps(self.softwarelist_data(machine)).encode('utf-8')
|
||||
|
||||
|
||||
class RomDumpsRpcHandler(QueryPageHandler):
|
||||
|
@ -817,11 +894,12 @@ class DiskDumpsRpcHandler(QueryPageHandler):
|
|||
class MiniMawsApp(object):
|
||||
JS_ESCAPE = re.compile('([\"\'\\\\])')
|
||||
RPC_SERVICES = {
|
||||
'bios': BiosRpcHandler,
|
||||
'flags': FlagsRpcHandler,
|
||||
'slots': SlotsRpcHandler,
|
||||
'romdumps': RomDumpsRpcHandler,
|
||||
'diskdumps': DiskDumpsRpcHandler }
|
||||
'bios': BiosRpcHandler,
|
||||
'flags': FlagsRpcHandler,
|
||||
'slots': SlotsRpcHandler,
|
||||
'softwarelists': SoftwareListsRpcHandler,
|
||||
'romdumps': RomDumpsRpcHandler,
|
||||
'diskdumps': DiskDumpsRpcHandler }
|
||||
|
||||
def __init__(self, dbfile, **kwargs):
|
||||
super(MiniMawsApp, self).__init__(**kwargs)
|
||||
|
|
Loading…
Reference in a new issue