Commit 218b415b authored by Samuel GAIST's avatar Samuel GAIST

Merge branch '184_toolchain_editor' into 'master'

Toolchain Editor

Closes #184

See merge request !117
parents 0d6ff0f9 5a48ef4b
Pipeline #35622 passed with stages
in 6 minutes and 51 seconds
# vim: set fileencoding=utf-8 :
###############################################################################
# #
# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ #
# Contact: beat.support@idiap.ch #
# #
# This file is part of the beat.editor module of the BEAT platform. #
# #
# Commercial License Usage #
# Licensees holding valid commercial BEAT licenses may use this file in #
# accordance with the terms contained in a written agreement between you #
# and Idiap. For further information contact tto@idiap.ch #
# #
# Alternatively, this file may be used under the terms of the GNU Affero #
# Public License version 3 as published by the Free Software and appearing #
# in the file LICENSE.AGPL included in the packaging of this file. #
# The BEAT platform is distributed in the hope that it will be useful, but #
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
# or FITNESS FOR A PARTICULAR PURPOSE. #
# #
# You should have received a copy of the GNU Affero Public License along #
# with the BEAT platform. If not, see http://www.gnu.org/licenses/. #
# #
###############################################################################
import simplejson as json
from PyQt5.QtSql import QSqlDatabase
from PyQt5.QtSql import QSqlQuery
from PyQt5.QtSql import QSqlTableModel
from beat.core.database import Database
from .asset import Asset
from .asset import AssetType
from .assetmodel import AssetModel
PARAMETER_TYPE_KEY = "parameter_type"
DEFAULT_VALUE_KEY = "default_value"
EDITED_KEY = "edited"
# ------------------------------------------------------------------------------
# Prefix modelization
class ExperimentResources:
"""Modelization of the experiments resources"""
def __init__(self, context=None):
self.context = context
database = QSqlDatabase.addDatabase("QSQLITE")
database.setDatabaseName(":memory:")
if not database.open():
raise RuntimeError(
f"Failed to open database: {database.lastError().text()}"
)
self.refresh()
def setContext(self, context):
if self.context == context:
return
self.context = context
self.refresh()
def refresh(self):
ALGORITHM_TABLE_CLEANUP = "DROP TABLE IF EXISTS algorithms"
ALGORITHM_TABLE = "CREATE TABLE algorithms(name varchar, type varchar, inputs integer, outputs integer, is_analyzer boolean)"
INSERT_ALGORITHM = "INSERT INTO algorithms(name, type, inputs, outputs, is_analyzer) VALUES(?, ?, ?, ?, ?)"
QUEUE_TABLE_CLEANUP = "DROP TABLE IF EXISTS queues"
QUEUE_TABLE = "CREATE TABLE queues(name varchar, env_name varchar, env_version varchar, env_type varchar)"
INSERT_QUEUE = "INSERT INTO queues(name, env_name, env_version, env_type) VALUES (?, ?, ?, ?)"
DATASET_TABLE_CLEANUP = "DROP TABLE IF EXISTS datasets"
DATASET_TABLE = "CREATE TABLE datasets(name varchar, outputs integer)"
INSERT_DATASET = "INSERT INTO datasets(name, outputs) VALUES(?, ?)"
query = QSqlQuery()
for query_str in [ALGORITHM_TABLE_CLEANUP, QUEUE_TABLE_CLEANUP, DATASET_TABLE_CLEANUP]:
if not query.exec_(query_str):
raise RuntimeError(f"Failed to drop table: {query.lastError().text()}")
for query_str in [ALGORITHM_TABLE, QUEUE_TABLE, DATASET_TABLE]:
if not query.exec_(query_str):
raise RuntimeError(
f"Failed to create table: {query.lastError().text()}"
)
if self.context is None:
return
prefix_path = self.context.meta["config"].path
model = AssetModel()
model.asset_type = AssetType.ALGORITHM
model.prefix_path = prefix_path
model.setLatestOnlyEnabled(False)
if not query.prepare(INSERT_ALGORITHM):
raise RuntimeError(f"Failed to prepare query: {query.lastError().text()}")
for algorithm in model.stringList():
asset = Asset(prefix_path, AssetType.ALGORITHM, algorithm)
try:
declaration = asset.declaration
except json.JSONDecodeError:
continue
inputs = {}
outputs = {}
for group in declaration["groups"]:
inputs.update(group.get("inputs", {}))
outputs.update(group.get("outputs", {}))
query.addBindValue(algorithm)
query.addBindValue(declaration.get("type", "legacy"))
query.addBindValue(len(inputs))
query.addBindValue(len(outputs))
query.addBindValue("results" in declaration)
if not query.exec_():
raise RuntimeError(
f"Failed to insert algorithm: {query.lastError().text()}"
)
if not query.prepare(INSERT_QUEUE):
raise RuntimeError(f"Failed to prepare query: {query.lastError().text()}")
environments_path = self.context.meta["environments"]
with open(environments_path, "rt") as file:
environment_data = json.load(file)
for item in environment_data.get("remote", []):
env_name = item["name"]
env_version = item["version"]
# import ipdb; ipdb.set_trace()
for name in item["queues"].keys():
query.addBindValue(name)
query.addBindValue(env_name)
query.addBindValue(env_version)
query.addBindValue("remote")
if not query.exec_():
raise RuntimeError(
f"Failed to insert queue: {query.lastError().text()}"
)
for _, image_info in environment_data.get("docker", {}).items():
env_name = image_info["name"]
env_version = image_info["version"]
query.addBindValue("Local")
query.addBindValue(env_name)
query.addBindValue(env_version)
query.addBindValue("docker")
if not query.exec_():
raise RuntimeError(
f"Failed to insert queue: {query.lastError().text()}"
)
model.asset_type = AssetType.DATABASE
if not query.prepare(INSERT_DATASET):
raise RuntimeError(f"Failed to prepare query: {query.lastError().text()}")
for database_name in model.stringList():
database = Database(prefix_path, database_name)
if not database.valid:
print("Skipping invalid database:", database_name)
continue
protocols = database.protocol_names
for protocol_name in protocols:
sets = database.set_names(protocol_name)
for set_name in sets:
set_data = database.set(protocol_name, set_name)
name = f"{database_name}/{protocol_name}/{set_name}"
output_count = len(set_data["outputs"])
query.addBindValue(name)
query.addBindValue(output_count)
if not query.exec_():
raise RuntimeError(
f"Failed to insert dataset: {query.lastError().text()}"
)
class AlgorithmResourceModel(QSqlTableModel):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._analyzer_enabled = False
self._input_count = None
self._output_count = None
self._types = []
self.setTable("algorithms")
self.select()
self.update()
def update(self):
filter_str = f"is_analyzer={self._analyzer_enabled}"
if self._input_count is not None:
filter_str += f" AND inputs={self._input_count}"
if self._output_count is not None:
filter_str += f" AND outputs={self._output_count}"
if self._types:
filter_str += " AND type in ({})".format(
",".join([f"'{type_}'" for type_ in self._types])
)
self.setFilter(filter_str)
def setAnalyzerEnabled(self, enabled):
if self._analyzer_enabled == enabled:
return
self._analyzer_enabled = enabled
self.update()
def setInputCount(self, count):
if self._input_count == count:
return
self._input_count = count
self.update()
def setOutputCount(self, count):
if self._output_count == count:
return
self._output_count = count
self.update()
def setTypes(self, type_list):
if self._types == type_list:
return
self._types = type_list
self.update()
class QueueResourceModel(QSqlTableModel):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._environment = None
self._version = None
self._type = None
self.setTable("queues")
self.select()
self.update()
def update(self):
filter_str = ""
if self._environment is not None:
filter_str += f"env_name='{self._environment}'"
if self._version is not None:
if filter_str:
filter_str += " AND "
filter_str += f"env_version='{self._version}'"
if self._type is not None:
if filter_str:
filter_str += " AND "
filter_str += f"env_type='{self._type}'"
self.setFilter(filter_str)
def setEnvironment(self, name, version):
if self._environment == name and self._version == version:
return
self._environment = name
self._version = version
self.update()
def setType(self, type_):
if self._type == type_:
return
self._type = type_
self.update()
def dump(self):
print(self.filter())
for i in range(self.rowCount()):
print([self.index(i, j).data() for j in range(4)])
class DatasetResourceModel(QSqlTableModel):
def __init__(self, parent=None):
super().__init__(parent=parent)
self._analyzer_enabled = False
self._output_count = None
self.setTable("datasets")
self.select()
def update(self):
filter_str = f""
if self._output_count is not None:
filter_str += f"outputs={self._output_count}"
self.setFilter(filter_str)
def setOutputCount(self, count):
if self._output_count == count:
return
self._output_count = count
self.update()
......@@ -9,74 +9,6 @@
from PyQt5 import QtCore
qt_resource_data = b"\
\x00\x00\x04\x1d\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x32\x00\x00\x00\x32\x08\x06\x00\x00\x00\x1e\x3f\x88\xb1\
\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\
\xa7\x93\x00\x00\x03\xd2\x49\x44\x41\x54\x68\x81\xed\xd9\x4f\x8c\
\x5d\x53\x1c\x07\xf0\x8f\x19\xa6\x33\x46\xa9\x54\x2a\x98\x41\x54\
\x4a\xea\x4f\x2c\x2a\x46\x08\xd1\x84\x54\x68\x17\x22\x12\x2c\x4a\
\x24\x36\x22\x11\x0d\x0b\x6c\x1a\x0b\x12\x06\x1b\xa5\x16\xe2\xdf\
\x4c\x11\x2b\x52\x2c\x58\x35\x61\xd1\x8c\x88\x96\xd4\x9f\x4d\x27\
\x99\xa2\x69\xa2\x2a\x35\xda\x0c\xcf\xe2\x9c\xdb\xf3\x3a\xf3\xde\
\xcc\xbd\xf7\xdd\xf7\xde\xe6\x7d\x93\x9b\xcc\x3b\xf7\xfb\xfb\x77\
\xef\xfd\xfd\x39\x67\xe8\xa1\x87\x1e\x7a\xe8\xa1\x87\xf2\x38\xa5\
\x03\x36\xc6\x70\x17\xae\xc3\x1a\xac\xc0\xbf\x98\xc1\x01\x7c\x81\
\x8f\xb1\xa7\x03\xbe\x94\xc2\x46\x7c\x87\x5a\xce\xeb\x2b\xdc\xdc\
\x15\x4f\x9b\xe0\x2c\xbc\x2f\x39\xf8\x1b\x5e\xc4\x1d\xb8\x00\x43\
\x18\x16\xde\xce\x46\x6c\xc7\xc1\x3a\xfe\x7b\x91\xd3\x55\xac\xc2\
\xb7\x82\x43\x7f\xe2\x71\x0c\xe4\x90\x1b\xc6\xd3\x38\x12\x65\x77\
\xe3\xbc\x36\xf9\xb8\x24\x06\x31\x15\x1d\xf9\x41\x78\xe2\x45\xb1\
\x16\x3f\x4b\xc1\x74\xe5\xcd\x6c\x8b\x0e\xfc\x82\x73\x5b\xd0\x73\
\x8e\x14\xcc\xbb\x15\xf8\xb5\x00\xeb\xf0\x36\xf6\xe3\x1f\x4c\x0b\
\x15\x67\x33\xae\x17\x2a\xd1\x31\x5c\x5d\x81\xad\xb5\xf8\x4b\x08\
\xe6\xa6\x0a\xf4\x21\x94\xe8\xe7\xf1\x9f\xe6\x15\x27\xbb\xf7\x5c\
\x55\x46\xf1\x8c\x54\xcd\x2a\xc1\xb3\x51\xe1\x71\xbc\x80\xcb\x71\
\x9a\x90\xd8\xf7\x48\xc9\x3d\x1b\xd7\xaa\xc2\xb0\x54\xcd\xae\x6c\
\x55\xd9\x15\x98\x13\x3e\x9b\x4d\x4d\x38\xcb\xf0\x21\x26\x5a\x35\
\xd6\x00\x6f\x08\x81\x3c\xd5\xaa\xa2\x57\xa3\xa2\xd7\x97\xe0\x9d\
\x8a\x4b\x5a\x35\xd6\x00\x9b\xa2\xfd\x2f\x5b\x55\xf4\x53\x54\xb4\
\xae\x55\x45\x25\xb1\x26\xda\xff\xb1\xac\x82\x3e\x6c\x10\xf2\xa2\
\x26\xf4\x88\x6e\x60\x79\xb4\x7f\xa4\x8c\xf0\x06\xec\x73\x72\x55\
\xea\xab\xcc\xb5\x62\x18\x92\xaa\xe2\x9d\x38\x3b\x8f\x50\x3f\x5e\
\x92\x9c\xdf\x8f\xad\xb8\xac\x3d\x3e\xe6\xc2\xf9\x4e\x7e\xa0\xc7\
\xf0\x26\x2e\x5c\x4c\x68\x5b\x1d\x79\x8b\x90\xc0\xdd\xc6\x2a\xa1\
\x87\xbd\x22\x24\xfc\x9c\xe0\xe3\x61\xdc\xdd\x48\xe0\x3e\xa9\x17\
\xac\xef\x8c\x8f\xa5\x30\x8a\x8f\x04\x5f\xe7\x70\x7f\xfd\xcd\x41\
\x61\x83\x53\xc3\x43\x25\x0d\x9c\x29\x0c\x79\xbb\x1a\xdc\xdb\xb5\
\xc8\xfa\xee\x28\x5b\x14\x59\xd7\x9f\x55\x37\x12\xdd\x1b\x17\xbf\
\x51\x2e\xa9\xfb\xb1\x33\xea\x98\x6a\x70\x3f\xfb\xbe\xe7\x23\x9b\
\x94\x77\x46\x1d\x45\xb1\x3d\xca\x7f\x9d\x2d\x4c\xc4\x85\xc7\x4a\
\x28\x83\xf1\x28\x7f\x08\x97\x36\xb8\xdf\x2c\x90\x8b\xf1\x7b\xbc\
\x37\x5e\xc2\xee\x72\x69\x84\x59\x4f\x6a\x7a\x57\x95\x50\xf6\x80\
\x34\x87\xdd\xd2\x84\xd3\x2c\x10\xb8\x51\x98\xa4\x6b\x78\xb8\x84\
\xfd\xad\x51\xf6\x35\xc2\x4e\xae\x86\x95\x05\x95\x8c\x09\x15\x6e\
\xa9\xdc\x5a\x2c\x10\x51\x36\xab\x96\x63\x05\x7d\xb8\x36\xca\x7e\
\x4f\x7a\x22\xcb\x0a\x28\x18\x90\x36\x3f\x2f\x2f\xc1\x5d\x2a\x10\
\x51\x47\x4d\xf8\x3a\xf2\x6c\x8d\x33\xac\x90\xca\xb1\xe9\xf8\x63\
\xd1\x26\x33\x0f\x0f\x46\x99\x3d\x39\x0c\xe7\x09\x64\x00\x7b\x23\
\x6f\x73\x01\x3f\x06\xa3\xcc\x6c\x9f\x30\x8e\xc0\x0d\x05\x14\x64\
\xcd\x68\x5c\xc8\x8f\x56\x71\x5c\x98\x2a\xea\x75\xe7\x41\x36\x71\
\x1f\x80\x47\x85\xa8\x3e\x2b\xa0\x20\xab\x36\x23\x39\xb8\x79\xde\
\x08\xa1\xd9\xd5\xf0\x6b\x01\x3f\xb6\x44\x99\x49\x42\x92\x1f\x8e\
\x0b\xb7\xe6\x54\x90\x8d\x0a\x79\xea\x7f\xde\x40\xfa\xa5\x8e\x9d\
\x07\x43\xc2\x3c\x58\x13\xce\xc7\xc0\x93\x71\xe1\x20\x56\x57\xe8\
\x5c\xbb\xb8\xfd\xf8\x40\x6a\xc2\x27\x1a\x79\x1f\x3e\x91\x82\xb9\
\xad\x0b\xce\xe5\xe5\x9e\x8e\x77\x22\xef\x28\xae\x99\x4f\x38\x43\
\x0a\xa6\x86\xcf\x85\x61\xf2\x22\x0b\x2b\x53\xa7\x03\x19\x12\x0e\
\x20\x9e\x90\xaa\xec\x51\xdc\xde\x4c\x51\x5f\x24\xff\x51\xa7\xb4\
\xfe\xaa\xd2\xb9\x3c\xdc\x66\xc7\x4f\x53\x72\x9e\x9f\xad\xc4\x23\
\xc2\x5b\x99\x96\x3a\x78\x15\xce\x15\xe1\x66\xbf\xff\x16\x4e\x30\
\xdf\x12\x76\x8a\x95\xfd\x3b\xa4\xdb\xc9\xbe\x00\xdd\xda\x8b\x57\
\x8e\xa2\x81\x8c\x60\x47\xdd\xef\x49\xcd\x9b\x62\xbb\xb8\x2d\x63\
\x54\xd8\x73\xcc\x4f\xbc\x43\x0d\x8c\xb6\x8b\x5b\x09\x76\x44\x03\
\x9f\x46\x03\x23\xf1\xef\x13\x23\x42\x07\xb8\x95\x60\x26\x2a\x1f\
\xad\x5b\xcb\xe6\xa3\x99\x0e\x71\x9b\xa2\x4c\xb2\x17\xa9\x2a\xed\
\xe2\xb6\x84\x49\xe9\x13\x18\x8d\x57\xf6\x09\xcc\x3f\x89\x6f\x17\
\xb7\x12\x8c\xc8\x9f\x94\xed\xe2\x56\x86\x11\xe1\x09\xce\xc4\x6b\
\x62\x11\x63\xed\xe2\x36\xc4\xff\xef\xb3\x8d\xc1\x9b\x2f\xa4\x85\
\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x04\xf7\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
......@@ -222,6 +154,624 @@ qt_resource_data = b"\
\xc5\x1d\x63\x37\xbe\xeb\x9b\xc5\x25\x4a\x94\x28\x51\xa2\x44\x01\
\xf8\x1b\xcf\xcc\xb2\x31\xb8\x23\x08\xf2\x00\x00\x00\x00\x49\x45\
\x4e\x44\xae\x42\x60\x82\
\x00\x00\x03\x1c\
\x7b\
\x0a\x09\x22\x64\x72\x61\x77\x69\x6e\x67\x5f\x73\x70\x61\x63\x65\
\x5f\x63\x6f\x6e\x66\x69\x67\x22\x3a\x7b\x0a\x09\x09\x22\x73\x63\
\x65\x6e\x65\x5f\x77\x69\x64\x74\x68\x22\x3a\x20\x32\x30\x30\x30\
\x2c\x0a\x09\x09\x22\x73\x63\x65\x6e\x65\x5f\x68\x65\x69\x67\x68\
\x74\x22\x3a\x20\x32\x30\x30\x30\x2c\x0a\x09\x09\x22\x67\x72\x69\
\x64\x5f\x73\x69\x7a\x65\x22\x3a\x20\x33\x36\x2c\x0a\x09\x09\x22\
\x67\x72\x69\x64\x5f\x63\x6f\x6c\x6f\x72\x22\x3a\x20\x5b\x32\x33\
\x32\x2c\x20\x32\x33\x32\x2c\x20\x32\x33\x32\x2c\x20\x32\x35\x35\
\x5d\x0a\x09\x7d\x2c\x0a\x09\x22\x62\x6c\x6f\x63\x6b\x5f\x63\x6f\
\x6e\x66\x69\x67\x22\x3a\x7b\x0a\x09\x09\x22\x77\x69\x64\x74\x68\
\x22\x3a\x20\x32\x30\x30\x2c\x0a\x09\x09\x22\x68\x65\x69\x67\x68\
\x74\x22\x3a\x20\x32\x35\x2c\x0a\x09\x09\x22\x72\x61\x64\x69\x75\
\x73\x22\x3a\x20\x31\x30\x2c\x0a\x09\x09\x22\x62\x6f\x72\x64\x65\
\x72\x22\x3a\x20\x31\x2c\x0a\x09\x09\x22\x66\x6f\x6e\x74\x22\x3a\
\x20\x22\x41\x72\x69\x61\x6c\x22\x2c\x0a\x09\x09\x22\x66\x6f\x6e\
\x74\x5f\x73\x69\x7a\x65\x22\x3a\x20\x31\x32\x2c\x0a\x09\x09\x22\
\x70\x69\x6e\x5f\x68\x65\x69\x67\x68\x74\x22\x3a\x20\x33\x30\x2c\
\x0a\x09\x09\x22\x70\x69\x6e\x5f\x66\x6f\x6e\x74\x22\x3a\x20\x22\
\x41\x72\x69\x61\x6c\x22\x2c\x0a\x09\x09\x22\x70\x69\x6e\x5f\x66\
\x6f\x6e\x74\x5f\x73\x69\x7a\x65\x22\x3a\x20\x31\x30\x2c\x0a\x09\
\x09\x22\x70\x69\x6e\x5f\x63\x6f\x6c\x6f\x72\x22\x3a\x20\x5b\x32\
\x35\x35\x2c\x20\x31\x35\x35\x2c\x20\x30\x2c\x20\x32\x35\x35\x5d\
\x2c\x0a\x09\x09\x22\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x5f\
\x63\x6f\x6c\x6f\x72\x22\x3a\x20\x5b\x38\x30\x2c\x20\x38\x30\x2c\
\x20\x38\x30\x2c\x20\x32\x35\x35\x5d\x2c\x0a\x09\x09\x22\x62\x61\
\x63\x6b\x67\x72\x6f\x75\x6e\x64\x5f\x63\x6f\x6c\x6f\x72\x5f\x64\
\x61\x74\x61\x73\x65\x74\x73\x22\x3a\x20\x5b\x32\x35\x35\x2c\x20\
\x32\x35\x34\x2c\x20\x32\x30\x30\x2c\x20\x32\x35\x35\x5d\x2c\x0a\
\x09\x09\x22\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x5f\x63\x6f\
\x6c\x6f\x72\x5f\x62\x6c\x6f\x63\x6b\x73\x22\x3a\x20\x5b\x32\x30\
\x34\x2c\x20\x32\x30\x34\x2c\x20\x32\x30\x34\x2c\x20\x32\x35\x35\
\x5d\x2c\x0a\x09\x09\x22\x62\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\
\x5f\x63\x6f\x6c\x6f\x72\x5f\x61\x6e\x61\x6c\x79\x7a\x65\x72\x73\
\x22\x3a\x20\x5b\x31\x33\x36\x2c\x20\x31\x35\x30\x2c\x20\x32\x31\
\x36\x2c\x20\x32\x35\x35\x5d\x2c\x0a\x09\x09\x22\x62\x61\x63\x6b\
\x67\x72\x6f\x75\x6e\x64\x5f\x63\x6f\x6c\x6f\x72\x5f\x6c\x6f\x6f\
\x70\x73\x22\x3a\x20\x5b\x31\x38\x32\x2c\x20\x32\x31\x36\x2c\x20\
\x31\x33\x36\x2c\x20\x32\x35\x35\x5d\x2c\x0a\x09\x09\x22\x62\x6f\
\x72\x64\x65\x72\x5f\x63\x6f\x6c\x6f\x72\x22\x3a\x20\x5b\x35\x30\
\x2c\x20\x35\x30\x2c\x20\x35\x30\x2c\x20\x32\x35\x35\x5d\x2c\x0a\
\x09\x09\x22\x73\x65\x6c\x65\x63\x74\x69\x6f\x6e\x5f\x62\x6f\x72\
\x64\x65\x72\x5f\x63\x6f\x6c\x6f\x72\x22\x3a\x20\x5b\x31\x37\x30\
\x2c\x20\x38\x30\x2c\x20\x38\x30\x2c\x20\x32\x35\x35\x5d\x2c\x0a\
\x09\x09\x22\x74\x65\x78\x74\x5f\x63\x6f\x6c\x6f\x72\x22\x3a\x20\
\x5b\x30\x2c\x20\x30\x2c\x20\x30\x2c\x20\x32\x35\x35\x5d\x0a\x09\
\x7d\x2c\x0a\x09\x22\x63\x6f\x6e\x6e\x65\x63\x74\x69\x6f\x6e\x5f\
\x63\x6f\x6e\x66\x69\x67\x22\x3a\x7b\x0a\x09\x09\x22\x77\x69\x64\
\x74\x68\x22\x3a\x20\x32\x2c\x0a\x09\x09\x22\x63\x6f\x6c\x6f\x72\
\x22\x3a\x20\x5b\x32\x35\x35\x2c\x20\x31\x38\x30\x2c\x20\x30\x2c\
\x20\x32\x35\x35\x5d\x0a\x09\x7d\x0a\x7d\x0a\
\x00\x00\x07\x1e\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x2e\x00\x00\x00\x29\x08\x06\x00\x00\x00\x4a\xbc\x1b\x8f\
\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
\x00\x00\x03\x82\x74\x45\x58\x74\x6d\x78\x66\x69\x6c\x65\x00\x25\
\x33\x43\x6d\x78\x66\x69\x6c\x65\x25\x32\x30\x68\x6f\x73\x74\x25\
\x33\x44\x25\x32\x32\x77\x77\x77\x2e\x64\x72\x61\x77\x2e\x69\x6f\
\x25\x32\x32\x25\x32\x30\x6d\x6f\x64\x69\x66\x69\x65\x64\x25\x33\
\x44\x25\x32\x32\x32\x30\x31\x39\x2d\x31\x31\x2d\x30\x38\x54\x31\
\x35\x25\x33\x41\x35\x32\x25\x33\x41\x34\x35\x2e\x34\x34\x35\x5a\
\x25\x32\x32\x25\x32\x30\x61\x67\x65\x6e\x74\x25\x33\x44\x25\x32\
\x32\x4d\x6f\x7a\x69\x6c\x6c\x61\x25\x32\x46\x35\x2e\x30\x25\x32\
\x30\x28\x58\x31\x31\x25\x33\x42\x25\x32\x30\x4c\x69\x6e\x75\x78\
\x25\x32\x30\x78\x38\x36\x5f\x36\x34\x29\x25\x32\x30\x41\x70\x70\
\x6c\x65\x57\x65\x62\x4b\x69\x74\x25\x32\x46\x35\x33\x37\x2e\x33\
\x36\x25\x32\x30\x28\x4b\x48\x54\x4d\x4c\x25\x32\x43\x25\x32\x30\
\x6c\x69\x6b\x65\x25\x32\x30\x47\x65\x63\x6b\x6f\x29\x25\x32\x30\
\x43\x68\x72\x6f\x6d\x65\x25\x32\x46\x37\x33\x2e\x30\x2e\x33\x36\
\x38\x33\x2e\x37\x35\x25\x32\x30\x53\x61\x66\x61\x72\x69\x25\x32\
\x46\x35\x33\x37\x2e\x33\x36\x25\x32\x32\x25\x32\x30\x76\x65\x72\
\x73\x69\x6f\x6e\x25\x33\x44\x25\x32\x32\x31\x32\x2e\x32\x2e\x33\
\x25\x32\x32\x25\x32\x30\x65\x74\x61\x67\x25\x33\x44\x25\x32\x32\
\x4c\x73\x45\x37\x51\x79\x4e\x6b\x6d\x48\x74\x6c\x41\x45\x47\x63\
\x43\x41\x4f\x64\x25\x32\x32\x25\x32\x30\x74\x79\x70\x65\x25\x33\
\x44\x25\x32\x32\x64\x65\x76\x69\x63\x65\x25\x32\x32\x25\x32\x30\
\x70\x61\x67\x65\x73\x25\x33\x44\x25\x32\x32\x31\x25\x32\x32\x25\
\x33\x45\x25\x33\x43\x64\x69\x61\x67\x72\x61\x6d\x25\x32\x30\x69\
\x64\x25\x33\x44\x25\x32\x32\x30\x49\x30\x77\x62\x31\x6e\x71\x41\
\x32\x37\x44\x76\x5a\x31\x76\x77\x7a\x5f\x53\x25\x32\x32\x25\x33\
\x45\x72\x5a\x52\x4e\x63\x34\x4d\x67\x45\x45\x42\x25\x32\x46\x6a\
\x58\x65\x52\x4f\x47\x4f\x75\x74\x55\x6c\x37\x36\x53\x6d\x48\x6e\
\x68\x6e\x5a\x43\x46\x4e\x30\x48\x53\x52\x56\x25\x32\x42\x25\x32\
\x42\x75\x4c\x41\x61\x49\x30\x53\x54\x39\x6d\x36\x73\x47\x42\x74\
\x37\x73\x43\x6a\x78\x30\x54\x57\x6a\x62\x6a\x6b\x32\x61\x64\x65\
\x45\x45\x4f\x4b\x73\x6c\x53\x50\x69\x62\x30\x4d\x63\x6d\x79\x67\
\x6c\x44\x37\x6e\x73\x48\x6b\x77\x49\x61\x6d\x44\x74\x52\x61\x63\
\x6f\x66\x49\x41\x67\x37\x79\x41\x7a\x77\x4d\x61\x53\x66\x4a\x6f\
\x59\x38\x53\x44\x61\x49\x79\x73\x6f\x74\x68\x68\x57\x30\x4c\x6c\
\x59\x6b\x59\x30\x78\x71\x48\x4f\x4f\x32\x49\x4b\x6c\x36\x31\x59\
\x7a\x56\x63\x67\x55\x50\x46\x31\x44\x56\x39\x6c\x64\x77\x49\x66\
\x36\x77\x38\x58\x66\x67\x7a\x79\x46\x71\x45\x6c\x55\x6e\x71\x49\
\x77\x30\x4c\x79\x52\x37\x30\x67\x6e\x45\x63\x56\x6f\x6a\x75\x45\
\x6c\x70\x71\x52\x4f\x4e\x47\x7a\x56\x69\x43\x6d\x74\x30\x46\x4c\
\x36\x35\x75\x66\x79\x64\x36\x32\x5a\x69\x47\x31\x76\x79\x6d\x49\
\x48\x4d\x46\x37\x30\x79\x64\x25\x32\x46\x4e\x6e\x38\x76\x73\x77\
\x55\x44\x71\x76\x78\x31\x48\x4b\x59\x38\x30\x6c\x43\x48\x77\x59\
\x68\x44\x52\x77\x36\x56\x73\x33\x52\x77\x64\x36\x75\x5a\x63\x49\
\x30\x79\x6f\x65\x50\x55\x71\x6b\x53\x46\x65\x70\x7a\x4c\x53\x32\
\x4b\x62\x63\x34\x4c\x79\x33\x75\x6a\x38\x51\x31\x57\x6b\x66\x54\
\x38\x32\x49\x6a\x66\x41\x47\x67\x44\x34\x39\x31\x44\x6b\x49\x73\
\x61\x32\x31\x4b\x41\x44\x52\x67\x39\x32\x52\x52\x66\x45\x47\x52\
\x4f\x38\x58\x52\x59\x72\x6d\x62\x6a\x6b\x56\x6a\x64\x53\x6d\x44\
\x4d\x4e\x30\x4e\x39\x25\x32\x42\x65\x37\x69\x79\x77\x36\x38\x73\
\x74\x76\x36\x36\x4d\x25\x32\x46\x36\x62\x49\x46\x74\x53\x25\x32\
\x46\x68\x5a\x48\x65\x73\x37\x31\x36\x74\x48\x4f\x63\x36\x36\x76\
\x33\x48\x32\x78\x66\x4a\x25\x32\x42\x76\x39\x33\x25\x32\x42\x6c\
\x30\x75\x53\x52\x79\x35\x70\x66\x75\x57\x53\x33\x48\x42\x4a\x25\
\x32\x46\x75\x37\x53\x54\x70\x63\x32\x50\x38\x64\x57\x25\x32\x46\
\x77\x71\x36\x25\x32\x42\x77\x51\x25\x33\x44\x25\x33\x43\x25\x32\
\x46\x64\x69\x61\x67\x72\x61\x6d\x25\x33\x45\x25\x33\x43\x25\x32\
\x46\x6d\x78\x66\x69\x6c\x65\x25\x33\x45\x94\xd7\x81\x6f\x00\x00\
\x03\x47\x49\x44\x41\x54\x58\x47\x63\x64\x20\x0c\x24\xb8\xb8\x85\
\x3a\xfe\xff\xfb\x63\xc7\xcd\x23\xc8\xf0\xe6\xf5\x43\x45\xc2\x5a\
\xa8\xa3\x42\x44\x54\xfe\xfe\xd7\x2f\xef\x19\x18\x99\x58\x0e\x7c\
\xfb\xfa\xae\x02\x68\xea\x2b\x98\xc9\x8c\xf8\xac\x10\x14\x94\x8c\
\xfe\xf6\xfd\xd3\xcc\xe0\xe8\x16\x6e\x11\x31\x05\x06\x29\x19\x4d\
\xea\xb8\x88\x04\x53\x9e\x3d\xb9\xce\xf0\xe6\xd5\x03\x86\x35\x4b\
\xaa\xbf\xf1\x70\x0b\x24\xbd\x7b\xf7\x6c\x25\x48\x3b\x4e\x87\x83\
\x1c\x2d\x22\xae\x34\x35\x39\x77\x2e\x3f\x09\xf6\xd0\x54\xe9\x9c\
\xc9\x89\x1f\xdf\xbe\x7c\x98\xf1\xfe\xfd\xb3\x15\xb8\x1c\x2e\xc1\
\xce\xc1\x7d\xb7\xb1\xf7\x2c\x17\x4d\x5d\x42\x86\xe1\x75\x85\x46\
\xdf\x7f\xfd\xfa\x26\x87\xd5\xe1\xc0\x34\xbd\x20\x20\xbc\x2e\x5e\
\xcf\xd8\x83\x0c\xa3\x69\xab\xe5\xc2\x99\x2d\x0c\x9b\xd7\xb4\xcd\
\xc3\xea\x70\x4e\x4e\xfe\x7b\xa9\x05\x0b\x14\x07\x22\x4d\x13\xf2\
\xf6\xd3\x47\x57\x19\xe6\x4c\x4e\xba\x8b\xd5\xe1\xc2\xc0\xdc\x5c\
\xda\xb0\x53\x81\x90\x21\x03\x25\xdf\xdd\xe0\xfe\x00\x57\x1a\xff\
\xdf\x31\xf5\xc6\x40\xb9\x8b\xa0\xbd\x15\xd9\x1a\x38\x4b\x95\x51\
\x87\x13\x0c\x3e\x32\x14\x8c\x86\x38\x19\x81\x46\x91\x96\xd1\x10\
\xa7\x28\xf8\xc8\xd0\x3c\x7c\x42\x5c\x8a\x8f\x61\xe2\xb7\xdf\x0c\
\xbe\xfc\x5c\x0c\x8a\x4c\x9c\x32\x0c\x9a\xba\x8e\x0c\x76\x3e\xd5\
\x64\x84\x09\x6d\xb5\x20\x87\x38\x33\x27\x2b\xc3\xc7\xe9\x51\x0c\
\x0c\x06\xb2\x0c\xdc\xfa\xb2\x0c\x0c\x17\x1e\x33\x30\x9c\x7b\xc2\
\xc1\x50\xb2\x96\x89\xa1\xa4\xf5\x34\x03\x33\x33\x33\x6d\x5d\x43\
\x82\xe9\x70\x87\x73\xb0\x30\x7c\x3e\x57\xc3\xc0\xa3\x29\x89\xa9\
\xfb\x22\xd0\x03\x4e\x93\xb8\x19\xca\xda\xcf\x92\x60\x34\x6d\x95\
\x82\x1d\x2e\x25\xc0\x30\xb1\xcd\x9f\x21\x39\xde\x8a\x81\x1b\x97\
\x75\x73\x8f\x71\x30\xac\x7f\x1d\xc6\x60\xeb\x5d\x45\x5b\x17\x11\
\x69\x3a\xd8\xe1\x82\x5c\x0c\xf7\xf7\x17\x31\x28\x80\x92\x07\x2e\
\x70\xfe\x11\x03\x43\xf0\x22\x19\x86\xf4\x8a\x3d\x44\x1a\x4d\x5b\
\x65\x60\x87\xab\x8a\x31\xdc\xbf\xd5\xcc\x40\xb0\x25\x68\xd4\x2b\
\xcd\x10\x9e\xbf\x97\xb6\x2e\x22\xd2\x74\x58\x88\xdf\xdb\x57\xc4\
\xa0\x08\xcc\x94\x38\xc1\x39\x60\x88\x87\x2e\x92\x65\x48\xab\xd8\
\x4d\xa4\xd1\xb4\x55\x06\x4f\xe3\x2d\xfe\x0c\x29\x89\x56\x0c\x38\
\xbb\x69\x73\x8e\xb1\x33\x6c\x7c\x13\xc9\x60\xe3\x05\xea\x68\x0f\
\x3c\x80\x97\x2a\xdc\x6c\x0c\x9f\x8e\x96\x33\xf0\xea\xcb\x60\x3a\
\xea\xf4\x03\x06\x06\xaf\x69\x3c\x0c\x25\x6d\x67\x06\xde\xc5\x50\
\x17\x20\x97\xe3\x4c\xdc\xac\x0c\xef\x27\x45\x32\xb0\x18\xca\x32\
\x70\x19\xca\x01\xcb\x70\x60\xf2\x38\xf7\x84\x9d\xa1\x72\x23\x1b\
\x43\x51\xf3\x49\x06\x26\x26\xa6\x41\xe9\x70\xb0\xa3\xa4\x05\x18\
\xfa\xbf\xfd\x62\xf0\xe3\xe7\x64\x50\x62\xe2\x92\x65\xd0\xd2\x73\
\x02\x26\x8f\xca\x41\xe3\x60\x98\x43\x86\x4f\x5b\x05\x29\x68\x47\
\xbb\x6e\xb4\x48\x67\xa3\x49\x85\x16\xa1\x8a\xcf\xcc\xd1\x10\x1f\
\x0d\x71\x22\x43\x00\x67\x52\x19\xb2\x63\x87\x43\x76\xb4\x96\x9b\
\x57\x68\x9e\x7f\x58\x6d\xa2\x9e\x91\x27\x91\x91\x47\x3f\x65\x17\
\xcf\x6c\x65\xd8\xb8\xaa\x75\x2e\xae\xd1\x5a\x31\x36\x76\xae\xfb\
\x4d\x7d\xe7\x06\xdd\x8c\x44\x6d\xa1\xe1\x8f\xdf\xbf\xbe\x4b\xe3\
\x9c\x03\x12\x12\x92\x0a\x17\x12\x53\x98\x99\x92\x3b\x6f\xf0\xcc\
\x01\x4d\x4a\xfc\xf8\xee\xf5\xd3\x94\x77\xef\x1e\xad\x21\x30\xeb\
\x26\x15\xf1\xf5\xeb\x87\x79\x41\x51\x4d\x9c\xa2\xe2\x8a\x0c\xd2\
\x72\xda\xf4\x4b\x13\x50\x9b\x40\x33\x10\xe0\x59\xb7\xa5\x35\x3f\
\x78\x79\x44\x63\x41\x8e\x06\x49\xe1\x75\x38\x54\xaf\x08\x70\x4e\
\xa8\xf3\xff\xff\xbf\xf6\x5c\xdc\x02\xcc\x6f\x5f\x3f\x54\xa0\x97\
\xeb\x81\xa5\xdb\x83\x6f\x5f\x3f\xfc\x65\x62\x66\x3e\xf0\xf5\xf3\
\xbb\x32\xa0\xbd\xef\x60\x76\x03\x00\x2e\xa6\x2d\x16\xfe\xd0\x50\
\x3e\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
\x00\x00\x07\x0e\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
\x00\x00\x2e\x00\x00\x00\x29\x08\x06\x00\x00\x00\x4a\xbc\x1b\x8f\
\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\
\x00\x00\x03\x7a\x74\x45\x58\x74\x6d\x78\x66\x69\x6c\x65\x00\x25\
\x33\x43\x6d\x78\x66\x69\x6c\x65\x25\x32\x30\x68\x6f\x73\x74\x25\
\x33\x44\x25\x32\x32\x77\x77\x77\x2e\x64\x72\x61\x77\x2e\x69\x6f\