summaryrefslogtreecommitdiffstatshomepage
path: root/scripts/minimaws/lib
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/minimaws/lib')
-rw-r--r--scripts/minimaws/lib/assets/machine.js103
-rw-r--r--scripts/minimaws/lib/assets/romident.js10
-rw-r--r--scripts/minimaws/lib/dbaccess.py10
-rw-r--r--scripts/minimaws/lib/htmltmpl.py49
-rw-r--r--scripts/minimaws/lib/wsgiserve.py188
5 files changed, 304 insertions, 56 deletions
diff --git a/scripts/minimaws/lib/assets/machine.js b/scripts/minimaws/lib/assets/machine.js
index d40dca12be0..b7f600f04d6 100644
--- a/scripts/minimaws/lib/assets/machine.js
+++ b/scripts/minimaws/lib/assets/machine.js
@@ -4,6 +4,7 @@
var slot_info = Object.create(null);
var bios_sets = Object.create(null);
var machine_flags = Object.create(null);
+var softwarelist_info = Object.create(null);
function make_slot_popup_id(name) { return ('select-slot-choice-' + name).replace(/:/g, '-'); }
@@ -197,6 +198,47 @@ var fetch_machine_flags = (function ()
})();
+var fetch_softwarelist_info = (function ()
+ {
+ var pending = Object.create(null);
+ return function (device)
+ {
+ if (!Object.prototype.hasOwnProperty.call(softwarelist_info, device) && !Object.prototype.hasOwnProperty.call(pending, device))
+ {
+ pending[device] = true;
+ var req = new XMLHttpRequest();
+ req.open('GET', appurl + 'rpc/softwarelists/' + encodeURIComponent(device), true);
+ req.responseType = 'json';
+ req.onload =
+ function ()
+ {
+ delete pending[device];
+ if (req.status == 200)
+ {
+ softwarelist_info[device] = req.response;
+ var slotslist = document.getElementById('list-slot-options');
+ if (slotslist)
+ {
+ for (var item = slotslist.firstChild; item; item = item.nextSibling)
+ {
+ if ((item.nodeName == 'DT') && (item.getAttribute('data-slotcard') == device))
+ {
+ var slotname = item.getAttribute('data-slotname');
+ add_softwarelist_rows(
+ device,
+ slotname,
+ document.getElementById('select-slot-choice-' + slotname.replace(/:/g, '-')).value);
+ }
+ }
+ }
+ }
+ };
+ req.send();
+ }
+ };
+ })();
+
+
function add_flag_rows(table, device)
{
var sorted_features = Object.keys(machine_flags[device].features).sort();
@@ -228,6 +270,51 @@ function add_flag_rows(table, device)
}
+function add_softwarelist_rows(device, slot, card)
+{
+ var sorted_softwarelists = Object.keys(softwarelist_info[device]).sort();
+ var cardtag = slot + ':' + card;
+ if (sorted_softwarelists.length)
+ {
+ var table = document.getElementById('tbl-softwarelists').tBodies[0];
+ sorted_softwarelists.forEach(
+ function (tag)
+ {
+ var cell, link;
+ var info = softwarelist_info[device][tag];
+ var href = appurl + 'softwarelist/' + encodeURIComponent(info.shortname);
+ var row = table.appendChild(document.createElement('tr'));
+ row.setAttribute('id', 'row-softwarelists-' + (cardtag + tag).replace(/:/g, '-'))
+ row.appendChild(document.createElement('td')).textContent = cardtag;
+ link = row.appendChild(document.createElement('td')).appendChild(document.createElement('a'));
+ link.setAttribute('href', href);
+ link.textContent = info.shortname;
+ link = row.appendChild(document.createElement('td')).appendChild(document.createElement('a'));
+ link.setAttribute('href', href);
+ link.textContent = info.description;
+ row.appendChild(document.createElement('td')).textContent = info.status;
+ total = info.total;
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = total.toString(10);
+ cell.style.textAlign = 'right';
+ if (!total)
+ total = 1;
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = (info.supported * 100.0 / total).toFixed(1) + '%';
+ cell.style.textAlign = 'right';
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = (info.partiallysupported * 100.0 / total).toFixed(1) + '%';
+ cell.style.textAlign = 'right';
+ cell = row.appendChild(document.createElement('td'));
+ cell.textContent = (info.unsupported * 100.0 / total).toFixed(1) + '%';
+ cell.style.textAlign = 'right';
+ });
+ document.getElementById('heading-softwarelists').style.removeProperty('display');
+ document.getElementById('tbl-softwarelists').style.removeProperty('display');
+ }
+}
+
+
function add_bios_row(slot, table, device)
{
var sorted_sets = Object.keys(bios_sets[device]).sort();
@@ -394,6 +481,16 @@ function make_slot_change_handler(name, slot, defaults, dfltbtn)
slotslist.removeChild(next);
}
+ // clear out any software lists from previous selection
+ var softwarelist_rowid_prefix = 'row-softwarelists-' + prefix.replace(/:/g, '-');
+ for (var candidate = document.getElementById('tbl-softwarelists').tBodies[0].rows[0]; candidate; )
+ {
+ var next = candidate.nextSibling;
+ if ((candidate.nodeName == 'TR') && candidate.getAttribute('id').startsWith(softwarelist_rowid_prefix))
+ candidate.parentNode.removeChild(candidate);
+ candidate = next;
+ }
+
if (selection === null)
{
// no selection, remove the slot card details table
@@ -427,6 +524,12 @@ function make_slot_change_handler(name, slot, defaults, dfltbtn)
else
add_bios_row(slotname, tbl, selection.device);
+ // if we have software list info, populate now, otherweise fetch asynchronously
+ if (!Object.prototype.hasOwnProperty.call(softwarelist_info, selection.device))
+ fetch_softwarelist_info(selection.device);
+ else
+ add_softwarelist_rows(selection.device, slotname, choice);
+
// drop the details table into the list
if (def.firstChild)
def.replaceChild(tbl, def.firstChild);
diff --git a/scripts/minimaws/lib/assets/romident.js b/scripts/minimaws/lib/assets/romident.js
index 9ca2c84f54d..e42daf875cf 100644
--- a/scripts/minimaws/lib/assets/romident.js
+++ b/scripts/minimaws/lib/assets/romident.js
@@ -454,6 +454,14 @@ function add_name(group, name, crc, sha1)
if (group.names.indexOf(name) < 0)
{
group.names.push(name);
+
+ if (group.hasOwnProperty('issues'))
+ {
+ var issues = group.issues;
+ if (issues !== null)
+ issues.parentNode.parentNode.insertBefore(document.createElement('dt'), issues.parentNode).textContent = name;
+ }
+
if (group.hasOwnProperty('machines'))
{
var machines = group.machines;
@@ -575,8 +583,6 @@ function identify_file(file, trychd, progress)
else
{
crcgrp = sha1grp.crc[crc];
- if (crcgrp.issues !== null)
- crcgrp.issues.parentNode.parentNode.insertBefore(document.createElement('dl'), crcgrp.issues.parentNode).textContent = file.name;
add_name(crcgrp, file.name, crc, sha1);
progress.parentNode.removeChild(progress);
}
diff --git a/scripts/minimaws/lib/dbaccess.py b/scripts/minimaws/lib/dbaccess.py
index cc359bfa65c..7ca5ebf243d 100644
--- a/scripts/minimaws/lib/dbaccess.py
+++ b/scripts/minimaws/lib/dbaccess.py
@@ -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():
diff --git a/scripts/minimaws/lib/htmltmpl.py b/scripts/minimaws/lib/htmltmpl.py
index 185771af6ee..7c9246865b1 100644
--- a/scripts/minimaws/lib/htmltmpl.py
+++ b/scripts/minimaws/lib/htmltmpl.py
@@ -70,6 +70,47 @@ 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>Card</th>\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 id="row-softwarelists-${rowid}">\n' \
+ ' <td></td>\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 +133,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&hellip;<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' \
diff --git a/scripts/minimaws/lib/wsgiserve.py b/scripts/minimaws/lib/wsgiserve.py
index 8046785b5f1..e77bb3f637f 100644
--- a/scripts/minimaws/lib/wsgiserve.py
+++ b/scripts/minimaws/lib/wsgiserve.py
@@ -109,6 +109,80 @@ 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 slot options
+ 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 }
+
+ # if there are any slots, get defaults
+ if result['slots']:
+ for slot, default in self.dbcurs.get_slot_defaults(machine):
+ result['defaults'][slot] = default
+
+ # 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
+ if result:
+ 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 +289,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 +304,22 @@ 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(
+ rowid=cgi.escape(softwarelist['tag'].replace(':', '-'), True),
+ 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 +355,29 @@ 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 machine_flags[%s] = %s;\n softwarelist_info[%s] = %s;\n' %
+ (carddev, self.sanitised_json(self.bios_data(cardid)), carddev, self.sanitised_json(self.flags_data(cardid)), carddev, self.sanitised_json(self.softwarelist_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 +444,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 +647,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 +735,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 +772,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 +900,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)