commit 6150f9cd92204a9b3209b7bba47afa8c99120801 Author: Adam McGuigan <31741286+apmcguigan@users.noreply.github.com> Date: Sat Apr 25 01:02:48 2020 -0300 Master Branch Merge diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9e36a2c --- /dev/null +++ b/.gitignore @@ -0,0 +1,134 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Custom +.idea/ +all_data/ +.DS_Store diff --git a/Constants.py b/Constants.py new file mode 100644 index 0000000..b319d41 --- /dev/null +++ b/Constants.py @@ -0,0 +1,609 @@ +from enum import Enum + +# Variable Constants for MainDriver.py +HELP_SITE = "https://git.library.upei.ca/mbelvadi/COUNTER-5-Report-Tool/wiki" + +# region Variable Constants for ManageDB + +# region field and table definitions +# region dictionary keys +NAME_KEY = 'name' +TYPE_KEY = 'type' +OPTIONS_KEY = 'options' +REPORTS_KEY = 'reports' +CALCULATION_KEY = 'calculation' +SOURCE_KEY = 'source' + +FIELD_KEY = 'field' +COMPARISON_KEY = 'comparison' +VALUE_KEY = 'value' +# endregion + +# region header definition +HEADER_ENTRIES = ('report_name', 'report_id', 'release', 'institution_name', 'institution_id', 'metric_types', + 'report_filters', 'report_attributes', 'exceptions', 'reporting_period', 'created', 'created_by') +HEADER_ROWS = len(HEADER_ENTRIES) +BLANK_ROWS = 1 +# endregion + +# region database report definitions +DATABASE_REPORTS = ('DR', 'DR_D1', 'DR_D2') +DATABASE_REPORTS_METRIC = ('Searches_Automated', + 'Searches_Federated', + 'Searches_Regular', + 'Total_Item_Investigations', + 'Total_Item_Requests', + 'Unique_Item_Investigations', + 'Unique_Item_Requests', + 'Unique_Title_Investigations', + 'Unique_Title_Requests', + 'Limit_Exceeded', + 'No_License') +DATABASE_REPORTS_ATTRIBUTES = ("Data_Type", + "Access_Method") +DATABASE_REPORT_FIELDS = ({NAME_KEY: 'database', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('DR', 'DR_D1', 'DR_D2')}, + {NAME_KEY: 'publisher', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('DR', 'DR_D1', 'DR_D2')}, + {NAME_KEY: 'publisher_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('DR', 'DR_D1', 'DR_D2')}, + {NAME_KEY: 'platform', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('DR', 'DR_D1', 'DR_D2')}, + {NAME_KEY: 'proprietary_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('DR', 'DR_D1', 'DR_D2')}, + {NAME_KEY: 'data_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('DR',)}, + {NAME_KEY: 'access_method', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('DR',)}) +# endregion + +# region item report definitions +ITEM_REPORTS = ('IR', 'IR_A1', 'IR_M1') +ITEM_REPORTS_METRIC = ('Total_Item_Investigations', + 'Total_Item_Requests', + 'Unique_Item_Investigations', + 'Unique_Item_Requests', + 'Limit_Exceeded', + 'No_License') +ITEM_REPORTS_ATTRIBUTES = ("Data_Type", + "Access_Type", + "Access_Method", + "YOP", + "Authors", + "Publication_Date", + "Article_Version") +ITEM_REPORT_FIELDS = ({NAME_KEY: 'item', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1', 'IR_M1')}, + {NAME_KEY: 'publisher', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1', 'IR_M1')}, + {NAME_KEY: 'publisher_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1', 'IR_M1')}, + {NAME_KEY: 'platform', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1', 'IR_M1')}, + {NAME_KEY: 'authors', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'publication_date', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'doi', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1', 'IR_M1')}, + {NAME_KEY: 'proprietary_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1', 'IR_M1')}, + {NAME_KEY: 'isbn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'print_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'online_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'uri', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1', 'IR_M1')}, + {NAME_KEY: 'parent_title', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'parent_authors', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'parent_publication_date', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'parent_article_version', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'parent_data_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'parent_doi', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'parent_proprietary_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'parent_isbn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'parent_print_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'parent_online_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'parent_uri', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'component_title', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_authors', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_publication_date', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_data_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_doi', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_proprietary_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_isbn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_print_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_online_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'component_uri', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'data_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'yop', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}, + {NAME_KEY: 'access_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR', 'IR_A1')}, + {NAME_KEY: 'access_method', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('IR',)}) +# endregion + +# region platform report definitions +PLATFORM_REPORTS = ('PR', 'PR_P1') +PLATFORM_REPORTS_METRIC = ('Searches_Platform', + 'Total_Item_Investigations', + 'Total_Item_Requests', + 'Unique_Item_Investigations', + 'Unique_Item_Requests', + 'Unique_Title_Investigations', + 'Unique_Title_Requests') +PLATFORM_REPORTS_ATTRIBUTES = ("Data_Type", + "Access_Method") +PLATFORM_REPORT_FIELDS = ({NAME_KEY: 'platform', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('PR', 'PR_P1')}, + {NAME_KEY: 'data_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('PR',)}, + {NAME_KEY: 'access_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('PR',)}) +# endregion + +# region title report definitions +TITLE_REPORTS = ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4') +TITLE_REPORTS_METRIC = ('Total_Item_Investigations', + 'Total_Item_Requests', + 'Unique_Item_Investigations', + 'Unique_Item_Requests', + 'Unique_Title_Investigations', + 'Unique_Title_Requests', + 'Limit_Exceeded', + 'No_License') +TITLE_REPORTS_ATTRIBUTES = ("Data_Type", + "Section_Type", + "Access_Type", + "Access_Method", + "YOP") +TITLE_REPORT_FIELDS = ({NAME_KEY: 'title', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'publisher', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'publisher_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'platform', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'doi', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'proprietary_id', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'isbn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3')}, + {NAME_KEY: 'print_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'online_issn', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'uri', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J1', 'TR_J2', 'TR_J3', 'TR_J4')}, + {NAME_KEY: 'data_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR',)}, + {NAME_KEY: 'section_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR',)}, + {NAME_KEY: 'yop', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B1', 'TR_B2', 'TR_B3', 'TR_J4')}, + {NAME_KEY: 'access_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR', 'TR_B3', 'TR_J3')}, + {NAME_KEY: 'access_method', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',), + REPORTS_KEY: ('TR',)}) +# endregion + +# region fields that all reports have +ALL_REPORT_FIELDS = ({NAME_KEY: 'metric_type', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL', 'CHECK(metric_type <> \"\")')}, + {NAME_KEY: 'vendor', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL', 'CHECK(vendor <> \"\")')}, + {NAME_KEY: 'year', + TYPE_KEY: 'INTEGER', + OPTIONS_KEY: ('NOT NULL', 'CHECK(LENGTH(year) = 4)')}, + {NAME_KEY: 'month', + TYPE_KEY: 'INTEGER', + OPTIONS_KEY: ('NOT NULL', 'CHECK(month BETWEEN 1 AND 12)')}, + {NAME_KEY: 'metric', + TYPE_KEY: 'INTEGER', + OPTIONS_KEY: ('NOT NULL', 'CHECK(metric > 0)')}, + {NAME_KEY: 'updated_on', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',)}, + {NAME_KEY: 'file', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL',)}) +# endregion + +# region cost table fields +COST_FIELDS = ({NAME_KEY: 'cost_in_original_currency', + TYPE_KEY: 'REAL', + OPTIONS_KEY: ('NOT NULL', 'CHECK(cost_in_original_currency >= 0)')}, + {NAME_KEY: 'original_currency', + TYPE_KEY: 'TEXT', + OPTIONS_KEY: ('NOT NULL', 'CHECK(original_currency <> \"\")')}, + {NAME_KEY: 'cost_in_local_currency', + TYPE_KEY: 'REAL', + OPTIONS_KEY: ('NOT NULL', 'CHECK(cost_in_local_currency >= 0)')}, + {NAME_KEY: 'cost_in_local_currency_with_tax', + TYPE_KEY: 'REAL', + OPTIONS_KEY: ('NOT NULL', 'CHECK(cost_in_local_currency_with_tax >= 0)')}) +# endregion +# endregion + +ALL_REPORTS = DATABASE_REPORTS + ITEM_REPORTS + PLATFORM_REPORTS + TITLE_REPORTS +REPORT_TYPE_SWITCHER = {'DR': {REPORTS_KEY: DATABASE_REPORTS, 'report_fields': DATABASE_REPORT_FIELDS}, + 'IR': {REPORTS_KEY: ITEM_REPORTS, 'report_fields': ITEM_REPORT_FIELDS}, + 'PR': {REPORTS_KEY: PLATFORM_REPORTS, 'report_fields': PLATFORM_REPORT_FIELDS}, + 'TR': {REPORTS_KEY: TITLE_REPORTS, 'report_fields': TITLE_REPORT_FIELDS}} +NAME_FIELD_SWITCHER = {'DR': 'database', 'IR': 'item', 'PR': 'platform', 'TR': 'title'} + +MONTHS = {1: 'january', 2: 'february', 3: 'march', 4: 'april', 5: 'may', 6: 'june', + 7: 'july', 8: 'august', 9: 'september', 10: 'october', 11: 'november', 12: 'december'} +MONTH_CALCULATION = 'COALESCE(SUM(CASE ' + 'month' + ' WHEN {} THEN ' + 'metric' + ' END), 0)' + +YEAR_TOTAL = 'reporting_period_total' +YEAR_TOTAL_CALCULATION = 'SUM(' + 'metric' + ')' + +RANKING = 'ranking' +RANKING_CALCULATION = 'RANK() OVER(ORDER BY ' + 'SUM(' + 'metric' + ')' + ' DESC)' + +VIEW_SUFFIX = '_view' +COST_TABLE_SUFFIX = '_costs' + +FIELDS_NOT_IN_VIEWS = ('month', 'metric', 'updated_on') +FIELDS_NOT_IN_KEYS = ('metric', 'updated_on') +FIELDS_NOT_IN_SEARCH_DROPDOWN = ('year',) +FIELDS_NOT_IN_CHARTS = FIELDS_NOT_IN_VIEWS + ('file',) +FIELDS_NOT_IN_TOP_NUMBER_CHARTS = FIELDS_NOT_IN_CHARTS + ('year',) + +COSTS_KEY_FIELDS = ('vendor', 'year') +CHART_KEY_FIELDS = ('vendor', 'metric_type') + +DATABASE_FOLDER = r'./all_data/search/' +DATABASE_LOCATION = DATABASE_FOLDER + r'search.db' +# All yearly reports tsv and json are saved here in original condition as backup +PROTECTED_DATABASE_FILE_DIR = "./all_data/.DO_NOT_MODIFY/" +FILE_SUBDIRECTORY_ORDER = ('year', 'vendor') +COSTS_SAVE_FOLDER = r'./all_data/costs/' + +DELIMITERS = {'.tsv': '\t', '.csv': ','} + +COMPARISON_OPERATORS = ('LIKE', 'NOT LIKE', '=', '<=', '<', '>=', '>', '<>') +NON_COMPARISONS = ('IS NULL', 'IS NOT NULL') + +CURRENCY_LIST = ('USD', 'EUR', 'JPY', 'GBP', 'CHF', 'CAD', 'AUD') + +# endregion + +# region Variable Constants for FileDialog Filters +JSON_FILTER = ('JSON files (*.dat)',) +TSV_FILTER = ('TSV files (*.tsv)',) +CSV_FILTER = ('CSV files (*.csv)',) +EXCEL_FILTER = ('Excel files (*.xlsx)',) +# endregion + + +# region Variable Constants for FetchData +MONTH_NAMES = ("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", + "November", "December") + +MASTER_REPORTS = ("DR", "IR", "PR", "TR") + + +class MajorReportType(Enum): + PLATFORM = "PR" + DATABASE = "DR" + TITLE = "TR" + ITEM = "IR" + + +class SpecialOptionType(Enum): + TO = 0 # Tabular Only, not included in request url, only used in creating tabular report + AO = 1 # Attribute Only, only in attributes_to_include, does not have it's own parameters in request url + AP = 2 # Attribute Parameter, in attributes_to_include and has it's own parameters in request url + ADP = 3 # Attribute Date Parameter, in attributes_to_include and has it's own date parameters in request url + POS = 4 # Parameter Only String, NOT in attributes_to_include and has it's own parameters in request url + POB = 5 # Parameter Only Bool, NOT in attributes_to_include and has it's own parameters in request url + + +SPECIAL_REPORT_OPTIONS = { + MajorReportType.PLATFORM: [(SpecialOptionType.AP, "Data_Type", ["Article", + "Book", + "Book_Segment", + "Database", + "Dataset", + "Journal", + "Multimedia", + "Newspaper_or_Newsletter", + "Other", + "Platform", + "Report", + "Repository_Item", + "Thesis_or_Dissertation"]), + (SpecialOptionType.AP, "Access_Method", ["Regular", + "TDM"]), + (SpecialOptionType.POS, "Metric_Type", ["Searches_Platform", + "Total_Item_Investigations", + "Total_Item_Requests", + "Unique_Item_Investigations", + "Unique_Item_Requests", + "Unique_Title_Investigations", + "Unique_Title_Requests"]), + (SpecialOptionType.TO, "Exclude_Monthly_Details")], + MajorReportType.DATABASE: [(SpecialOptionType.AP, "Data_Type", ["Book", + "Database", + "Journal", + "Multimedia", + "Newspaper_or_Newsletter", + "Other", + "Report", + "Repository_Item", + "Thesis_or_Dissertation"]), + (SpecialOptionType.AP, "Access_Method", ["Regular", + "TDM"]), + (SpecialOptionType.POS, "Metric_Type", ["Searches_Automated", + "Searches_Federated", + "Searches_Regular", + "Total_Item_Investigations", + "Total_Item_Requests", + "Unique_Item_Investigations", + "Unique_Item_Requests", + "Unique_Title_Investigations", + "Unique_Title_Requests", + "Limit_Exceeded", + "No_License"]), + (SpecialOptionType.TO, "Exclude_Monthly_Details")], + MajorReportType.TITLE: [(SpecialOptionType.AP, "Data_Type", ["Book", + "Journal", + "Newspaper_or_Newsletter", + "Other", + "Report", + "Thesis_or_Dissertation"]), + (SpecialOptionType.AP, "Section_Type", ["Article", + "Book", + "Chapter", + "Other", + "Section"]), + (SpecialOptionType.AP, "Access_Type", ["Controlled", + "OA_Gold"]), + (SpecialOptionType.AP, "Access_Method", ["Regular", + "TDM"]), + (SpecialOptionType.POS, "Metric_Type", ["Total_Item_Investigations", + "Total_Item_Requests", + "Unique_Item_Investigations", + "Unique_Item_Requests", + "Unique_Title_Investigations", + "Unique_Title_Requests", + "Limit_Exceeded", + "No_License"]), + (SpecialOptionType.ADP, "YOP"), + (SpecialOptionType.TO, "Exclude_Monthly_Details")], + MajorReportType.ITEM: [(SpecialOptionType.AP, "Data_Type", ["Article", + "Book", + "Book_Segment", + "Dataset", + "Journal", + "Multimedia", + "Newspaper_or_Newsletter", + "Other", + "Report", + "Repository_Item", + "Thesis_or_Dissertation"]), + (SpecialOptionType.AP, "Access_Type", ["Controlled", + "OA_Gold", + "Other_Free_To_Read"]), + (SpecialOptionType.AP, "Access_Method", ["Regular", + "TDM"]), + (SpecialOptionType.POS, "Metric_Type", ["Total_Item_Investigations", + "Total_Item_Requests", + "Unique_Item_Investigations", + "Unique_Item_Requests", + "Limit_Exceeded", + "No_License"]), + (SpecialOptionType.ADP, "YOP"), + (SpecialOptionType.AO, "Authors"), + (SpecialOptionType.AO, "Publication_Date"), + (SpecialOptionType.AO, "Article_Version"), + (SpecialOptionType.POB, "Include_Component_Details"), + (SpecialOptionType.POB, "Include_Parent_Details"), + (SpecialOptionType.TO, "Exclude_Monthly_Details")] +} + +DEFAULT_SPECIAL_OPTION_VALUE = "all" + +# If these codes are received with a Report_Header, files will be created and saved +ACCEPTABLE_CODES = [3030, + 3031, + 3032, + 3040, + 3050, + 3060, + 3062] + list(range(1, 1000)) + +# If these codes are received the retry checkbox will be checked, user can retry later +RETRY_LATER_CODES = [1010, + 1011] +RETRY_WAIT_TIME = 5 # Seconds + + +class CompletionStatus(Enum): + SUCCESSFUL = "Successful!" + WARNING = "Warning!" + FAILED = "Failed!" + CANCELLED = "Cancelled!" + +# endregion + + +# region Variable Constants for Settings +SETTINGS_FILE_DIR = "./all_data/settings/" +SETTINGS_FILE_NAME = "settings.dat" + +# Default Settings +SHOW_DEBUG_MESSAGES = False +YEARLY_DIR = "./all_data/yearly_files/" +OTHER_DIR = "./all_data/other_files/" +REQUEST_INTERVAL = 3 # Seconds +REQUEST_TIMEOUT = 120 # Seconds +CONCURRENT_VENDORS = 2 +CONCURRENT_REPORTS = 2 +USER_AGENT = "Mozilla/5.0 Firefox/73.0 Chrome/80.0.3987.132 Safari/605.1.15" +DEFAULT_CURRENCY = 'USD' +# endregion + + +# region Variable Constants for ManageVendors +VENDORS_FILE_DIR = "./all_data/vendor_manager/" +VENDORS_FILE_NAME = "vendors.dat" +VENDORS_FILE_PATH = VENDORS_FILE_DIR + VENDORS_FILE_NAME + +EXPORT_VENDORS_FILE_NAME = "exported_vendor_data.tsv" +# endregion diff --git a/Costs.py b/Costs.py new file mode 100644 index 0000000..1206d28 --- /dev/null +++ b/Costs.py @@ -0,0 +1,293 @@ +import json +from PyQt5.QtWidgets import QDialog +from PyQt5.QtGui import QStandardItemModel, QStandardItem, QFont + +import ManageDB +import ManageVendors +from Settings import SettingsModel +from ui import CostsTab, ReportTypeDialog +from Constants import * +from GeneralUtils import * + + +class CostsController: + """Controls the Costs tab + + :param costs_ui: the UI for the costs_widget + :param settings: the user's settings""" + + def __init__(self, costs_ui: CostsTab.Ui_costs_tab, settings: SettingsModel): + self.costs_ui = costs_ui + self.settings = settings + + # set parameters + self.report_parameter_combobox = costs_ui.costs_report_parameter_combobox + self.report_parameter_combobox.addItems(REPORT_TYPE_SWITCHER.keys()) + self.report_parameter = None + + self.vendor_parameter_combobox = costs_ui.costs_vendor_parameter_combobox + self.vendor_parameter = None + + self.year_parameter_dateedit = costs_ui.costs_year_parameter_dateedit + self.year_parameter_dateedit.setDate(QDate.currentDate()) + self.year_parameter = None + + self.name_label = costs_ui.costs_name_parameter_label + self.name_parameter_combobox = costs_ui.costs_name_parameter_combobox + self.name_parameter = None + + # set up values + self.cost_in_original_currency_doublespinbox = costs_ui.costs_cost_in_original_currency_doublespinbox + self.cost_in_original_currency = 0.0 + + self.original_currency_combobox = costs_ui.costs_original_currency_value_combobox + self.load_currency_list() + self.original_currency = '' + + self.cost_in_local_currency_doublespinbox = costs_ui.costs_cost_in_local_currency_doublespinbox + self.cost_in_local_currency = 0.0 + + self.cost_in_local_currency_with_tax_doublespinbox = \ + costs_ui.costs_cost_in_local_currency_with_tax_doublespinbox + self.cost_in_local_currency_with_tax = 0.0 + + # set up buttons + self.save_costs_button = costs_ui.costs_save_button + self.save_costs_button.clicked.connect(self.save_costs) + + self.load_button = costs_ui.costs_load_button + self.load_button.clicked.connect(self.load_costs) + + self.clear_button = costs_ui.costs_clear_button + self.clear_button.clicked.connect(self.clear_costs) + + vendors_json_string = read_json_file(ManageVendors.VENDORS_FILE_PATH) + vendor_dicts = json.loads(vendors_json_string) + self.vendor_parameter_combobox.clear() + self.vendor_parameter_combobox.addItems([vendor_dict[NAME_KEY] for vendor_dict in vendor_dicts]) + + self.report_parameter_combobox.currentTextChanged.connect(self.on_report_parameter_changed) + self.vendor_parameter_combobox.currentTextChanged.connect(self.on_vendor_parameter_changed) + self.year_parameter_dateedit.dateChanged.connect(self.on_year_parameter_changed) + self.name_parameter_combobox.currentTextChanged.connect(self.on_name_parameter_changed) + + self.names = [] + self.costs_names = [] + + self.on_report_parameter_changed() + self.year_parameter = int(self.year_parameter_dateedit.text()) + self.vendor_parameter = self.vendor_parameter_combobox.currentText() + self.fill_names() + + self.cost_in_original_currency_doublespinbox.valueChanged.connect(self.on_cost_in_original_currency_changed) + self.original_currency_combobox.currentTextChanged.connect(self.on_original_currency_changed) + self.cost_in_local_currency_doublespinbox.valueChanged.connect(self.on_cost_in_local_currency_changed) + self.cost_in_local_currency_with_tax_doublespinbox.valueChanged.connect( + self.on_cost_in_local_currency_with_tax_changed) + + self.import_costs_button = costs_ui.costs_import_costs_button + self.import_costs_button.clicked.connect(self.import_costs) + + def update_settings(self, settings: SettingsModel): + """Invoked when the settings are saved + + :param settings: the new settings""" + self.settings = settings + self.load_currency_list() + + def database_updated(self, code: int): + """Invoked when the database is updated + + :param code: the exit code of the update""" + self.fill_names() + + def load_vendor_list(self, vendors: Sequence[ManageVendors.Vendor]): + """Updates the vendor list combobox + + :param vendors: the new list of vendors""" + self.vendor_parameter_combobox.clear() + self.vendor_parameter_combobox.addItems([vendor.name for vendor in vendors]) + + def load_currency_list(self): + """Updates the original currency combobox""" + self.original_currency_combobox.clear() + self.original_currency_combobox.addItem(self.settings.default_currency) + self.original_currency_combobox.addItems([currency for currency in CURRENCY_LIST if currency != + self.settings.default_currency]) + self.original_currency_combobox.setCurrentText('') + + def on_report_parameter_changed(self): + """Invoked when the report parameter changes""" + self.report_parameter = self.report_parameter_combobox.currentText() + self.name_label.setText(NAME_FIELD_SWITCHER[self.report_parameter].capitalize()) + if self.vendor_parameter: + self.fill_names() + + def on_vendor_parameter_changed(self): + """Invoked when the vendor parameter changes""" + self.vendor_parameter = self.vendor_parameter_combobox.currentText() + if self.report_parameter: + self.fill_names() + + def on_year_parameter_changed(self): + """Invoked when the year parameter changes""" + self.year_parameter = int(self.year_parameter_dateedit.text()) + self.load_costs() + self.fill_names(True) + + def fill_names(self, only_get_costs_names: bool = False): + """Fills the name field combobox""" + self.name_parameter_combobox.clear() + + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + if not only_get_costs_names: + names_sql_text, names_data = ManageDB.get_names_sql_text(self.report_parameter, self.vendor_parameter) + names_results = ManageDB.run_select_sql(connection, names_sql_text, names_data) + if names_results: + self.names = [result[0] for result in names_results] + else: + self.names = [] + if self.settings.show_debug_messages: print(names_results) + + costs_sql_text, costs_data = ManageDB.get_names_with_costs_sql_text(self.report_parameter, + self.vendor_parameter, + self.year_parameter, + self.year_parameter) + costs_results = ManageDB.run_select_sql(connection, costs_sql_text, costs_data) + connection.close() + + if costs_results: + self.costs_names = [result[0] for result in costs_results] + else: + self.costs_names = [] + if self.settings.show_debug_messages: print(costs_results) + model = QStandardItemModel() + for name in self.names: + item = QStandardItem(name) + if name in self.costs_names: + font = QFont() + font.setBold(True) + item.setFont(font) + model.appendRow(item) + self.name_parameter_combobox.setModel(model) + else: + print('Error, no connection') + + def on_name_parameter_changed(self): + """Invoked when the name field parameter changes""" + self.name_parameter = self.name_parameter_combobox.currentText() + enable = False + if self.name_parameter: + enable = True + self.load_costs() + self.cost_in_original_currency_doublespinbox.setEnabled(enable) + self.original_currency_combobox.setEnabled(enable) + self.cost_in_local_currency_doublespinbox.setEnabled(enable) + self.cost_in_local_currency_with_tax_doublespinbox.setEnabled(enable) + + def on_cost_in_original_currency_changed(self): + """Invoked when the cost in original currency parameter changes""" + self.cost_in_original_currency = self.cost_in_original_currency_doublespinbox.value() + + def on_original_currency_changed(self): + """Invoked when the original currency parameter changes""" + self.original_currency = self.original_currency_combobox.currentText() + + def on_cost_in_local_currency_changed(self): + """Invoked when the cost in local currency parameter changes""" + self.cost_in_local_currency = self.cost_in_local_currency_doublespinbox.value() + + def on_cost_in_local_currency_with_tax_changed(self): + """Invoked when the cost in local currency with tax parameter changes""" + self.cost_in_local_currency_with_tax = self.cost_in_local_currency_with_tax_doublespinbox.value() + + def save_costs(self): + """Saves the cost data: if it is nonzero, add it to the database; if it is zero, delete it from the database""" + INSERT = 'insert' + DELETE = 'delete' + insert_or_delete = None + sql_text = None + data = None + if self.cost_in_original_currency > 0 and self.original_currency != '' \ + and self.cost_in_local_currency > 0 and self.cost_in_local_currency_with_tax > 0: + insert_or_delete = INSERT + elif self.cost_in_original_currency == 0 and self.original_currency == '' \ + and self.cost_in_local_currency == 0 and self.cost_in_local_currency_with_tax == 0: + insert_or_delete = DELETE + if insert_or_delete == INSERT: + sql_text, data = ManageDB.replace_costs_sql_text(self.report_parameter, + ({NAME_FIELD_SWITCHER[self.report_parameter]: + self.name_parameter, + 'vendor': self.vendor_parameter, + 'year': self.year_parameter, + 'cost_in_original_currency': + self.cost_in_original_currency, + 'original_currency': self.original_currency, + 'cost_in_local_currency': self.cost_in_local_currency, + 'cost_in_local_currency_with_tax': + self.cost_in_local_currency_with_tax},)) + elif insert_or_delete == DELETE: + sql_text, data = ManageDB.delete_costs_sql_text(self.report_parameter, self.vendor_parameter, + self.year_parameter, self.name_parameter) + if insert_or_delete in (INSERT, DELETE): + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + ManageDB.run_sql(connection, sql_text, data) + connection.close() + ManageDB.backup_costs_data(self.report_parameter) + if insert_or_delete == INSERT: + show_message('Data inserted/replaced') + elif insert_or_delete == DELETE: + show_message('Data removed') + else: + show_message('Invalid entry') + + def load_costs(self): + """Fills the costs fields with data from the database""" + sql_text, data = ManageDB.get_costs_sql_text(self.report_parameter, self.vendor_parameter, self.year_parameter, + self.name_parameter) + results = [] + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + results = ManageDB.run_select_sql(connection, sql_text, data) + if not results: + results.append((0.0, '', 0.0, 0.0)) + connection.close() + values = {} + index = 0 + for field in COST_FIELDS: + values[field[NAME_KEY]] = results[0][index] + index += 1 + self.cost_in_original_currency_doublespinbox.setValue(values['cost_in_original_currency']) + self.original_currency_combobox.setCurrentText(values['original_currency']) + self.cost_in_local_currency_doublespinbox.setValue(values['cost_in_local_currency']) + self.cost_in_local_currency_with_tax_doublespinbox.setValue(values['cost_in_local_currency_with_tax']) + self.cost_in_original_currency_doublespinbox.repaint() + + def clear_costs(self): + """Empties the costs fields""" + self.cost_in_original_currency_doublespinbox.setValue(0.0) + self.original_currency_combobox.setCurrentText('') + self.cost_in_local_currency_doublespinbox.setValue(0.0) + self.cost_in_local_currency_with_tax_doublespinbox.setValue(0.0) + + def import_costs(self): + """Import a file with costs data in it into the database""" + report_type_dialog = QDialog() + report_type_dialog_ui = ReportTypeDialog.Ui_report_type_dialog() + report_type_dialog_ui.setupUi(report_type_dialog) + report_type_dialog_ui.report_type_combobox.addItems(REPORT_TYPE_SWITCHER.keys()) + report_type_dialog.show() + if report_type_dialog.exec_(): + report_type = report_type_dialog_ui.report_type_combobox.currentText() + if report_type != '': + file_name = choose_file(TSV_FILTER + CSV_FILTER) + if file_name != '': + ManageDB.insert_single_cost_file(report_type, file_name) + ManageDB.backup_costs_data(report_type) + show_message('File ' + file_name + ' imported') + else: + print('Error, no file location selected') + else: + print('Error, no report type selected') diff --git a/DataStorage.py b/DataStorage.py new file mode 100644 index 0000000..a5df158 --- /dev/null +++ b/DataStorage.py @@ -0,0 +1,25 @@ +import os + + +def save_json_file(file_dir: str, file_name: str, json_string: str): + try: + if not os.path.isdir(file_dir): + os.makedirs(file_dir) + file = open(file_dir + file_name, 'w') + file.write(json_string) + file.close() + except IOError as e: + print(e) + + +def read_json_file(file_path: str): + json_string = "[]" + try: + file = open(file_path, 'r') + json_string = file.read() + file.close() + except IOError as e: + print(e) + finally: + return json_string + diff --git a/FetchData.py b/FetchData.py new file mode 100644 index 0000000..1d3aaf6 --- /dev/null +++ b/FetchData.py @@ -0,0 +1,3067 @@ +"""This module handles all operations involving fetching reports. + +The process of fetching reports is made up of these steps: + +1. Each vendor is queried for its supported reports using the SUSHI API +2. The vendor is then queried for each supported report, also using the SUSHI API +3. The raw JSON response is converted to JsonModel objects that make it easier to work with the JSON data. +4. The model objects are then used to create create row objects that will be in the final TSV file. +5. The row objects are then sorted by their primary columns, for example, item reports are sorted by the item column. +6. The sorted rows are then used to create and save a final TSV report file that adheres to the COUNTER 5 standards. +7. After all reports are processed, the database is updated with the new data + +.. NOTE:: + All fetch operations are multi-threaded. Each vendor has it's own thread, each report for that vendor + also has it's own thread. The maximum concurrent vendors and reports (per vendor) can be changed in the settings tab + on the GUI +""" + +from os import path, makedirs +import csv +import json +import requests +import platform +import copy +import ctypes + +from PyQt5.QtCore import QObject, QThread, pyqtSignal, QDate, Qt +from PyQt5.QtGui import QStandardItemModel, QStandardItem +from PyQt5.QtWidgets import QPushButton, QDialog, QWidget, QProgressBar, QLabel, QVBoxLayout, QDialogButtonBox, \ + QCheckBox, QDateEdit, QFrame, QHBoxLayout, QSizePolicy, QLineEdit, QListView, QRadioButton, QButtonGroup + +import GeneralUtils +from ui import FetchReportsTab, FetchSpecialReportsTab, FetchProgressDialog, ReportResultWidget, VendorResultsWidget +from GeneralUtils import JsonModel +from ManageVendors import Vendor +from Settings import SettingsModel +from ManageDB import UpdateDatabaseWorker +from Constants import * + + +# region Models +class SupportedReportModel(JsonModel): + """Models a SUSHI Supported Report""" + def __init__(self, report_id: str): + self.report_id = report_id + + @classmethod + def from_json(cls, json_dict: dict): + report_id = str(json_dict["Report_ID"]).upper() if "Report_ID" in json_dict else "" + + return cls(report_id) + + +class PeriodModel(JsonModel): + """Models a SUSHI Period""" + def __init__(self, begin_date: str, end_date: str): + self.begin_date = begin_date + self.end_date = end_date + + @classmethod + def from_json(cls, json_dict: dict): + begin_date = json_dict["Begin_Date"] if "Begin_Date" in json_dict else "" + end_date = json_dict["End_Date"] if "End_Date" in json_dict else "" + + return cls(begin_date, end_date) + + +class InstanceModel(JsonModel): + """Models a SUSHI Instance""" + def __init__(self, metric_type: str, count: int): + self.metric_type = metric_type + self.count = count + + @classmethod + def from_json(cls, json_dict: dict): + metric_type = json_dict["Metric_Type"] if "Metric_Type" in json_dict else "" + count = int(json_dict["Count"]) if "Count" in json_dict else 0 + + return cls(metric_type, count) + + +class PerformanceModel(JsonModel): + """Models a SUSHI Performance""" + def __init__(self, period: PeriodModel, instances: list): + self.period = period + self.instances = instances + + @classmethod + def from_json(cls, json_dict: dict): + period = PeriodModel.from_json(json_dict["Period"]) if "Period" in json_dict else None + + instances = get_models("Instance", InstanceModel, json_dict) + + return cls(period, instances) + + +class TypeValueModel(JsonModel): + """Models SUSHI models that are made up of Type and value""" + def __init__(self, item_type: str, value: str): + self.item_type = item_type + self.value = value + + @classmethod + def from_json(cls, json_dict: dict): + item_type = str(json_dict["Type"]) if "Type" in json_dict else "" + value = str(json_dict["Value"]) if "Value" in json_dict else "" + + return cls(item_type, value) + + +class NameValueModel(JsonModel): + """Models SUSHI models that are made up of Name and value""" + def __init__(self, name: str, value: str): + self.name = name + self.value = value + + @classmethod + def from_json(cls, json_dict: dict): + name = str(json_dict["Name"]) if "Name" in json_dict else "" + value = str(json_dict["Value"]) if "Value" in json_dict else "" + + return cls(name, value) + + +class ExceptionModel(JsonModel): + """Models a SUSHI Exception""" + def __init__(self, code: int, message: str, severity: str, data: str): + self.code = code + self.message = message + self.severity = severity + self.data = data + + @classmethod + def from_json(cls, json_dict: dict): + code = int(json_dict["Code"]) if "Code" in json_dict else 0 + message = json_dict["Message"] if "Message" in json_dict else "" + severity = json_dict["Severity"] if "Severity" in json_dict else "" + data = json_dict["Data"] if "Data" in json_dict else "" + + return cls(code, message, severity, data) + + +class ReportHeaderModel(JsonModel): + """Models a SUSHI Report Header""" + def __init__(self, report_name: str, report_id: str, release: str, institution_name: str, institution_ids: list, + report_filters: list, report_attributes: list, exceptions: list, created: str, created_by: str): + self.report_name = report_name + self.report_id = report_id + self.release = release + self.institution_name = institution_name + self.institution_ids = institution_ids + self.report_filters = report_filters + self.report_attributes = report_attributes + self.exceptions = exceptions + self.created = created + self.created_by = created_by + + # Not part of JSON + self.major_report_type = None + + @classmethod + def from_json(cls, json_dict: dict): + report_name = json_dict["Report_Name"] if "Report_Name" in json_dict else "" + report_id = str(json_dict["Report_ID"]).upper() if "Report_ID" in json_dict else "" + release = json_dict["Release"] if "Release" in json_dict else "" + institution_name = json_dict["Institution_Name"] if "Institution_Name" in json_dict else "" + created = json_dict["Created"] if "Created" in json_dict else "" + created_by = json_dict["Created_By"] if "Created_By" in json_dict else "" + + institution_ids = get_models("Institution_ID", TypeValueModel, json_dict) + report_filters = get_models("Report_Filters", NameValueModel, json_dict) + report_attributes = get_models("Report_Attributes", NameValueModel, json_dict) + exceptions = get_models("Exceptions", ExceptionModel, json_dict) + + return cls(report_name, report_id, release, institution_name, institution_ids, report_filters, + report_attributes, exceptions, created, created_by) + + +class ReportModel(JsonModel): + """Models a SUSHI Report""" + def __init__(self, report_header: ReportHeaderModel, report_items: list): + self.report_header = report_header + self.report_items = report_items + + # Not part of JSON + self.exceptions = [] + + @classmethod + def from_json(cls, json_dict: dict): + exceptions = ReportModel.process_exceptions(json_dict) + + report_header = ReportHeaderModel.from_json(json_dict["Report_Header"]) + report_type = report_header.report_id + major_report_type = get_major_report_type(report_type) + report_header.major_report_type = major_report_type + + report_items = [] + if "Report_Items" in json_dict: + report_item_dicts = json_dict["Report_Items"] + if len(report_item_dicts) > 0: + if major_report_type == MajorReportType.PLATFORM: + for report_item_dict in report_item_dicts: + report_items.append(PlatformReportItemModel.from_json(report_item_dict)) + + elif major_report_type == MajorReportType.DATABASE: + for report_item_dict in report_item_dicts: + report_items.append(DatabaseReportItemModel.from_json(report_item_dict)) + + elif major_report_type == MajorReportType.TITLE: + for report_item_dict in report_item_dicts: + report_items.append(TitleReportItemModel.from_json(report_item_dict)) + + elif major_report_type == MajorReportType.ITEM: + for report_item_dict in report_item_dicts: + report_items.append(ItemReportItemModel.from_json(report_item_dict)) + + report_model = cls(report_header, report_items) + report_model.exceptions = exceptions + return report_model + + @classmethod + def process_exceptions(cls, json_dict: dict) -> list: + """Gets all exception models in a JSON dict, returns them as a list + + :param json_dict: A JSON dict + :raises ReportHeaderMissingException: When the report header is missing + :raises RetryLaterException: When a retry later exception model is received + :raises UnacceptableCodeException: When the report cannot be processed based on the exception code + """ + exceptions = [] + + if "Exception" in json_dict: + exceptions.append(ExceptionModel.from_json(json_dict["Exception"])) + + code = int(json_dict["Code"]) if "Code" in json_dict else "" + message = json_dict["Message"] if "Message" in json_dict else "" + data = json_dict["Data"] if "Data" in json_dict else "" + severity = json_dict["Severity"] if "Severity" in json_dict else "" + if code: + exceptions.append(ExceptionModel(code, message, severity, data)) + + if "Report_Header" in json_dict: + report_header = ReportHeaderModel.from_json(json_dict["Report_Header"]) + if len(report_header.exceptions) > 0: + for exception in report_header.exceptions: + exceptions.append(exception) + else: + raise ReportHeaderMissingException(exceptions) + + for exception in exceptions: + if exception.code in RETRY_LATER_CODES: + raise RetryLaterException(exceptions) + elif exception.code not in ACCEPTABLE_CODES: + raise UnacceptableCodeException(exceptions) + + return exceptions + + +class PlatformReportItemModel(JsonModel): + """Models a SUSHI Platform Report Item""" + def __init__(self, platform: str, data_type: str, access_method: str, performances: list): + self.platform = platform + self.data_type = data_type + self.access_method = access_method + self.performances = performances + + @classmethod + def from_json(cls, json_dict: dict): + platform = json_dict["Platform"] if "Platform" in json_dict else "" + data_type = json_dict["Data_Type"] if "Data_Type" in json_dict else "" + access_method = json_dict["Access_Method"] if "Access_Method" in json_dict else "" + + performances = get_models("Performance", PerformanceModel, json_dict) + + return cls(platform, data_type, access_method, performances) + + +class DatabaseReportItemModel(JsonModel): + """Models a SUSHI Database Report Item""" + def __init__(self, database: str, publisher: str, item_ids: list, publisher_ids: list, platform: str, + data_type: str, access_method: str, + performances: list): + self.database = database + self.publisher = publisher + self.item_ids = item_ids + self.publisher_ids = publisher_ids + self.platform = platform + self.data_type = data_type + self.access_method = access_method + self.performances = performances + + @classmethod + def from_json(cls, json_dict: dict): + database = json_dict["Database"] if "Database" in json_dict else "" + publisher = json_dict["Publisher"] if "Publisher" in json_dict else "" + platform = json_dict["Platform"] if "Platform" in json_dict else "" + data_type = json_dict["Data_Type"] if "Data_Type" in json_dict else "" + access_method = json_dict["Access_Method"] if "Access_Method" in json_dict else "" + + item_ids = get_models("Item_ID", TypeValueModel, json_dict) + publisher_ids = get_models("Publisher_ID", TypeValueModel, json_dict) + performances = get_models("Performance", PerformanceModel, json_dict) + + return cls(database, publisher, item_ids, publisher_ids, platform, data_type, access_method, performances) + + +class TitleReportItemModel(JsonModel): + """Models a SUSHI Title Report Item""" + def __init__(self, title: str, item_ids: list, platform: str, publisher: str, publisher_ids: list, data_type: str, + section_type: str, yop: str, access_type: str, access_method: str, performances: list): + self.title = title + self.item_ids = item_ids + self.platform = platform + self.publisher = publisher + self.publisher_ids = publisher_ids + self.data_type = data_type + self.section_type = section_type + self.yop = yop # Year of publication + self.access_type = access_type + self.access_method = access_method + self.performances = performances + + @classmethod + def from_json(cls, json_dict: dict): + title = json_dict["Title"] if "Title" in json_dict else "" + platform = json_dict["Platform"] if "Platform" in json_dict else "" + publisher = json_dict["Publisher"] if "Publisher" in json_dict else "" + data_type = json_dict["Data_Type"] if "Data_Type" in json_dict else "" + section_type = json_dict["Section_Type"] if "Section_Type" in json_dict else "" + yop = json_dict["YOP"] if "YOP" in json_dict else "" + access_type = json_dict["Access_Type"] if "Access_Type" in json_dict else "" + access_method = json_dict["Access_Method"] if "Access_Method" in json_dict else "" + + item_ids = get_models("Item_ID", TypeValueModel, json_dict) + publisher_ids = get_models("Publisher_ID", TypeValueModel, json_dict) + performances = get_models("Performance", PerformanceModel, json_dict) + + return cls(title, item_ids, platform, publisher, publisher_ids, data_type, section_type, yop, access_type, + access_method, performances) + + +class ItemContributorModel(JsonModel): + """Models a SUSHI Item Contributor""" + def __init__(self, item_type: str, name: str, identifier: str): + self.item_type = item_type + self.name = name + self.identifier = identifier + + @classmethod + def from_json(cls, json_dict: dict): + item_type = json_dict["Type"] if "Type" in json_dict else "" + name = json_dict["Name"] if "Name" in json_dict else "" + identifier = json_dict["Identifier"] if "Identifier" in json_dict else "" + + return cls(item_type, name, identifier) + + +class ItemParentModel(JsonModel): + """Models a SUSHI Item Parent""" + def __init__(self, item_name: str, item_ids: list, item_contributors: list, item_dates: list, item_attributes: list, + data_type: str): + self.item_name = item_name + self.item_ids = item_ids + self.item_contributors = item_contributors + self.item_dates = item_dates + self.item_attributes = item_attributes + self.data_type = data_type + + @classmethod + def from_json(cls, json_dict: dict): + item_name = json_dict["Item_Name"] if "Item_Name" in json_dict else "" + data_type = json_dict["Data_Type"] if "Data_Type" in json_dict else "" + + item_ids = get_models("Item_ID", TypeValueModel, json_dict) + item_contributors = get_models("Item_Contributors", ItemContributorModel, json_dict) + item_dates = get_models("Item_Dates", TypeValueModel, json_dict) + item_attributes = get_models("Item_Attributes", TypeValueModel, json_dict) + + return cls(item_name, item_ids, item_contributors, item_dates, item_attributes, data_type) + + +class ItemComponentModel(JsonModel): + """Models a SUSHI Item Component""" + def __init__(self, item_name: str, item_ids: list, item_contributors: list, item_dates: list, item_attributes: list, + data_type: str, performances: list): + self.item_name = item_name + self.item_ids = item_ids + self.item_contributors = item_contributors + self.item_dates = item_dates + self.item_attributes = item_attributes + self.data_type = data_type + self.performances = performances + + @classmethod + def from_json(cls, json_dict: dict): + item_name = json_dict["Item_Name"] if "Item_Name" in json_dict else "" + data_type = json_dict["Data_Type"] if "Data_Type" in json_dict else "" + + item_ids = get_models("Item_ID", TypeValueModel, json_dict) + item_contributors = get_models("Item_Contributors", ItemContributorModel, json_dict) + item_dates = get_models("Item_Dates", TypeValueModel, json_dict) + item_attributes = get_models("Item_Attributes", TypeValueModel, json_dict) + performances = get_models("Performance", PerformanceModel, json_dict) + + return cls(item_name, item_ids, item_contributors, item_dates, item_attributes, data_type, performances) + + +class ItemReportItemModel(JsonModel): + """Models a SUSHI Item Report model""" + def __init__(self, item: str, item_ids: list, item_contributors: list, item_dates: list, item_attributes: list, + platform: str, publisher: str, publisher_ids: list, item_parent: ItemParentModel, + item_components: list, data_type: str, yop: str, access_type: str, + access_method: str, performances: list): + self.item = item + self.item_ids = item_ids + self.item_contributors = item_contributors + self.item_dates = item_dates + self.item_attributes = item_attributes + self.platform = platform + self.publisher = publisher + self.publisher_ids = publisher_ids + self.item_parent = item_parent + self.item_components = item_components + self.data_type = data_type + self.yop = yop # Year of publication + self.access_type = access_type + self.access_method = access_method + self.performances = performances + + @classmethod + def from_json(cls, json_dict: dict): + item = json_dict["Item"] if "Item" in json_dict else "" + platform = json_dict["Platform"] if "Platform" in json_dict else "" + publisher = json_dict["Publisher"] if "Publisher" in json_dict else "" + data_type = json_dict["Data_Type"] if "Data_Type" in json_dict else "" + yop = json_dict["YOP"] if "YOP" in json_dict else "" + access_type = json_dict["Access_Type"] if "Access_Type" in json_dict else "" + access_method = json_dict["Access_Method"] if "Access_Method" in json_dict else "" + + item_parent = ItemParentModel.from_json(json_dict["Item_Parent"]) if "Item_Parent" in json_dict else None + + item_ids = get_models("Item_ID", TypeValueModel, json_dict) + item_contributors = get_models("Item_Contributors", ItemContributorModel, json_dict) + item_dates = get_models("Item_Dates", TypeValueModel, json_dict) + item_attributes = get_models("Item_Attributes", TypeValueModel, json_dict) + publisher_ids = get_models("Publisher_ID", TypeValueModel, json_dict) + item_components = get_models("Item_Component", ItemComponentModel, json_dict) + performances = get_models("Performance", PerformanceModel, json_dict) + + return cls(item, item_ids, item_contributors, item_dates, item_attributes, platform, publisher, publisher_ids, + item_parent, item_components, data_type, yop, access_type, access_method, performances) + + +# endregion + + +# region Custom Exceptions +class RetryLaterException(Exception): + """An exception raised when a retry later exception code is received in an exception model""" + def __init__(self, exceptions: list): + self.exceptions = exceptions + + +class ReportHeaderMissingException(Exception): + """An exception raised when a report header is missing from a report""" + def __init__(self, exceptions: list): + self.exceptions = exceptions + + +class UnacceptableCodeException(Exception): + """An exception raised when a report cannot be processed based on an exception code""" + def __init__(self, exceptions: list): + self.exceptions = exceptions +# endregion + + +def exception_models_to_message(exceptions: list) -> str: + """Formats a list of exception models into a single string """ + message = "" + for exception in exceptions: + if message: message += "\n\n" + message += f"Code: {exception.code}" \ + f"\nMessage: {exception.message}" \ + f"\nSeverity: {exception.severity}" \ + f"\nData: {exception.data}" + + return message + + +def get_models(model_key: str, model_type, json_dict: dict) -> list: + """This converts json lists into a list of the specified SUSHI model type + + :param model_key: The target key to get the list of JSONObjects + :param model_type: The target model type, e.g PerformanceModel + :param json_dict: The JSON dict to get the list from + """ + # Some vendors sometimes return a single dict even when the standard specifies a list, + # we need to check for that + models = [] + if model_key in json_dict and json_dict[model_key] is not None: + if type(json_dict[model_key]) is list: + model_dicts = json_dict[model_key] + for model_dict in model_dicts: + models.append(model_type.from_json(model_dict)) + + elif type(json_dict[model_key]) is dict: + models.append(model_type.from_json(json_dict[model_key])) + + return models + + +def get_major_report_type(report_type: str) -> MajorReportType: + """Returns a major report type that a report type falls under""" + if report_type == "PR" or report_type == "PR_P1": + return MajorReportType.PLATFORM + + elif report_type == "DR" or report_type == "DR_D1" or report_type == "DR_D2": + return MajorReportType.DATABASE + + elif report_type == "TR" or report_type == "TR_B1" or report_type == "TR_B2" \ + or report_type == "TR_B3" or report_type == "TR_J1" or report_type == "TR_J2" \ + or report_type == "TR_J3" or report_type == "TR_J4": + return MajorReportType.TITLE + + elif report_type == "IR" or report_type == "IR_A1" or report_type == "IR_M1": + return MajorReportType.ITEM + + +def get_month_years(begin_date: QDate, end_date: QDate) -> list: + """Returns a list of month-year (MMM-yyyy) strings within a date range""" + month_years = [] + if begin_date.year() == end_date.year(): + num_months = (end_date.month() - begin_date.month()) + 1 + else: + num_months = (12 - begin_date.month() + end_date.month()) + 1 + num_years = end_date.year() - begin_date.year() + num_months += (num_years - 1) * 12 + + for i in range(num_months): + month_years.append(begin_date.addMonths(i).toString("MMM-yyyy")) + + return month_years + + +class SpecialReportOptions: + """This holds all the parameters that are used to process a special report + + The options are stored as tuples, (option has non-default value, option type, option name, list of option values) + """ + def __init__(self): + # PR, DR, TR, IR + self.data_type = False, SpecialOptionType.AP, "Data_Type", [DEFAULT_SPECIAL_OPTION_VALUE] + self.access_method = False, SpecialOptionType.AP, "Access_Method", [DEFAULT_SPECIAL_OPTION_VALUE] + self.metric_type = False, SpecialOptionType.POS, "Metric_Type", [DEFAULT_SPECIAL_OPTION_VALUE] + self.exclude_monthly_details = False, SpecialOptionType.TO, None, None + # TR, IR + current_date = QDate.currentDate() + self.yop = False, SpecialOptionType.ADP, "YOP", [DEFAULT_SPECIAL_OPTION_VALUE] + self.access_type = False, SpecialOptionType.AP, "Access_Type", [DEFAULT_SPECIAL_OPTION_VALUE] + # TR + self.section_type = False, SpecialOptionType.AP, "Section_Type", [DEFAULT_SPECIAL_OPTION_VALUE] + # IR + self.authors = False, SpecialOptionType.AO, "Authors", [DEFAULT_SPECIAL_OPTION_VALUE] + self.publication_date = False, SpecialOptionType.AO, "Publication_Date", [DEFAULT_SPECIAL_OPTION_VALUE] + self.article_version = False, SpecialOptionType.AO, "Article_Version", [DEFAULT_SPECIAL_OPTION_VALUE] + self.include_component_details = False, SpecialOptionType.POB, None, None + self.include_parent_details = False, SpecialOptionType.POB, None, None + + +class ReportRow: + """This models a row in the generated report, it contains every possible column + + :param begin_date: The begin date of the request, used to populate the month columns in the report + :param end_date: The end date of the request, used to populate the month columns in the report + """ + def __init__(self, begin_date: QDate, end_date: QDate): + self.database = "" + self.title = "" + self.item = "" + self.publisher = "" + self.publisher_id = "" + self.platform = "" + self.authors = "" + self.publication_date = "" + self.article_version = "" + self.doi = "" + self.proprietary_id = "" + self.online_issn = "" + self.print_issn = "" + self.linking_issn = "" + self.isbn = "" + self.uri = "" + self.parent_title = "" + self.parent_authors = "" + self.parent_publication_date = "" + self.parent_article_version = "" + self.parent_data_type = "" + self.parent_doi = "" + self.parent_proprietary_id = "" + self.parent_online_issn = "" + self.parent_print_issn = "" + self.parent_linking_issn = "" + self.parent_isbn = "" + self.parent_uri = "" + self.component_title = "" + self.component_authors = "" + self.component_publication_date = "" + self.component_data_type = "" + self.component_doi = "" + self.component_proprietary_id = "" + self.component_online_issn = "" + self.component_print_issn = "" + self.component_linking_issn = "" + self.component_isbn = "" + self.component_uri = "" + self.data_type = "" + self.section_type = "" + self.yop = "" + self.access_type = "" + self.access_method = "" + self.metric_type = "" + self.total_count = 0 + + self.month_counts = {} + + # This only works with 12 months + # for i in range(12): + # curr_date: QDate + # if QDate(begin_date.year(), i + 1, 1) < begin_date: + # curr_date = QDate(end_date.year(), i + 1, 1) + # else: + # curr_date = QDate(begin_date.year(), i + 1, 1) + # + # self.month_counts[curr_date.toString("MMM-yyyy")] = 0 + # + # self.total_count = 0 + + # This works with more than 12 months + for month_year_str in get_month_years(begin_date, end_date): + self.month_counts[month_year_str] = 0 + + +class RequestData: + """This holds the data about a report request + + :param vendor: The vendor being processed + :param target_report_types: The report types to attempt to fetch + :param begin_date: The begin date to specify in the request + :param end_date: The end date to specify in the request + :param save_location: Where the generated report should be saved + :param settings: The system's settings object + :param special_options: Special options if fetching a special report + """ + def __init__(self, vendor: Vendor, target_report_types: list, begin_date: QDate, end_date: QDate, + save_location: str, settings: SettingsModel, special_options: SpecialReportOptions = None): + self.vendor = vendor + self.target_report_types = target_report_types + self.begin_date = begin_date + self.end_date = end_date + self.save_location = save_location + self.settings = settings + self.special_options = special_options + + +class ProcessResult: + """This holds the results of an fetch process + + :param vendor: The target vendor + :param report_type: The target report type + """ + def __init__(self, vendor: Vendor, report_type: str = None): + self.vendor = vendor + self.report_type = report_type + self.completion_status = CompletionStatus.SUCCESSFUL + self.message = "" + self.retry = False + self.file_name = "" + self.file_dir = "" + self.file_path = "" + self.protected_file_path = "" + self.year = "" + + +class FetchReportsAbstract: + def __init__(self, vendors: list, settings: SettingsModel, widget: QWidget): + """This contains common functionality shared between classes that fetch reports + + :param vendors: The list of vendors in the system + :param settings: The system's user settings + :param widget: The widget of the tab that this class controls + """ + + # region General + self.widget = widget + self.vendors = [] + self.update_vendors(vendors) + self.selected_data = [] # List of ReportData Objects + self.retry_data = [] # List of (Vendor, list[report_types])> + self.vendor_workers = {} # + self.started_processes = 0 + self.completed_processes = 0 + self.total_processes = 0 + self.begin_date = QDate() + self.end_date = QDate() + self.selected_options = None + self.save_dir = "" + self.is_cancelling = False + self.is_yearly_fetch = False + self.settings = settings + self.database_report_data = [] + # endregion + + # region Fetch Progress Dialog + self.fetch_progress_dialog: QDialog = None + self.progress_bar: QProgressBar = None + self.status_label: QLabel = None + self.scroll_contents: QWidget = None + self.scroll_layout: QVBoxLayout = None + self.ok_button: QPushButton = None + self.retry_button: QPushButton = None + self.cancel_button: QPushButton = None + + self.vendor_result_widgets = {} # + # endregion + + # region Update Database Dialog + self.is_updating_database = False + self.add_to_database = True + self.database_thread = None + self.database_worker = None + + # endregion + + def on_vendors_changed(self, vendors: list): + """Handles the signal emitted when the system's vendor list is updated + + :param vendors: An updated list of the system's vendors + """ + self.update_vendors(vendors) + self.update_vendors_ui() + + def update_vendors(self, vendors: list): + """ Updates the local copy of vendors that support report fetching (SUSHI) + + :param vendors: A list of vendors + """ + self.vendors = [] + for vendor in vendors: + if vendor.is_non_sushi: continue + self.vendors.append(vendor) + + def update_vendors_ui(self): + """Updates the UI to show vendors that support report fetching (SUSHI)""" + raise NotImplementedError() + + def fetch_vendor_data(self, request_data: RequestData): + """Initiates the process to fetch reports from a vendor + + This creates a new thread to work on this vendor + + :param request_data: The request data for this vendor request + """ + worker_id = request_data.vendor.name + if worker_id in self.vendor_workers: return # Avoid processing a vendor twice + + vendor_worker = VendorWorker(worker_id, request_data) + vendor_worker.worker_finished_signal.connect(self.on_vendor_worker_finished) + vendor_thread = QThread() + self.vendor_workers[worker_id] = vendor_worker, vendor_thread + vendor_worker.moveToThread(vendor_thread) + vendor_thread.started.connect(vendor_worker.work) + vendor_thread.finished.connect(vendor_thread.deleteLater) + + vendor_thread.start() + + if self.settings.show_debug_messages: print(f"{worker_id}: Added a process, total processes: {self.total_processes}") + self.update_results_ui(request_data.vendor) + + def update_results_ui(self, vendor: Vendor, vendor_result: ProcessResult = None, report_results: list = None): + """Updates the fetch progress dialog to show results + + :param vendor: The vendor being updated + :param vendor_result: The result of the vendor + :param report_results: The results of the vendor's reports + """ + self.progress_bar.setValue(int((self.completed_processes / self.total_processes) * 100)) + if not self.is_cancelling and self.completed_processes != self.total_processes: + self.status_label.setText(f"Vendor progress: {self.completed_processes}/{self.total_processes}") + + if vendor.name in self.vendor_result_widgets: + vendor_results_widget, vendor_results_ui = self.vendor_result_widgets[vendor.name] + vertical_layout = vendor_results_ui.results_frame.layout() + status_label = vendor_results_ui.status_label + else: + vendor_results_widget = QWidget(self.scroll_contents) + vendor_results_ui = VendorResultsWidget.Ui_VendorResultsWidget() + vendor_results_ui.setupUi(vendor_results_widget) + vendor_results_ui.vendor_label.setText(vendor.name) + vertical_layout = vendor_results_ui.results_frame.layout() + + status_label = vendor_results_ui.status_label + frame = vendor_results_ui.results_frame + expand_button = vendor_results_ui.expand_button + collapse_button = vendor_results_ui.collapse_button + + status_label.setText("Working...") + frame.hide() + expand_button.clicked.connect(lambda: frame.show()) + collapse_button.clicked.connect(lambda: frame.hide()) + + self.vendor_result_widgets[vendor.name] = vendor_results_widget, vendor_results_ui + self.scroll_layout.addWidget(vendor_results_widget) + + if vendor_result is None: return + + status_label.setText("Done") + result_widget = self.get_result_widget(vendor, vendor_results_widget, vendor_result) + vertical_layout.addWidget(result_widget) + + for report_result in report_results: + result_widget = self.get_result_widget(vendor, vendor_results_widget, report_result) + vertical_layout.addWidget(result_widget) + + def get_result_widget(self, vendor: Vendor, vendor_widget: QWidget, process_result: ProcessResult) -> QWidget: + """This creates a result widget for either a vendor or a vendor's report + + :param vendor: The target vendor + :param vendor_widget: The vendor's widget in the fetch progress dialog + :param process_result: The result to show + """ + completion_status = process_result.completion_status + report_result_widget = QWidget(vendor_widget) + report_result_ui = ReportResultWidget.Ui_ReportResultWidget() + report_result_ui.setupUi(report_result_widget) + + if process_result.message: + report_result_ui.message_label.setText(process_result.message) + else: + report_result_ui.message_label.hide() + + if process_result.report_type is not None: # If this is a report result, not vendor + report_result_ui.report_type_label.setText(process_result.report_type) + if completion_status == CompletionStatus.SUCCESSFUL or completion_status == CompletionStatus.WARNING: + report_result_ui.file_frame.show() + + report_result_ui.folder_button.clicked.connect( + lambda: GeneralUtils.open_file_or_dir(process_result.file_dir)) + + report_result_ui.file_label.setText(f"Saved as: {process_result.file_name}") + report_result_ui.file_label.mousePressEvent = \ + lambda event: GeneralUtils.open_file_or_dir(process_result.file_path) + else: + report_result_ui.file_frame.hide() + else: + report_result_ui.report_type_label.setText("Target Reports") + report_result_ui.file_frame.hide() + report_result_ui.retry_frame.hide() + + report_result_ui.success_label.setText(process_result.completion_status.value) + if completion_status == CompletionStatus.FAILED: + report_result_ui.retry_check_box.stateChanged.connect( + lambda checked_state: self.on_report_to_retry_toggled(checked_state, vendor, process_result.report_type)) + else: + report_result_ui.retry_frame.hide() + + return report_result_widget + + def on_vendor_worker_finished(self, worker_id: str): + """Handles the signal emmited when a vendor worker has finished + + :param worker_id: The worker ID of the vendor + """ + self.completed_processes += 1 + + thread: QThread + worker: VendorWorker + worker, thread = self.vendor_workers[worker_id] + self.update_results_ui(worker.vendor, worker.process_result, worker.report_process_results) + + if self.is_yearly_fetch: + process_result: ProcessResult + for process_result in worker.report_process_results: + if process_result.completion_status != CompletionStatus.SUCCESSFUL: + continue + + self.database_report_data.append({'file': process_result.protected_file_path, + 'vendor': process_result.vendor.name, + 'year': process_result.year}) + + worker.deleteLater() + thread.quit() + thread.wait() + self.vendor_workers.pop(worker_id, None) + + if self.started_processes < self.total_processes and not self.is_cancelling: + request_data = self.selected_data[self.started_processes] + self.fetch_vendor_data(request_data) + self.started_processes += 1 + + elif len(self.vendor_workers) == 0: self.finish_fetching_reports() + + def start_progress_dialog(self, window_title: str): + """Sets up and shows the fetch progress dialog + + :param window_title: The title of the fetch progress dialog + """ + self.vendor_result_widgets = {} + + if self.fetch_progress_dialog: self.fetch_progress_dialog.close() + self.fetch_progress_dialog = QDialog(self.widget, flags=Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint) + fetch_progress_ui = FetchProgressDialog.Ui_FetchProgressDialog() + fetch_progress_ui.setupUi(self.fetch_progress_dialog) + self.fetch_progress_dialog.setWindowTitle(window_title) + + self.progress_bar = fetch_progress_ui.progress_bar + self.status_label = fetch_progress_ui.status_label + self.scroll_contents = fetch_progress_ui.scroll_area_widget_contents + self.scroll_layout = fetch_progress_ui.scroll_area_vertical_layout + + self.ok_button = fetch_progress_ui.buttonBox.button(QDialogButtonBox.Ok) + self.retry_button = fetch_progress_ui.buttonBox.button(QDialogButtonBox.Retry) + self.cancel_button = fetch_progress_ui.buttonBox.button(QDialogButtonBox.Cancel) + + self.ok_button.setEnabled(False) + self.retry_button.setEnabled(False) + self.retry_button.setText("Retry Selected") + self.ok_button.clicked.connect(lambda: self.fetch_progress_dialog.close()) + self.retry_button.clicked.connect(lambda: self.retry_selected_reports(window_title)) + self.cancel_button.clicked.connect(self.cancel_workers) + + self.status_label.setText("Starting...") + self.fetch_progress_dialog.show() + + def on_report_to_retry_toggled(self, checked_state: int, vendor: Vendor, report_type): + """Handles the signal emmited when a report result's retry checkbox is toggled + + The report is added the the list of reports to be retried when the 'retry selected reports' button is clicked + + :param checked_state: The new checked state + :param vendor: The vendor that this report belongs to + :param report_type: The report type of the report + """ + if checked_state == Qt.Checked: + found = False + for i in range(len(self.retry_data)): + v, report_types = self.retry_data[i] + if v == vendor: + report_types.append(report_type) + found = True + + if not found: + self.retry_data.append((vendor, [report_type])) + + elif checked_state == Qt.Unchecked: + for i in range(len(self.retry_data)): + v, report_types = self.retry_data[i] + if v == vendor: + report_types.remove(report_type) + if len(report_types) == 0: self.retry_data.pop(i) + + def retry_selected_reports(self, progress_window_title: str): + """Retries the selected reports to retry + + :param progress_window_title: The title of the fetch progress dialog + """ + if len(self.retry_data) == 0: + GeneralUtils.show_message("No report selected") + return + + self.selected_data = [] + for vendor, report_types in self.retry_data: + request_data = RequestData(vendor, report_types, self.begin_date, self.end_date, self.save_dir, + self.settings, self.selected_options) + self.selected_data.append(request_data) + + self.start_progress_dialog(progress_window_title) + self.retry_data = [] + + self.total_processes = len(self.selected_data) + self.started_processes = 0 + concurrent_vendors = self.settings.concurrent_vendors + while self.started_processes < len(self.selected_data) and self.started_processes < concurrent_vendors: + request_data = self.selected_data[self.started_processes] + self.fetch_vendor_data(request_data) + self.started_processes += 1 + + def finish_fetching_reports(self): + """Finishes up the fetch process""" + self.started_processes = 0 + self.completed_processes = 0 + self.total_processes = 0 + self.is_cancelling = False + self.cancel_button.setEnabled(False) + + # Start updating database... + if self.is_yearly_fetch and len(self.database_report_data) > 0: + if not self.start_updating_database(): self.finish_updating_database() + else: + self.finish_updating_database() + + def finish_updating_database(self): + """Finishes up the database update process""" + self.is_updating_database = False + self.database_report_data = [] + + self.ok_button.setEnabled(True) + self.retry_button.setEnabled(True) + self.status_label.setText("Done!") + if self.settings.show_debug_messages: print("Fin!") + + def cancel_workers(self): + """Sends a cancel signal to all vendor workers, updates the UI accordingly""" + self.is_cancelling = True + self.total_processes = self.started_processes + self.status_label.setText(f"Cancelling... (Waiting for started requests to finish)") + for worker, thread in self.vendor_workers.values(): + worker.set_cancelling() + + def is_yearly_range(self, begin_date: QDate, end_date: QDate) -> bool: + """Checks if a date range will retrieve all available reports for one year + + :param begin_date: The begin date + :param end_date: The end date + """ + current_date = QDate.currentDate() + + if begin_date.year() != end_date.year() or begin_date.year() > current_date.year(): + return False + + if begin_date.year() == current_date.year(): + if begin_date.month() == 1 and end_date.month() == max(current_date.month() - 1, 1): + return True + else: + if begin_date.month() == 1 and end_date.month() == 12: + return True + + return False + + def start_updating_database(self) -> bool: + """Starts a thread to update the database. Returns True if successfully started""" + if self.is_updating_database: + if self.settings.show_debug_messages: print("Database is already updating") + return False + + self.is_updating_database = True + self.status_label.setText("Updating database...") + + self.database_thread = QThread() + self.database_worker = UpdateDatabaseWorker(self.database_report_data, False) + self.database_worker.moveToThread(self.database_thread) + + def on_progress_changed(progress: int): + self.progress_bar.setValue(int((progress / len(self.database_report_data)) * 100)) + + def on_worker_finished(code): + self.database_thread.quit() + self.database_thread.wait() + self.finish_updating_database() + + self.database_worker.progress_changed_signal.connect(on_progress_changed) + self.database_worker.worker_finished_signal.connect(on_worker_finished) + + self.database_thread.started.connect(self.database_worker.work) + self.database_thread.start() + + return True + + +class FetchReportsController(FetchReportsAbstract): + """Controls the Fetch Reports tab + + This class fetches master and standard reports with default parameters. The generated TSV files only include + the mandatory columns for each report type + + :param vendors: The list of vendors in the system + :param settings: The system's user settings + :param widget: The fetch reports widget + :param fetch_reports_ui: The UI for the fetch reports widget + """ + def __init__(self, vendors: list, settings: SettingsModel, widget: QWidget, + fetch_reports_ui: FetchReportsTab.Ui_fetch_reports_tab): + super().__init__(vendors, settings, widget) + + # region General + current_date = QDate.currentDate() + begin_date = QDate(current_date.year(), 1, 1) + end_date = QDate(current_date.year(), max(current_date.month() - 1, 1), 1) + self.fetch_all_begin_date = QDate(begin_date) + self.adv_begin_date = QDate(begin_date) + self.fetch_all_end_date = QDate(end_date) + self.adv_end_date = QDate(end_date) + self.is_last_fetch_advanced = False + # endregion + + # region Start Fetch Buttons + self.fetch_all_btn = fetch_reports_ui.fetch_all_data_button + self.fetch_all_btn.clicked.connect(self.fetch_all_basic_data) + + self.fetch_adv_btn = fetch_reports_ui.fetch_advanced_button + self.fetch_adv_btn.clicked.connect(self.fetch_advanced_data) + # endregion + + # region Vendors + self.vendor_list_view = fetch_reports_ui.vendors_list_view_fetch + self.vendor_list_model = QStandardItemModel(self.vendor_list_view) + self.vendor_list_view.setModel(self.vendor_list_model) + self.update_vendors_ui() + + self.select_vendors_btn = fetch_reports_ui.select_vendors_button_fetch + self.select_vendors_btn.clicked.connect(self.select_all_vendors) + self.deselect_vendors_btn = fetch_reports_ui.deselect_vendors_button_fetch + self.deselect_vendors_btn.clicked.connect(self.deselect_all_vendors) + # endregion + + # region Report Types + self.report_type_list_view = fetch_reports_ui.report_types_list_view + self.report_type_list_model = QStandardItemModel(self.report_type_list_view) + self.report_type_list_view.setModel(self.report_type_list_model) + for report_type in ALL_REPORTS: + item = QStandardItem(report_type) + item.setCheckable(True) + item.setEditable(False) + self.report_type_list_model.appendRow(item) + + self.select_report_types_btn = fetch_reports_ui.select_report_types_button_fetch + self.select_report_types_btn.clicked.connect(self.select_all_report_types) + self.deselect_report_types_btn = fetch_reports_ui.deselect_report_types_button_fetch + self.deselect_report_types_btn.clicked.connect(self.deselect_all_report_types) + + self.report_types_help_btn = fetch_reports_ui.report_types_help_button + self.report_types_help_btn.clicked.connect( + lambda: GeneralUtils.show_message("Only reports supported by each respective vendor will be retrieved, " + "and unsupported reports will be listed in \"Expand\" results")) + # endregion + + # region Custom Directory + self.custom_dir_frame = fetch_reports_ui.custom_dir_frame + self.custom_dir_frame.hide() + self.custom_dir_message_frame = fetch_reports_ui.frame + self.custom_dir_message_frame.hide() + self.custom_dir_message_frame2 = fetch_reports_ui.frame_2 + self.custom_dir_message_frame2.hide() + self.custom_dir_frame_message1 = fetch_reports_ui.label_38 + self.custom_dir_frame_message2 = fetch_reports_ui.label + self.custom_dir_edit = fetch_reports_ui.custom_dir_edit + self.custom_dir_edit.setText(self.settings.other_directory) + self.custom_dir_button = fetch_reports_ui.custom_dir_button + self.custom_dir_button.clicked.connect(self.on_custom_dir_clicked) + self.date_range_help_btn = fetch_reports_ui.date_range_help_button + self.date_range_help_btn.clicked.connect( + lambda: GeneralUtils.show_message("Reports run for date ranges that represent a normal Jan-Dec calendar" + " year, or Jan-last month for calendar years in progress, will be added" + " to (or updated in) the search database. All other date ranges will be " + "saved in the specified folder but not added to the search database.")) + self.date_range_help_btn2 = fetch_reports_ui.date_range_help_button2 + self.date_range_help_btn2.clicked.connect( + lambda: GeneralUtils.show_message("Reports run for date ranges that represent a normal Jan-Dec calendar" + " year, or Jan-last month for calendar years in progress, will be added" + " to (or updated in) the search database. All other date ranges will be " + "saved in the specified folder but not added to the search database.")) + self.date_range_help_btn3 = fetch_reports_ui.date_range_help_button3 + self.date_range_help_btn3.clicked.connect( + lambda: GeneralUtils.show_message("See Other Reports Directory setting in Settings for default")) + + # endregion + + # region Date Edits + self.all_date_edit = fetch_reports_ui.All_reports_edit_fetch + self.all_date_edit.setDate(self.fetch_all_begin_date) + self.all_date_edit.dateChanged.connect(self.on_fetch_all_date_changed) + + self.begin_date_edit_year = fetch_reports_ui.begin_date_edit_fetch_year + self.begin_date_edit_year.setDate(self.adv_begin_date) + self.begin_date_edit_year.dateChanged.connect(lambda date: self.on_date_year_changed(date, "adv_begin")) + + self.end_date_edit_year = fetch_reports_ui.end_date_edit_fetch_year + self.end_date_edit_year.setDate(self.adv_end_date) + self.end_date_edit_year.dateChanged.connect(lambda date: self.on_date_year_changed(date, "adv_end")) + + self.begin_month_combo_box = fetch_reports_ui.begin_month_combo_box + for month in MONTH_NAMES: + self.begin_month_combo_box.addItem(month) + self.begin_month_combo_box.currentIndexChanged.connect( + lambda index: self.on_date_month_changed(index + 1, "adv_begin")) + self.begin_month_combo_box.setCurrentIndex(self.adv_begin_date.month() - 1) + + self.end_month_combo_box = fetch_reports_ui.end_month_combo_box + for month in MONTH_NAMES: + self.end_month_combo_box.addItem(month) + self.end_month_combo_box.currentIndexChanged.connect( + lambda index: self.on_date_month_changed(index + 1, "adv_end")) + self.end_month_combo_box.setCurrentIndex(self.adv_end_date.month() - 1) + + # endregion + + def update_vendors_ui(self): + """Updates the UI to show vendors that support report fetching (SUSHI)""" + self.vendor_list_model.clear() + for vendor in self.vendors: + item = QStandardItem(vendor.name) + item.setCheckable(True) + item.setEditable(False) + self.vendor_list_model.appendRow(item) + + def on_fetch_all_date_changed(self, date: QDate): + """Handles the signal emitted when the 'fetch all' date is changed + + :param date: The new date + """ + current_date = QDate.currentDate() + if date.year() == current_date.year(): + self.fetch_all_begin_date = QDate(current_date.year(), 1, 1) + self.fetch_all_end_date = QDate(current_date.year(), max(current_date.month() - 1, 1), 1) + elif date.year() < current_date.year(): + self.fetch_all_begin_date = QDate(date.year(), 1, 1) + self.fetch_all_end_date = QDate(date.year(), 12, 1) + else: + self.all_date_edit.setDate(current_date) + + def on_date_year_changed(self, date: QDate, date_type: str): + """Handles the signal emitted when a date's year is changed + + :param date: The new date + :param date_type: The date to be updated + """ + if date_type == "adv_begin": + self.adv_begin_date = QDate(date.year(), self.adv_begin_date.month(), self.adv_begin_date.day()) + + elif date_type == "adv_end": + self.adv_end_date = QDate(date.year(), self.adv_end_date.month(), self.adv_end_date.day()) + + if self.is_yearly_range(self.adv_begin_date, self.adv_end_date): + self.custom_dir_frame.hide() + self.custom_dir_message_frame.hide() + self.custom_dir_message_frame2.hide() + else: + self.custom_dir_frame.show() + if self.custom_dir_frame_message_show(self.adv_begin_date, self.adv_end_date): + self.custom_dir_message_frame2.show() + self.custom_dir_message_frame.hide() + else: + self.custom_dir_message_frame.show() + self.custom_dir_message_frame2.hide() + + def on_date_month_changed(self, month: int, date_type: str): + """Handles the signal emitted when a date's month is changed + + :param month: The new month + :param date_type: The date to be updated + """ + if date_type == "adv_begin": + self.adv_begin_date = QDate(self.adv_begin_date.year(), month, self.adv_begin_date.day()) + + elif date_type == "adv_end": + self.adv_end_date = QDate(self.adv_end_date.year(), month, self.adv_end_date.day()) + + if self.is_yearly_range(self.adv_begin_date, self.adv_end_date): + self.custom_dir_frame.hide() + self.custom_dir_message_frame.hide() + self.custom_dir_message_frame2.hide() + else: + self.custom_dir_frame.show() + if self.custom_dir_frame_message_show(self.adv_begin_date, self.adv_end_date): + self.custom_dir_message_frame2.show() + self.custom_dir_message_frame.hide() + else: + self.custom_dir_message_frame.show() + self.custom_dir_message_frame2.hide() + + def custom_dir_frame_message_show(self, begin_date: QDate, end_date: QDate) -> bool: + """Checks which message will show on the custom dir frame + + :param begin_date: The begin date + :param end_date: The end date + """ + current_date = QDate.currentDate() + + if begin_date.year() == end_date.year() == current_date.year(): + if begin_date.month() == 1 and end_date.month() == 12: + return True + + return False + + def on_custom_dir_clicked(self): + """Handles the signal emitted when the choose custom directory button is clicked""" + dir_path = GeneralUtils.choose_directory() + if dir_path: self.custom_dir_edit.setText(dir_path) + + def select_all_vendors(self): + """Checks all vendors in the vendors list view""" + for i in range(self.vendor_list_model.rowCount()): + self.vendor_list_model.item(i).setCheckState(Qt.Checked) + + def deselect_all_vendors(self): + """Un-checks all vendors in the vendors list view""" + for i in range(self.vendor_list_model.rowCount()): + self.vendor_list_model.item(i).setCheckState(Qt.Unchecked) + + def select_all_report_types(self): + """Checks all report types in the report types list view""" + for i in range(self.report_type_list_model.rowCount()): + self.report_type_list_model.item(i).setCheckState(Qt.Checked) + + def deselect_all_report_types(self): + """Un-checks all report types in the report types list view""" + for i in range(self.report_type_list_model.rowCount()): + self.report_type_list_model.item(i).setCheckState(Qt.Unchecked) + + def fetch_all_basic_data(self): + """Fetches all reports for the selected year""" + if self.total_processes > 0: + GeneralUtils.show_message(f"Waiting for pending processes to complete...") + if self.settings.show_debug_messages: print(f"Waiting for pending processes to complete...") + return + + if len(self.vendors) == 0: + GeneralUtils.show_message("Vendor list is empty") + return + + self.begin_date = self.fetch_all_begin_date + self.end_date = self.fetch_all_end_date + if self.begin_date > self.end_date: + GeneralUtils.show_message("\'Begin Date\' is earlier than \'End Date\'") + return + + self.is_yearly_fetch = True + self.save_dir = self.settings.yearly_directory + self.selected_data = [] + for i in range(len(self.vendors)): + if self.vendors[i].is_non_sushi: continue + + request_data = RequestData(self.vendors[i], ALL_REPORTS, self.begin_date, self.end_date, + self.save_dir, self.settings) + self.selected_data.append(request_data) + + self.is_last_fetch_advanced = False + self.start_progress_dialog("Fetch Reports Progress") + self.retry_data = [] + + self.total_processes = len(self.selected_data) + self.started_processes = 0 + concurrent_vendors = self.settings.concurrent_vendors + while self.started_processes < len(self.selected_data) and self.started_processes < concurrent_vendors: + request_data = self.selected_data[self.started_processes] + self.fetch_vendor_data(request_data) + self.started_processes += 1 + + def fetch_advanced_data(self): + """Fetches reports based on the selected options in the advanced view of the UI""" + if self.total_processes > 0: + GeneralUtils.show_message(f"Waiting for pending processes to complete...") + if self.settings.show_debug_messages: print(f"Waiting for pending processes to complete...") + return + + if len(self.vendors) == 0: + GeneralUtils.show_message("Vendor list is empty") + return + + self.begin_date = self.adv_begin_date + self.end_date = self.adv_end_date + if self.begin_date > self.end_date: + GeneralUtils.show_message("\'Begin Date\' is earlier than \'End Date\'") + return + + self.selected_data = [] + selected_report_types = [] + for i in range(len(ALL_REPORTS)): + if self.report_type_list_model.item(i).checkState() == Qt.Checked: + selected_report_types.append(ALL_REPORTS[i]) + if len(selected_report_types) == 0: + GeneralUtils.show_message("No report type selected") + return + + self.is_yearly_fetch = self.is_yearly_range(self.adv_begin_date, self.adv_end_date) + custom_dir = self.custom_dir_edit.text() + if not custom_dir: custom_dir = self.settings.other_directory + self.save_dir = custom_dir if not self.is_yearly_fetch else self.settings.yearly_directory + for i in range(self.vendor_list_model.rowCount()): + if self.vendor_list_model.item(i).checkState() == Qt.Checked: + request_data = RequestData(self.vendors[i], selected_report_types, self.begin_date, self.end_date, + self.save_dir, self.settings) + self.selected_data.append(request_data) + if len(self.selected_data) == 0: + GeneralUtils.show_message("No vendor selected") + return + + self.start_progress_dialog("Fetch Reports Progress") + self.is_last_fetch_advanced = False + self.retry_data = [] + + self.total_processes = len(self.selected_data) + self.started_processes = 0 + concurrent_vendors = self.settings.concurrent_vendors + while self.started_processes < len(self.selected_data) and self.started_processes < concurrent_vendors: + request_data = self.selected_data[self.started_processes] + self.fetch_vendor_data(request_data) + self.started_processes += 1 + + +class FetchSpecialReportsController(FetchReportsAbstract): + def __init__(self, vendors: list, settings: SettingsModel, widget: QWidget, + fetch_special_reports_ui: FetchSpecialReportsTab.Ui_fetch_special_reports_tab): + super().__init__(vendors, settings, widget) + + # region General + self.selected_report_type = None + self.selected_options = SpecialReportOptions() + current_date = QDate.currentDate() + self.begin_date = QDate(current_date.year(), 1, 1) + self.end_date = QDate(current_date.year(), max(current_date.month() - 1, 1), 1) + # endregion + + # region Start Fetch Button + self.fetch_special_btn = fetch_special_reports_ui.fetch_special_data_button + self.fetch_special_btn.clicked.connect(self.fetch_special_data) + # endregion + + # region Vendors + self.vendor_list_view = fetch_special_reports_ui.vendors_list_view_special + self.vendor_list_model = QStandardItemModel(self.vendor_list_view) + self.vendor_list_view.setModel(self.vendor_list_model) + self.update_vendors_ui() + + self.select_vendors_btn = fetch_special_reports_ui.select_vendors_button_special + self.select_vendors_btn.clicked.connect(self.select_all_vendors) + self.deselect_vendors_btn = fetch_special_reports_ui.deselect_vendors_button_special + self.deselect_vendors_btn.clicked.connect(self.deselect_all_vendors) + # endregion + + # region Options + self.options_frame = fetch_special_reports_ui.options_frame + self.options_layout = self.options_frame.layout() + help_message = "The 'show' checkboxes will break down your data by displaying extra columns with the " \ + "selected attributes (usually results in more lines per item)\n" \ + "Filters will limit your data to just the selected options (implies also showing that " \ + "attribute).\n" \ + "You can show the attribute breakdown without selecting filtering to specific values." + fetch_special_reports_ui.options_help_button.clicked.connect( + lambda: GeneralUtils.show_message(help_message) + ) + # endregion + + # region Report Types + self.pr_radio_button = fetch_special_reports_ui.pr_radio_button + self.dr_radio_button = fetch_special_reports_ui.dr_radio_button + self.tr_radio_button = fetch_special_reports_ui.tr_radio_button + self.ir_radio_button = fetch_special_reports_ui.ir_radio_button + + self.pr_radio_button.clicked.connect(lambda checked: self.on_report_type_selected(MajorReportType.PLATFORM)) + self.dr_radio_button.clicked.connect(lambda checked: self.on_report_type_selected(MajorReportType.DATABASE)) + self.tr_radio_button.clicked.connect(lambda checked: self.on_report_type_selected(MajorReportType.TITLE)) + self.ir_radio_button.clicked.connect(lambda checked: self.on_report_type_selected(MajorReportType.ITEM)) + + self.pr_radio_button.setChecked(True) + self.on_report_type_selected(MajorReportType.PLATFORM) + + # endregion + + # region Date Edits + self.date_range_help_btn = fetch_special_reports_ui.date_range_help_button + self.date_range_help_btn.clicked.connect( + lambda: GeneralUtils.show_message("Many vendors do not support a date range longer than 12 consecutive months.")) + + self.begin_date_edit_year = fetch_special_reports_ui.begin_date_edit_special_year + self.begin_date_edit_year.setDate(self.begin_date) + self.begin_date_edit_year.dateChanged.connect(lambda date: self.on_date_year_changed(date, "begin_date")) + + self.end_date_edit_year = fetch_special_reports_ui.end_date_edit_special_year + self.end_date_edit_year.setDate(self.end_date) + self.end_date_edit_year.dateChanged.connect(lambda date: self.on_date_year_changed(date, "end_date")) + + self.begin_month_combo_box = fetch_special_reports_ui.begin_month_combo_box + for month in MONTH_NAMES: + self.begin_month_combo_box.addItem(month) + self.begin_month_combo_box.currentIndexChanged.connect( + lambda index: self.on_date_month_changed(index + 1, "begin_date")) + self.begin_month_combo_box.setCurrentIndex(self.begin_date.month() - 1) + + self.end_month_combo_box = fetch_special_reports_ui.end_month_combo_box + for month in MONTH_NAMES: + self.end_month_combo_box.addItem(month) + self.end_month_combo_box.currentIndexChanged.connect( + lambda index: self.on_date_month_changed(index + 1, "end_date")) + self.end_month_combo_box.setCurrentIndex(self.end_date.month() - 1) + + # endregion + + # region Custom Directory + self.custom_dir_frame = fetch_special_reports_ui.custom_dir_frame + self.custom_dir_edit = fetch_special_reports_ui.custom_dir_edit + self.custom_dir_edit.setText(self.settings.other_directory) + self.custom_dir_button = fetch_special_reports_ui.custom_dir_button + self.custom_dir_button.clicked.connect(self.on_custom_dir_clicked) + self.custom_dir_help_btn = fetch_special_reports_ui.custom_dir_help_button + self.custom_dir_help_btn.clicked.connect( + lambda: GeneralUtils.show_message("See Other Reports Directory setting in Settings for default.")) + # endregion + + def update_vendors_ui(self): + """Updates the UI to show vendors that support report fetching (SUSHI)""" + self.vendor_list_model.clear() + for vendor in self.vendors: + item = QStandardItem(vendor.name) + item.setCheckable(True) + item.setEditable(False) + self.vendor_list_model.appendRow(item) + + def on_date_year_changed(self, date: QDate, date_type: str): + """Handles the signal emitted when a date's year is changed + + :param date: The new date + :param date_type: The date to be updated + """ + if date_type == "begin_date": + self.begin_date = QDate(date.year(),self.begin_date.month(),self.begin_date.day()) + # if self.begin_date.year() != self.end_date.year(): + # self.end_date.setDate(self.begin_date.year(), + # self.end_date.month(), + # self.end_date.day()) + # self.end_date_edit.setDate(self.end_date) + elif date_type == "end_date": + self.end_date = QDate(date.year(),self.end_date.month(),self.end_date.day()) + # if self.end_date.year() != self.begin_date.year(): + # self.begin_date.setDate(self.end_date.year(), + # self.begin_date.month(), + # self.begin_date.day()) + # self.begin_date_edit.setDate(self.begin_date) + + def on_date_month_changed(self, month: int, date_type: str): + """Handles the signal emitted when a date's month is changed + + :param month: The new month + :param date_type: The date to be updated + """ + if date_type == "begin_date": + self.begin_date = QDate(self.begin_date.year(), month, self.begin_date.day()) + elif date_type == "end_date": + self.end_date = QDate(self.end_date.year(), month, self.end_date.day()) + + def on_report_type_selected(self, major_report_type: MajorReportType): + """Handles the signal emitted when a report type is selected + + :param major_report_type: The major report type (Only master reports are supported) + """ + if major_report_type == self.selected_report_type: return + + self.selected_report_type = major_report_type + self.selected_options = SpecialReportOptions() + + # Remove existing options from ui + for i in reversed(range(self.options_layout.count())): + widget = self.options_layout.itemAt(i).widget() + # remove it from the layout list + self.options_layout.removeWidget(widget) + # remove it from the gui + widget.deleteLater() + + # Add new options + self.options_layout.addWidget(QLabel("Show", self.options_frame), 0, 0) + self.options_layout.addWidget(QLabel("Filters", self.options_frame), 0, 1) + + special_options = SPECIAL_REPORT_OPTIONS[major_report_type] + for i in range(len(special_options)): + option_name = special_options[i][1] + checkbox = QCheckBox(option_name, self.options_frame) + checkbox.toggled.connect( + lambda is_checked, option=option_name: self.on_special_option_toggled(option, is_checked)) + self.options_layout.addWidget(checkbox, i + 1, 0) + + option_type: SpecialOptionType = special_options[i][0] + if option_type == SpecialOptionType.AP or option_type == SpecialOptionType.POS\ + or option_type == SpecialOptionType.ADP: + line_edit = QLineEdit(DEFAULT_SPECIAL_OPTION_VALUE, self.options_frame) + line_edit.setReadOnly(True) + button = QPushButton("Choose", self.options_frame) + if option_type == SpecialOptionType.ADP: + button.clicked.connect( + lambda c, so=special_options[i], edit=line_edit: + self.on_special_date_parameter_option_button_clicked(so, edit)) + else: + button.clicked.connect( + lambda c, so=special_options[i], edit=line_edit: + self.on_special_parameter_option_button_clicked(so, edit)) + + self.options_layout.addWidget(line_edit, i + 1, 1) + self.options_layout.addWidget(button, i + 1, 2) + + def on_special_option_toggled(self, option: str, is_checked: bool): + """Handles the signal emitted when a special option is checked or un-checked + + :param option: The special option + :param is_checked: Checked or un-checked + """ + option = option.lower() + __, option_type, option_name, curr_options = self.selected_options.__getattribute__(option) + self.selected_options.__setattr__(option, (is_checked, option_type, option_name, curr_options)) + + def on_special_parameter_option_button_clicked(self, special_option, line_edit): + """Handles the signal emitted when a special option with parameters clicked + + :param special_option: The special option + :param line_edit: The line edit to show the selected parameters for this option + """ + option_type, option_name, option_list = special_option + is_selected, option_type, option_name, curr_options = self.selected_options.__getattribute__(option_name.lower()) + + dialog = QDialog(self.options_frame, flags=Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint) + dialog.setWindowTitle(option_name + " options") + layout = QVBoxLayout(dialog) + + list_view = QListView(dialog) + list_view.setAlternatingRowColors(True) + model = QStandardItemModel(list_view) + + for option in option_list: + item = QStandardItem(option) + item.setCheckable(True) + item.setEditable(False) + if option in curr_options: + item.setCheckState(Qt.Checked) + model.appendRow(item) + + list_view.setModel(model) + + layout.addWidget(list_view) + + def on_ok_button_clicked(): + checked_list = [] + for i in range(model.rowCount()): + if model.item(i).checkState() == Qt.Checked: + checked_list.append(model.item(i).text()) + + if len(checked_list) == 0: + checked_list = [DEFAULT_SPECIAL_OPTION_VALUE] + + line_edit.setText("|".join(checked_list)) + self.selected_options.__setattr__(option_name.lower(), (is_selected, option_type, option_name, checked_list)) + dialog.close() + + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, dialog) + button_box.accepted.connect(on_ok_button_clicked) + button_box.rejected.connect(lambda: dialog.close()) + button_box.setCenterButtons(True) + layout.addWidget(button_box) + + dialog.exec_() + + def on_special_date_parameter_option_button_clicked(self, special_option, line_edit): + """Handles the signal emitted when a date special option is clicked + + :param special_option: The special option + :param line_edit: The line edit to show the selected date parameters for this option + """ + option_type, option_name = special_option + is_selected, option_type, option_name, selected_options = self.selected_options.__getattribute__(option_name.lower()) + + dialog = QDialog(self.options_frame, flags=Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint) + dialog.setWindowTitle(option_name + " options") + layout = QVBoxLayout(dialog) + + radio_button_group = QButtonGroup(dialog) + + default_frame = QFrame(dialog) + default_frame.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + default_layout = QHBoxLayout(default_frame) + default_layout.setContentsMargins(0, 0, 0, 0) + + default_radio_btn = QRadioButton(default_frame) + default_radio_btn.setChecked(True) + radio_button_group.addButton(default_radio_btn) + default_label = QLabel(DEFAULT_SPECIAL_OPTION_VALUE, dialog) + + default_layout.addWidget(default_radio_btn) + default_layout.addWidget(default_label) + + single_date_frame = QFrame(dialog) + single_date_frame.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + single_date_layout = QHBoxLayout(single_date_frame) + single_date_layout.setContentsMargins(0, 0, 0, 0) + + single_date_radio_btn = QRadioButton(single_date_frame) + radio_button_group.addButton(single_date_radio_btn) + single_date_edit = QDateEdit(QDate.currentDate(), single_date_frame) + single_date_edit.setDisplayFormat("yyyy") + + single_date_layout.addWidget(single_date_radio_btn) + single_date_layout.addWidget(single_date_edit) + + range_date_frame = QFrame(dialog) + range_date_frame.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + range_date_layout = QHBoxLayout(range_date_frame) + range_date_layout.setContentsMargins(0, 0, 0, 0) + + range_date_radio_btn = QRadioButton(range_date_frame) + radio_button_group.addButton(range_date_radio_btn) + begin_date_edit = QDateEdit(QDate.currentDate(), range_date_frame) + end_date_edit = QDateEdit(QDate.currentDate(), range_date_frame) + begin_date_edit.setDisplayFormat("yyyy") + end_date_edit.setDisplayFormat("yyyy") + to_label = QLabel(" to ", range_date_frame) + to_label.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) + + range_date_layout.addWidget(range_date_radio_btn) + range_date_layout.addWidget(begin_date_edit) + range_date_layout.addWidget(to_label) + range_date_layout.addWidget(end_date_edit) + + def on_ok_button_clicked(): + new_selection = [DEFAULT_SPECIAL_OPTION_VALUE] + checked_button = radio_button_group.checkedButton() + if checked_button == single_date_radio_btn: + new_selection = [single_date_edit.date().toString("yyyy")] + elif checked_button == range_date_radio_btn: + new_selection = [begin_date_edit.date().toString("yyyy") + "-" + end_date_edit.date().toString("yyyy")] + + line_edit.setText(new_selection[0]) + self.selected_options.__setattr__(option_name.lower(), (is_selected, option_type, option_name, new_selection)) + dialog.close() + + button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, dialog) + button_box.accepted.connect(on_ok_button_clicked) + button_box.rejected.connect(lambda: dialog.close()) + button_box.setCenterButtons(True) + + layout.addWidget(default_frame) + layout.addWidget(single_date_frame) + layout.addWidget(range_date_frame) + layout.addWidget(button_box) + + dialog.exec_() + + def on_custom_dir_clicked(self): + """Handles the signal emitted when the choose custom directory button is clicked""" + dir_path = GeneralUtils.choose_directory() + if dir_path: self.custom_dir_edit.setText(dir_path) + + def select_all_vendors(self): + """Checks all vendors in the vendors list view""" + for i in range(self.vendor_list_model.rowCount()): + self.vendor_list_model.item(i).setCheckState(Qt.Checked) + + def deselect_all_vendors(self): + """Un-checks all vendors in the vendors list view""" + for i in range(self.vendor_list_model.rowCount()): + self.vendor_list_model.item(i).setCheckState(Qt.Unchecked) + + def fetch_special_data(self): + """Fetches reports based on the selected options in the UI""" + if self.total_processes > 0: + GeneralUtils.show_message(f"Waiting for pending processes to complete...") + if self.settings.show_debug_messages: print(f"Waiting for pending processes to complete...") + return + + if len(self.vendors) == 0: + GeneralUtils.show_message("Vendor list is empty") + return + + if self.begin_date > self.end_date: + GeneralUtils.show_message("\'Begin Date\' is earlier than \'End Date\'") + return + + self.selected_data = [] + selected_report_types = [self.selected_report_type.value] + + self.is_yearly_fetch = False + custom_dir = self.custom_dir_edit.text() + self.save_dir = custom_dir if custom_dir else self.settings.other_directory + for i in range(self.vendor_list_model.rowCount()): + if self.vendor_list_model.item(i).checkState() == Qt.Checked: + request_data = RequestData(self.vendors[i], selected_report_types, self.begin_date, self.end_date, + self.save_dir, self.settings, self.selected_options) + self.selected_data.append(request_data) + if len(self.selected_data) == 0: + GeneralUtils.show_message("No vendor selected") + return + + self.start_progress_dialog("Fetch Special Reports Progress") + self.retry_data = [] + + self.total_processes = len(self.selected_data) + self.started_processes = 0 + concurrent_vendors = self.settings.concurrent_vendors + while self.started_processes < len(self.selected_data) and self.started_processes < concurrent_vendors: + request_data = self.selected_data[self.started_processes] + self.fetch_vendor_data(request_data) + self.started_processes += 1 + + +class VendorWorker(QObject): + """This does all the work for a vendor when fetching reports + + :param worker_id: The ID to identify this worker (vendor_name) + :param request_data: The request data for this request + """ + worker_finished_signal = pyqtSignal(str) + + def __init__(self, worker_id: str, request_data: RequestData): + super().__init__() + self.worker_id = worker_id + self.request_data = request_data + self.vendor = request_data.vendor + self.target_report_types = request_data.target_report_types + self.show_debug = request_data.settings.show_debug_messages + self.concurrent_reports = request_data.settings.concurrent_reports + self.request_interval = request_data.settings.request_interval + self.request_timeout = request_data.settings.request_timeout + self.user_agent = request_data.settings.user_agent + self.reports_to_process = [] + self.started_processes = 0 + self.completed_processes = 0 + self.total_processes = 0 + + self.process_result = ProcessResult(self.vendor) + self.report_process_results = [] + self.report_workers = {} # + self.is_cancelling = False + + def work(self): + """Processes the vendor's requests + + Requests the vendor's supported reports before requesting only the supported reports + """ + if self.show_debug: print(f"{self.vendor.name}: Fetching supported reports") + request_query = {} + if self.vendor.customer_id.strip(): request_query["customer_id"] = self.vendor.customer_id + if self.vendor.requestor_id.strip(): request_query["requestor_id"] = self.vendor.requestor_id + if self.vendor.api_key.strip(): request_query["api_key"] = self.vendor.api_key + if self.vendor.platform.strip(): request_query["platform"] = self.vendor.platform + + request_url = self.vendor.base_url + + try: + # Some vendors only work if they think a web browser is making the request... + response = requests.get(request_url, request_query, headers={'User-Agent': self.user_agent}, + timeout=self.request_timeout) + if self.show_debug: print(response.url) + if response.status_code == 200: + self.process_response(response) + else: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = f"Unexpected HTTP status code received: {response.status_code}" + except requests.exceptions.Timeout as e: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = f"Request timed out after {self.request_timeout} second(s)" + if self.show_debug: print(f"{self.vendor.name}: Request timed out") + except requests.exceptions.RequestException as e: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = f"Request Exception: {e}" + if self.show_debug: print(f"{self.vendor.name}: Request Exception: {e}") + + if len(self.report_workers) == 0: self.notify_worker_finished() + + def process_response(self, response: requests.Response): + """Processes the response from a REST request + + Requests the target reports that are supported by the vendor + """ + if self.is_cancelling: + self.process_result.message = "Target reports not processed" + self.process_result.completion_status = CompletionStatus.CANCELLED + return + + try: + json_response = response.json() + exceptions = self.check_for_exception(json_response) + if len(exceptions) > 0: + self.process_result.message = exception_models_to_message(exceptions) + self.process_result.completion_status = CompletionStatus.FAILED + return + + json_dicts = [] + if type(json_response) is dict: # This should never be a dict by the standard, but some vendors...... + json_dicts = json_response["Report_Items"] if "Report_Items" in json_response else [] # Report_Items??? + # Some other vendor might implement it in a different way.......................... + elif type(json_response) is list: + json_dicts = json_response + + if len(json_dicts) == 0: + raise Exception("JSON is empty") + + supported_report_types = [] + self.reports_to_process = [] + for json_dict in json_dicts: + supported_report = SupportedReportModel.from_json(json_dict) + supported_report_types.append(supported_report.report_id) + + if supported_report.report_id in self.target_report_types: + self.reports_to_process.append(supported_report.report_id) + + unsupported_report_types = list(set(self.target_report_types) - set(supported_report_types)) + + self.process_result.message = "Supported by vendor: " + self.process_result.message += ", ".join(self.reports_to_process) + self.process_result.message += "\nUnsupported: " + self.process_result.message += ", ".join(unsupported_report_types) + + if len(self.reports_to_process) == 0: return + + self.total_processes = len(self.reports_to_process) + self.started_processes = 0 + while self.started_processes < self.total_processes and self.started_processes < self.concurrent_reports: + QThread.currentThread().sleep(self.request_interval) # Avoid spamming vendor's server + if self.is_cancelling: + self.process_result.completion_status = CompletionStatus.CANCELLED + return + + self.fetch_report(self.reports_to_process[self.started_processes]) + self.started_processes += 1 + + except json.JSONDecodeError as e: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = f"JSON Exception: {e}" + if self.show_debug: print(f"{self.vendor.name}: JSON Exception: {e.msg}") + except Exception as e: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = str(e) + if self.show_debug: print(f"{self.vendor.name}: Exception: {e}") + + def fetch_report(self, report_type: str): + """Initiates the process to fetch a report + + :param report_type: The target report type + """ + worker_id = report_type + if worker_id in self.report_workers: return # Avoid fetching a report twice, app will crash!! + + report_worker = ReportWorker(worker_id, report_type, self.request_data) + report_worker.worker_finished_signal.connect(self.on_report_worker_finished) + report_thread = QThread() + self.report_workers[worker_id] = report_worker, report_thread + report_worker.moveToThread(report_thread) + report_thread.started.connect(report_worker.work) + report_thread.finished.connect(report_thread.deleteLater) + + report_thread.start() + + def check_for_exception(self, json_response) -> list: + """Checks a JSON response for exception models + + :param json_response: The JSON response to be processed + """ + exceptions = [] + + if type(json_response) is dict: + if "Exception" in json_response: + exceptions.append(ExceptionModel.from_json(json_response["Exception"])) + # raise Exception(f"Code: {exception.code}, Message: {exception.message}") + + code = int(json_response["Code"]) if "Code" in json_response else "" + message = json_response["Message"] if "Message" in json_response else "" + data = json_response["Data"] if "Data" in json_response else "" + severity = json_response["Severity"] if "Severity" in json_response else "" + if code: + exceptions.append(ExceptionModel(code, message, severity, data)) + + elif type(json_response) is list: + for json_dict in json_response: + exception = ExceptionModel.from_json(json_dict) + if exception.code: + exceptions.append(exception) + + return exceptions + + def notify_worker_finished(self): + """Notifies any listeners that this worker has finished""" + self.worker_finished_signal.emit(self.vendor.name) + + def on_report_worker_finished(self, worker_id: str): + """Handles the signal emmited when a report worker has finished + + :param worker_id: The report worker's worker id + """ + self.completed_processes += 1 + + thread: QThread + worker: ReportWorker + worker, thread = self.report_workers[worker_id] + + self.report_process_results.append(worker.process_result) + worker.deleteLater() + thread.quit() + thread.wait() + self.report_workers.pop(worker_id, None) + + if self.started_processes < self.total_processes and not self.is_cancelling: + QThread.currentThread().sleep(self.request_interval) # Avoid spamming vendor's server + self.fetch_report(self.reports_to_process[self.started_processes]) + self.started_processes += 1 + + if len(self.report_workers) == 0: self.notify_worker_finished() + + def set_cancelling(self): + """Sets the worker to a cancelling state""" + self.is_cancelling = True + + +class ReportWorker(QObject): + """This does all the work for a report + + :param worker_id: The ID to identify this worker (vendor_name-report_type) + :param report_type: The report type to be processed + :param request_data: The request data for this request + """ + worker_finished_signal = pyqtSignal(str) + + def __init__(self, worker_id: str, report_type: str, request_data: RequestData): + super().__init__() + self.worker_id = worker_id + self.report_type = report_type + self.vendor = request_data.vendor + self.begin_date = request_data.begin_date + self.end_date = request_data.end_date + self.show_debug = request_data.settings.show_debug_messages + self.request_timeout = request_data.settings.request_timeout + self.user_agent = request_data.settings.user_agent + self.save_dir = request_data.save_location + self.special_options = request_data.special_options + + self.is_yearly = self.save_dir == request_data.settings.yearly_directory + self.is_special = self.special_options is not None + self.is_master = self.report_type in MASTER_REPORTS + + self.process_result = ProcessResult(self.vendor, self.report_type) + self.retried_request = False + + def work(self): + """Processes the report request""" + if self.show_debug: print(f"{self.vendor.name}-{self.report_type}: Fetching Report") + + self.make_request() + + if self.show_debug: print(f"{self.vendor.name}-{self.report_type}: Done") + self.notify_worker_finished() + + def make_request(self): + """Sends the request fetch a report""" + request_query = {} + if self.vendor.customer_id.strip(): request_query["customer_id"] = self.vendor.customer_id + if self.vendor.requestor_id.strip(): request_query["requestor_id"] = self.vendor.requestor_id + if self.vendor.api_key.strip(): request_query["api_key"] = self.vendor.api_key + if self.vendor.platform.strip(): request_query["platform"] = self.vendor.platform + request_query["begin_date"] = self.begin_date.toString("yyyy-MM") + request_query["end_date"] = self.end_date.toString("yyyy-MM") + + attributes_to_show = "" + if self.is_special: + attr_count = 0 + special_options_dict = self.special_options.__dict__ + for option in special_options_dict: + value = special_options_dict[option] + is_selected, option_type, option_name, option_parameters = value + + if is_selected: + if option_type == SpecialOptionType.AP or option_type == SpecialOptionType.ADP\ + or option_type == SpecialOptionType.POS: + if option_parameters[0] != DEFAULT_SPECIAL_OPTION_VALUE: + request_query[option] = "|".join(option_parameters) + + elif option_type == SpecialOptionType.POB: + request_query[option] = "True" + + if option_type == SpecialOptionType.AP or option_type == SpecialOptionType.ADP\ + or option_type == SpecialOptionType.AO: + if attr_count == 0: + attributes_to_show += option_name + attr_count += 1 + elif attr_count > 0: + attributes_to_show += f"|{option_name}" + attr_count += 1 + elif self.is_yearly and self.is_master: + major_report_type = get_major_report_type(self.report_type) + if major_report_type == MajorReportType.PLATFORM: + attributes_to_show = "|".join(PLATFORM_REPORTS_ATTRIBUTES) + elif major_report_type == MajorReportType.DATABASE: + attributes_to_show = "|".join(DATABASE_REPORTS_ATTRIBUTES) + elif major_report_type == MajorReportType.TITLE: + attributes_to_show = "|".join(TITLE_REPORTS_ATTRIBUTES) + elif major_report_type == MajorReportType.ITEM: + attributes_to_show = "|".join(ITEM_REPORTS_ATTRIBUTES) + request_query["include_parent_details"] = True + request_query["include_component_details"] = True + + if attributes_to_show: request_query["attributes_to_show"] = attributes_to_show + + request_url = f"{self.vendor.base_url}/{self.report_type.lower()}" + + try: + # Some vendors only work if they think a web browser is making the request... + response = requests.get(request_url, request_query, headers={'User-Agent': self.user_agent}, + timeout=self.request_timeout) + if self.show_debug: print(response.url) + if response.status_code == 200: + self.process_response(response) + else: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = f"Unexpected HTTP status code received: {response.status_code}" + except requests.exceptions.Timeout as e: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = f"Request timed out after {self.request_timeout} second(s)" + if self.show_debug: print(f"{self.vendor.name}: Request timed out") + except requests.exceptions.RequestException as e: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = f"Request Exception: {e}" + if self.show_debug: print( + f"{self.vendor.name}-{self.report_type}: Request Exception: {e}") + + def process_response(self, response: requests.Response): + """Processes the response from a request + + Converts the receoved JSON to a SUSHI Report Model + + :param response: The received response + """ + try: + json_string = response.text + if self.is_yearly: self.save_json_file(json_string) + + json_dict = json.loads(json_string) + report_model = ReportModel.from_json(json_dict) + self.process_report_model(report_model) + if len(report_model.report_items) > 0 or len(report_model.exceptions) > 0: + self.process_result.message = exception_models_to_message(report_model.exceptions) + else: + self.process_result.message = "Report items not received. No exception received." + except json.JSONDecodeError as e: + self.process_result.completion_status = CompletionStatus.FAILED + if e.msg == "Expecting value": + self.process_result.message = f"Vendor did not return any data" + else: + self.process_result.message = f"JSON Exception: {e.msg}" + if self.show_debug: print( + f"{self.vendor.name}-{self.report_type}: JSON Exception: {e.msg}") + except RetryLaterException as e: + if not self.retried_request: + if self.show_debug: + print(f"{self.vendor.name}-{self.report_type}: Retry Later Exception: {e}") + print(f"{self.vendor.name}-{self.report_type}: Retrying in {RETRY_WAIT_TIME} seconds...") + QThread.currentThread().sleep(RETRY_WAIT_TIME) # Wait some time before retrying request + self.retried_request = True + self.make_request() + else: + self.process_result.message = "Retry later exception received" + message = exception_models_to_message(e.exceptions) + if message: self.process_result.message += "\n\n" + message + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.retry = True + if self.show_debug: print( + f"{self.vendor.name}-{self.report_type}: Retry Later Exception: {e}") + except ReportHeaderMissingException as e: + self.process_result.message = "Report_Header not received, no file was created" + message = exception_models_to_message(e.exceptions) + if message: self.process_result.message += "\n\n" + message + self.process_result.completion_status = CompletionStatus.FAILED + if self.show_debug: print( + f"{self.vendor.name}-{self.report_type}: Report Header Missing Exception: {e}") + except UnacceptableCodeException as e: + self.process_result.message = "Unsupported exception code received" + message = exception_models_to_message(e.exceptions) + if message: self.process_result.message += "\n\n" + message + self.process_result.completion_status = CompletionStatus.FAILED + if self.show_debug: print( + f"{self.vendor.name}-{self.report_type}: Unsupported Code Exception: {e}") + except Exception as e: + self.process_result.completion_status = CompletionStatus.FAILED + self.process_result.message = str(e) + if self.show_debug: print(f"{self.vendor.name}-{self.report_type}: Exception: {e}") + + def process_report_model(self, report_model: ReportModel): + """Processes the report model into a TSV report + + :param report_model: The report model + """ + report_type = report_model.report_header.report_id + major_report_type = report_model.report_header.major_report_type + report_items = report_model.report_items + report_rows = [] + file_dir = f"{self.save_dir}{self.begin_date.toString('yyyy')}/{self.vendor.name}/" + file_name = f"{self.begin_date.toString('yyyy')}_{self.vendor.name}_{report_type}.tsv" + file_path = f"{file_dir}{file_name}" + + if self.show_debug: print(f"{self.vendor.name}-{self.report_type}: Processing report") + + for report_item in report_items: + metric_row_dict = {} # Some metric_types have a list of components + # Some Item report metric_types have a list of components + components = [] # list({component_values_as_dict}) + + performance: PerformanceModel + for performance in report_item.performances: + begin_month = QDate.fromString(performance.period.begin_date, "yyyy-MM-dd").toString("MMM-yyyy") + + instance: InstanceModel + for instance in performance.instances: + metric_type = instance.metric_type + if metric_type not in metric_row_dict: + metric_row = ReportRow(self.begin_date, self.end_date) + metric_row.metric_type = metric_type + + metric_row_dict[metric_type] = metric_row + else: + metric_row = metric_row_dict[metric_type] + + if major_report_type == MajorReportType.PLATFORM: + report_item: PlatformReportItemModel + if report_item.platform: metric_row.platform = report_item.platform + if report_item.data_type: metric_row.data_type = report_item.data_type + if report_item.access_method: metric_row.access_method = report_item.access_method + + elif major_report_type == MajorReportType.DATABASE: + report_item: DatabaseReportItemModel + if report_item.database: metric_row.database = report_item.database + if report_item.publisher: metric_row.publisher = report_item.publisher + if report_item.platform: metric_row.platform = report_item.platform + if report_item.data_type: metric_row.data_type = report_item.data_type + if report_item.access_method: metric_row.access_method = report_item.access_method + + pub_id_str = "" + for pub_id in report_item.publisher_ids: + pub_id_str += f"{pub_id.item_type}:{pub_id.value}; " + if pub_id_str: metric_row.publisher_id = pub_id_str + + for item_id in report_item.item_ids: + if item_id.item_type == "Proprietary" or item_id.item_type == "Proprietary_ID": + metric_row.proprietary_id = item_id.value + + elif major_report_type == MajorReportType.TITLE: + report_item: TitleReportItemModel + if report_item.title: metric_row.title = report_item.title + if report_item.publisher: metric_row.publisher = report_item.publisher + if report_item.platform: metric_row.platform = report_item.platform + if report_item.data_type: metric_row.data_type = report_item.data_type + if report_item.section_type: metric_row.section_type = report_item.section_type + if report_item.yop: metric_row.yop = report_item.yop + if report_item.access_type: metric_row.access_type = report_item.access_type + if report_item.access_method: metric_row.access_method = report_item.access_method + + pub_id_str = "" + for pub_id in report_item.publisher_ids: + pub_id_str += f"{pub_id.item_type}:{pub_id.value}; " + if pub_id_str: metric_row.publisher_id = pub_id_str + + item_id: TypeValueModel + for item_id in report_item.item_ids: + item_type = item_id.item_type + + if item_type == "DOI": + metric_row.doi = item_id.value + elif item_type == "Proprietary" or item_type == "Proprietary_ID": + metric_row.proprietary_id = item_id.value + elif item_type == "ISBN": + metric_row.isbn = item_id.value + elif item_type == "Print_ISSN": + metric_row.print_issn = item_id.value + elif item_type == "Online_ISSN": + metric_row.online_issn = item_id.value + elif item_type == "Linking_ISSN": + metric_row.linking_issn = item_id.value + elif item_type == "URI": + metric_row.uri = item_id.value + + elif major_report_type == MajorReportType.ITEM: + report_item: ItemReportItemModel + if report_item.item: metric_row.item = report_item.item + if report_item.publisher: metric_row.publisher = report_item.publisher + if report_item.platform: metric_row.platform = report_item.platform + if report_item.data_type: metric_row.data_type = report_item.data_type + if report_item.yop: metric_row.yop = report_item.yop + if report_item.access_type: metric_row.access_type = report_item.access_type + if report_item.access_method: metric_row.access_method = report_item.access_method + + # Publisher ID + pub_id_str = "" + for pub_id in report_item.publisher_ids: + pub_id_str += f"{pub_id.item_type}:{pub_id.value}; " + if pub_id_str: metric_row.publisher_id = pub_id_str + + # Authors + authors_str = "" + item_contributor: ItemContributorModel + for item_contributor in report_item.item_contributors: + if item_contributor.item_type == "Author": + authors_str += f"{item_contributor.name}" + if item_contributor.identifier: + authors_str += f" ({item_contributor.identifier})" + authors_str += "; " + if authors_str: metric_row.authors = authors_str.rstrip("; ") + + # Publication date + item_date: TypeValueModel + for item_date in report_item.item_dates: + if item_date.item_type == "Publication_Date": + metric_row.publication_date = item_date.value + + # Article version + item_attribute: TypeValueModel + for item_attribute in report_item.item_attributes: + if item_attribute.item_type == "Article_Version": + metric_row.article_version = item_attribute.value + + # Base IDs + item_id: TypeValueModel + for item_id in report_item.item_ids: + item_type = item_id.item_type + + if item_type == "DOI": + metric_row.doi = item_id.value + elif item_type == "Proprietary" or item_type == "Proprietary_ID": + metric_row.proprietary_id = item_id.value + elif item_type == "ISBN": + metric_row.isbn = item_id.value + elif item_type == "Print_ISSN": + metric_row.print_issn = item_id.value + elif item_type == "Online_ISSN": + metric_row.online_issn = item_id.value + elif item_type == "Linking_ISSN": + metric_row.linking_issn = item_id.value + elif item_type == "URI": + metric_row.uri = item_id.value + + # Parent + if report_item.item_parent is not None: + item_parent: ItemParentModel + item_parent = report_item.item_parent + if item_parent.item_name: metric_row.parent_title = item_parent.item_name + if item_parent.data_type: metric_row.parent_data_type = item_parent.data_type + + # Authors + authors_str = "" + item_contributor: ItemContributorModel + for item_contributor in report_item.item_contributors: + if item_contributor.item_type == "Author": + authors_str += f"{item_contributor.name}" + if item_contributor.identifier: + authors_str += f" ({item_contributor.identifier})" + authors_str += "; " + authors_str.rstrip("; ") + if authors_str: metric_row.authors = authors_str + + # Publication date + item_date: TypeValueModel + for item_date in item_parent.item_dates: + if item_date.item_type == "Publication_Date" or item_date.item_type == "Pub_Date": + metric_row.parent_publication_date = item_date.value + + # Article version + item_attribute: TypeValueModel + for item_attribute in item_parent.item_attributes: + if item_attribute.item_type == "Article_Version": + metric_row.parent_article_version = item_attribute.value + + # Parent IDs + item_id: TypeValueModel + for item_id in item_parent.item_ids: + item_type = item_id.item_type + + if item_type == "DOI": + metric_row.parent_doi = item_id.value + elif item_type == "Proprietary" or item_type == "Proprietary_ID": + metric_row.parent_proprietary_id = item_id.value + elif item_type == "ISBN": + metric_row.parent_isbn = item_id.value + elif item_type == "Print_ISSN": + metric_row.parent_print_issn = item_id.value + elif item_type == "Online_ISSN": + metric_row.parent_online_issn = item_id.value + elif item_type == "URI": + metric_row.parent_uri = item_id.value + + else: + if self.show_debug: print( + f"{self.vendor.name}-{self.report_type}: Unexpected report type") + + month_counts = metric_row.month_counts + month_counts[begin_month] += instance.count + + metric_row.total_count += instance.count + + if major_report_type == MajorReportType.ITEM: + # Item Components + item_component: ItemComponentModel + for item_component in report_item.item_components: + component_dict = { + "component_title": "", + "component_authors": "", + "component_publication_date": "", + "component_data_type": "", + "component_doi": "", + "component_proprietary_id": "", + "component_isbn": "", + "component_print_issn": "", + "component_online_issn": "", + "component_uri": "" + } + + if item_component.item_name: component_dict["component_title"] = item_component.item_name + if item_component.data_type: component_dict["component_data_type"] = item_component.data_type + + # Authors + authors_str = "" + item_contributor: ItemContributorModel + for item_contributor in item_component.item_contributors: + if item_contributor.item_type == "Author": + authors_str += f"{item_contributor.name}" + if item_contributor.identifier: + authors_str += f" ({item_contributor.identifier})" + authors_str += "; " + authors_str.rstrip("; ") + if authors_str: component_dict["component_authors"] = authors_str + + # Publication date + item_date: TypeValueModel + for item_date in item_component.item_dates: + if item_date.item_type == "Publication_Date" or item_date.item_type == "Pub_Date": + component_dict["component_publication_date"] = item_date.value + + # Component IDs + item_id: TypeValueModel + for item_id in item_component.item_ids: + item_type = item_id.item_type + + if item_type == "DOI": + component_dict["component_doi"] = item_id.value + elif item_type == "Proprietary" or item_type == "Proprietary_ID": + component_dict["component_proprietary_id"] = item_id.value + elif item_type == "ISBN": + component_dict["component_isbn"] = item_id.value + elif item_type == "Print_ISSN": + component_dict["component_print_issn"] = item_id.value + elif item_type == "Online_ISSN": + component_dict["component_online_issn"] = item_id.value + elif item_type == "URI": + component_dict["component_uri"] = item_id.value + + components.append(component_dict) + + for metric_type in metric_row_dict: + metric_row = metric_row_dict[metric_type] + + if major_report_type == MajorReportType.ITEM: + if len(components) > 0 and \ + (metric_type == "Total_Item_Investigations" or metric_type == "Total_Item_Requests"): + for component in components: + row = copy.copy(metric_row) + row.component_title = component["component_title"] + row.component_authors = component["component_authors"] + row.component_publication_date = component["component_publication_date"] + row.component_data_type = component["component_data_type"] + row.component_doi = component["component_doi"] + row.component_proprietary_id = component["component_proprietary_id"] + row.component_isbn = component["component_isbn"] + row.component_print_issn = component["component_print_issn"] + row.component_online_issn = component["component_online_issn"] + row.component_uri = component["component_uri"] + report_rows.append(row) + else: + report_rows.append(metric_row) + else: + report_rows.append(metric_row) + + report_rows = self.sort_rows(report_rows, major_report_type) + self.save_tsv_files(report_model.report_header, report_rows) + + def sort_rows(self, report_rows: list, major_report_type: MajorReportType) -> list: + """Sorts the rows of the report + + :param report_rows: The report's rows + :param major_report_type: The major report type of this report type + """ + if major_report_type == MajorReportType.PLATFORM: + return sorted(report_rows, key=lambda row: row.platform.lower()) + elif major_report_type == MajorReportType.DATABASE: + return sorted(report_rows, key=lambda row: row.database.lower()) + elif major_report_type == MajorReportType.TITLE: + return sorted(report_rows, key=lambda row: (row.title.lower(), row.yop)) + elif major_report_type == MajorReportType.ITEM: + return sorted(report_rows, key=lambda row: row.item.lower()) + + def save_tsv_files(self, report_header, report_rows: list): + """Saves the TSV file in the target directories + + :param report_header: The SUSHI report header model + :param report_rows: The report's rows + """ + report_type = report_header.report_id + major_report_type = report_header.major_report_type + + if self.is_yearly: + file_dir = GeneralUtils.get_yearly_file_dir(self.save_dir, self.vendor.name, self.begin_date) + file_name = GeneralUtils.get_yearly_file_name(self.vendor.name, self.report_type, self.begin_date) + elif self.is_special: + file_dir = GeneralUtils.get_special_file_dir(self.save_dir, self.vendor.name) + file_name = GeneralUtils.get_special_file_name(self.vendor.name, self.report_type, self.begin_date, + self.end_date) + else: + file_dir = GeneralUtils.get_other_file_dir(self.save_dir, self.vendor.name) + file_name = GeneralUtils.get_other_file_name(self.vendor.name, self.report_type, self.begin_date, + self.end_date) + + # Save user tsv file + if not path.isdir(file_dir): + makedirs(file_dir) + + file_path = f"{file_dir}{file_name}" + file = open(file_path, 'w', encoding="utf-8", newline='') + if self.is_yearly and self.is_master: + self.add_report_header_to_file(report_header, file, False) + else: + self.add_report_header_to_file(report_header, file, True) + + if not self.add_report_rows_to_file(report_type, report_rows, file, False): + self.process_result.completion_status = CompletionStatus.WARNING + + file.close() + self.process_result.file_name = file_name + self.process_result.file_dir = file_dir + self.process_result.file_path = file_path + self.process_result.year = self.begin_date.toString('yyyy') + + # Save protected tsv file + if self.is_yearly: + protected_file_dir = GeneralUtils.get_yearly_file_dir(PROTECTED_DATABASE_FILE_DIR, self.vendor.name, + self.begin_date) + if not path.isdir(protected_file_dir): + makedirs(protected_file_dir) + if platform.system() == "Windows": + ctypes.windll.kernel32.SetFileAttributesW(PROTECTED_DATABASE_FILE_DIR, 2) # Hide folder + + protected_file_path = f"{protected_file_dir}{file_name}" + protected_file = open(protected_file_path, 'w', encoding="utf-8", newline='') + self.add_report_header_to_file(report_header, protected_file, True) + self.add_report_rows_to_file(report_type, report_rows, protected_file, True) + + protected_file.close() + self.process_result.protected_file_path = protected_file_path + + def add_report_header_to_file(self, report_header: ReportHeaderModel, file, include_attributes: bool): + """Adds the report header to a TSV file + + :param report_header: The report header model + :param file: The TSV file to write to + :param include_attributes: Include the Report_Attributes value + """ + tsv_writer = csv.writer(file, delimiter='\t') + tsv_writer.writerow(["Report_Name", report_header.report_name]) + tsv_writer.writerow(["Report_ID", report_header.report_id]) + tsv_writer.writerow(["Release", report_header.release]) + tsv_writer.writerow(["Institution_Name", report_header.institution_name]) + + institution_ids_str = "" + for institution_id in report_header.institution_ids: + institution_ids_str += f"{institution_id.value}; " + tsv_writer.writerow(["Institution_ID", institution_ids_str.rstrip("; ")]) + + metric_types_str = "" + reporting_period_str = "" + report_filters_str = "" + for report_filter in report_header.report_filters: + if report_filter.name == "Metric_Type": + metric_types_str += f"{report_filter.value}; " + elif report_filter.name == "Begin_Date" or report_filter.name == "End_Date": + reporting_period_str += f"{report_filter.name}={report_filter.value}; " + else: + report_filters_str += f"{report_filter.name}={report_filter.value}; " + tsv_writer.writerow(["Metric_Types", metric_types_str.replace("|", "; ").rstrip("; ")]) + tsv_writer.writerow(["Report_Filters", report_filters_str.rstrip("; ")]) + + report_attributes_str = "" + if include_attributes: + for report_attribute in report_header.report_attributes: + report_attributes_str += f"{report_attribute.name}={report_attribute.value}; " + tsv_writer.writerow(["Report_Attributes", report_attributes_str.rstrip("; ")]) + + exceptions_str = "" + for exception in report_header.exceptions: + exceptions_str += f"{exception.code}: {exception.message} ({exception.data}); " + tsv_writer.writerow(["Exceptions", exceptions_str.rstrip("; ")]) + + tsv_writer.writerow(["Reporting_Period", reporting_period_str.rstrip("; ")]) + tsv_writer.writerow(["Created", report_header.created]) + tsv_writer.writerow(["Created_By", report_header.created_by]) + tsv_writer.writerow([]) + + def add_report_rows_to_file(self, report_type: str, report_rows: list, file, include_all_attributes: bool) -> bool: + """Adds the report's rows to a TSV file + + :param report_type: The report type + :param report_rows: The report's rows + :param file: The TSV file to write to + :param include_all_attributes: Option to include all possible attributes for this report type to the report + """ + column_names = [] + row_dicts = [] + + if report_type == "PR": + column_names += ["Platform"] + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["data_type"][0]: column_names.append("Data_Type") + if special_options_dict["access_method"][0]: column_names.append("Access_Method") + elif include_all_attributes: + column_names.append("Data_Type") + column_names.append("Access_Method") + + row: ReportRow + for row in report_rows: + row_dict = {"Platform": row.platform} + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["data_type"][0]: row_dict["Data_Type"] = row.data_type + if special_options_dict["access_method"][0]: row_dict["Access_Method"] = row.access_method + + if not special_options_dict["exclude_monthly_details"][0]: + row_dict.update(row.month_counts) + else: + if include_all_attributes: + row_dict["Data_Type"] = row.data_type + row_dict["Access_Method"] = row.access_method + row_dict.update(row.month_counts) + + row_dict.update({"Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count}) + row_dicts.append(row_dict) + + elif report_type == "PR_P1": + column_names += ["Platform"] + + row: ReportRow + for row in report_rows: + row_dict = {"Platform": row.platform, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "DR": + column_names += ["Database", "Publisher", "Publisher_ID", "Platform", "Proprietary_ID"] + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["data_type"][0]: column_names.append("Data_Type") + if special_options_dict["access_method"][0]: column_names.append("Access_Method") + elif include_all_attributes: + column_names.append("Data_Type") + column_names.append("Access_Method") + + row: ReportRow + for row in report_rows: + row_dict = {"Database": row.database, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "Proprietary_ID": row.proprietary_id} + + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["data_type"][0]: row_dict["Data_Type"] = row.data_type + if special_options_dict["access_method"][0]: row_dict["Access_Method"] = row.access_method + + if not special_options_dict["exclude_monthly_details"][0]: + row_dict.update(row.month_counts) + else: + if include_all_attributes: + row_dict["Data_Type"] = row.data_type + row_dict["Access_Method"] = row.access_method + row_dict.update(row.month_counts) + + row_dict.update({"Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count}) + row_dicts.append(row_dict) + + elif report_type == "DR_D1" or report_type == "DR_D2": + column_names += ["Database", "Publisher", "Publisher_ID", "Platform", "Proprietary_ID"] + + row: ReportRow + for row in report_rows: + row_dict = {"Database": row.database, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "Proprietary_ID": row.proprietary_id, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "TR": + column_names += ["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Proprietary_ID", "ISBN", + "Print_ISSN", "Online_ISSN", "URI"] + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["data_type"][0]: column_names.append("Data_Type") + if special_options_dict["section_type"][0]: column_names.append("Section_Type") + if special_options_dict["yop"][0]: column_names.append("YOP") + if special_options_dict["access_type"][0]: column_names.append("Access_Type") + if special_options_dict["access_method"][0]: column_names.append("Access_Method") + elif include_all_attributes: + column_names.append("Data_Type") + column_names.append("Section_Type") + column_names.append("YOP") + column_names.append("Access_Type") + column_names.append("Access_Method") + + row: ReportRow + for row in report_rows: + row_dict = {"Title": row.title, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "ISBN": row.isbn, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri} + + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["data_type"][0]: row_dict["Data_Type"] = row.data_type + if special_options_dict["section_type"][0]: row_dict["Section_Type"] = row.section_type + if special_options_dict["yop"][0]: row_dict["YOP"] = row.yop + if special_options_dict["access_type"][0]: row_dict["Access_Type"] = row.access_type + if special_options_dict["access_method"][0]: row_dict["Access_Method"] = row.access_method + + if not special_options_dict["exclude_monthly_details"][0]: + row_dict.update(row.month_counts) + else: + if include_all_attributes: + row_dict["Data_Type"] = row.data_type + row_dict["Section_Type"] = row.section_type + row_dict["YOP"] = row.yop + row_dict["Access_Type"] = row.access_type + row_dict["Access_Method"] = row.access_method + row_dict.update(row.month_counts) + + row_dict.update({"Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count}) + row_dicts.append(row_dict) + + elif report_type == "TR_B1" or report_type == "TR_B2": + column_names += ["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Proprietary_ID", "ISBN", + "Print_ISSN", "Online_ISSN", "URI", "YOP"] + + row: ReportRow + for row in report_rows: + row_dict = {"Title": row.title, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "ISBN": row.isbn, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri, + "YOP": row.yop, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "TR_B3": + column_names += ["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Proprietary_ID", "ISBN", + "Print_ISSN", "Online_ISSN", "URI", "YOP", "Access_Type"] + + row: ReportRow + for row in report_rows: + row_dict = {"Title": row.title, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "ISBN": row.isbn, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri, + "YOP": row.yop, + "Access_Type": row.access_type, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "TR_J1" or report_type == "TR_J2": + column_names += ["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Proprietary_ID", "Print_ISSN", + "Online_ISSN", "URI"] + + row: ReportRow + for row in report_rows: + row_dict = {"Title": row.title, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "TR_J3": + column_names += ["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Proprietary_ID", "Print_ISSN", + "Online_ISSN", "URI", "Access_Type"] + + row: ReportRow + for row in report_rows: + row_dict = {"Title": row.title, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri, + "Access_Type": row.access_type, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "TR_J4": + column_names += ["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Proprietary_ID", "Print_ISSN", + "Online_ISSN", "URI", "YOP"] + + row: ReportRow + for row in report_rows: + row_dict = {"Title": row.title, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri, + "YOP": row.yop, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "IR": + column_names += ["Item", "Publisher", "Publisher_ID", "Platform"] + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["authors"][0]: column_names.append("Authors") + if special_options_dict["publication_date"][0]: column_names.append("Publication_Date") + if special_options_dict["article_version"][0]: column_names.append("Article_version") + elif include_all_attributes: + column_names.append("Authors") + column_names.append("Publication_Date") + column_names.append("Article_version") + column_names += ["DOI", "Proprietary_ID", "ISBN", "Print_ISSN", "Online_ISSN", "URI"] + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["include_parent_details"][0]: + column_names += ["Parent_Title", "Parent_Authors", "Parent_Publication_Date", + "Parent_Article_Version", "Parent_Data_Type", "Parent_DOI", + "Parent_Proprietary_ID", "Parent_ISBN", "Parent_Print_ISSN", "Parent_Online_ISSN", + "Parent_URI"] + if special_options_dict["include_component_details"][0]: + column_names += ["Component_Title", "Component_Authors", "Component_Publication_Date", + "Component_Data_Type", "Component_DOI", "Component_Proprietary_ID", + "Component_ISBN", "Component_Print_ISSN", "Component_Online_ISSN", "Component_URI"] + if special_options_dict["data_type"][0]: column_names.append("Data_Type") + if special_options_dict["yop"][0]: column_names.append("YOP") + if special_options_dict["access_type"][0]: column_names.append("Access_Type") + if special_options_dict["access_method"][0]: column_names.append("Access_Method") + elif include_all_attributes: + column_names += ["Parent_Title", "Parent_Authors", "Parent_Publication_Date", "Parent_Article_Version", + "Parent_Data_Type", "Parent_DOI", "Parent_Proprietary_ID", "Parent_ISBN", + "Parent_Print_ISSN", "Parent_Online_ISSN", "Parent_URI"] + column_names += ["Component_Title", "Component_Authors", "Component_Publication_Date", + "Component_Data_Type", "Component_DOI", "Component_Proprietary_ID", "Component_ISBN", + "Component_Print_ISSN", "Component_Online_ISSN", "Component_URI"] + column_names.append("Data_Type") + column_names.append("YOP") + column_names.append("Access_Type") + column_names.append("Access_Method") + + row: ReportRow + for row in report_rows: + row_dict = {"Item": row.item, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "ISBN": row.isbn, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri} + + if self.is_special: + special_options_dict = self.special_options.__dict__ + if special_options_dict["authors"][0]: row_dict["Authors"] = row.authors + if special_options_dict["publication_date"][0]: row_dict["Publication_Date"] = row.publication_date + if special_options_dict["article_version"][0]: row_dict["Article_version"] = row.article_version + if special_options_dict["include_parent_details"][0]: + row_dict.update({"Parent_Title": row.parent_title, + "Parent_Authors": row.parent_authors, + "Parent_Publication_Date": row.parent_publication_date, + "Parent_Article_Version": row.parent_article_version, + "Parent_Data_Type": row.parent_data_type, + "Parent_DOI": row.parent_doi, + "Parent_Proprietary_ID": row.parent_proprietary_id, + "Parent_ISBN": row.parent_isbn, + "Parent_Print_ISSN": row.parent_print_issn, + "Parent_Online_ISSN": row.parent_online_issn, + "Parent_URI": row.parent_uri}) + if special_options_dict["include_component_details"][0]: + row_dict.update({"Component_Title": row.component_title, + "Component_Authors": row.component_authors, + "Component_Publication_Date": row.component_publication_date, + "Component_Data_Type": row.component_data_type, + "Component_DOI": row.component_doi, + "Component_Proprietary_ID": row.component_proprietary_id, + "Component_ISBN": row.component_isbn, + "Component_Print_ISSN": row.component_print_issn, + "Component_Online_ISSN": row.component_online_issn, + "Component_URI": row.component_uri}) + if special_options_dict["data_type"][0]: row_dict["Data_Type"] = row.data_type + if special_options_dict["yop"][0]: row_dict["YOP"] = row.yop + if special_options_dict["access_type"][0]: row_dict["Access_Type"] = row.access_type + if special_options_dict["access_method"][0]: row_dict["Access_Method"] = row.access_method + + if not special_options_dict["exclude_monthly_details"][0]: + row_dict.update(row.month_counts) + else: + if include_all_attributes: + row_dict["Authors"] = row.authors + row_dict["Publication_Date"] = row.publication_date + row_dict["Article_version"] = row.article_version + row_dict.update({"Parent_Title": row.parent_title, + "Parent_Authors": row.parent_authors, + "Parent_Publication_Date": row.parent_publication_date, + "Parent_Article_Version": row.parent_article_version, + "Parent_Data_Type": row.parent_data_type, + "Parent_DOI": row.parent_doi, + "Parent_Proprietary_ID": row.parent_proprietary_id, + "Parent_ISBN": row.parent_isbn, + "Parent_Print_ISSN": row.parent_print_issn, + "Parent_Online_ISSN": row.parent_online_issn, + "Parent_URI": row.parent_uri}) + row_dict.update({"Component_Title": row.component_title, + "Component_Authors": row.component_authors, + "Component_Publication_Date": row.component_publication_date, + "Component_Data_Type": row.component_data_type, + "Component_DOI": row.component_doi, + "Component_Proprietary_ID": row.component_proprietary_id, + "Component_ISBN": row.component_isbn, + "Component_Print_ISSN": row.component_print_issn, + "Component_Online_ISSN": row.component_online_issn, + "Component_URI": row.component_uri}) + row_dict["Data_Type"] = row.data_type + row_dict["YOP"] = row.yop + row_dict["Access_Type"] = row.access_type + row_dict["Access_Method"] = row.access_method + row_dict.update(row.month_counts) + + row_dict.update({"Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count}) + row_dicts.append(row_dict) + + elif report_type == "IR_A1": + column_names += ["Item", "Publisher", "Publisher_ID", "Platform", "Authors", "Publication_Date", + "Article_version", "DOI", "Proprietary_ID", "Print_ISSN", "Online_ISSN", "URI", + "Parent_Title", "Parent_Authors", "Parent_Article_Version", "Parent_DOI", + "Parent_Proprietary_ID", "Parent_Print_ISSN", "Parent_Online_ISSN", "Parent_URI", + "Access_Type"] + + row: ReportRow + for row in report_rows: + row_dict = {"Item": row.item, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "Authors": row.authors, + "Publication_Date": row.publication_date, + "Article_version": row.article_version, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "Print_ISSN": row.print_issn, + "Online_ISSN": row.online_issn, + "URI": row.uri, + "Parent_Title": row.parent_title, + "Parent_Authors": row.parent_authors, + "Parent_Article_Version": row.parent_article_version, + "Parent_DOI": row.parent_doi, + "Parent_Proprietary_ID": row.parent_proprietary_id, + "Parent_Print_ISSN": row.parent_print_issn, + "Parent_Online_ISSN": row.parent_online_issn, + "Parent_URI": row.parent_uri, + "Access_Type": row.access_type, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + elif report_type == "IR_M1": + column_names += ["Item", "Publisher", "Publisher_ID", "Platform", "DOI", "Proprietary_ID", "URI"] + + row: ReportRow + for row in report_rows: + row_dict = {"Item": row.item, + "Publisher": row.publisher, + "Publisher_ID": row.publisher_id, + "Platform": row.platform, + "DOI": row.doi, + "Proprietary_ID": row.proprietary_id, + "URI": row.uri, + "Metric_Type": row.metric_type, + "Reporting_Period_Total": row.total_count} + row_dict.update(row.month_counts) + + row_dicts.append(row_dict) + + column_names += ["Metric_Type", "Reporting_Period_Total"] + + if self.is_special: + special_options_dict = self.special_options.__dict__ + if not special_options_dict["exclude_monthly_details"][0]: + column_names += get_month_years(self.begin_date, self.end_date) + else: + column_names += get_month_years(self.begin_date, self.end_date) + + tsv_dict_writer = csv.DictWriter(file, column_names, delimiter='\t') + tsv_dict_writer.writeheader() + + if len(row_dicts) == 0: + return False + + tsv_dict_writer.writerows(row_dicts) + return True + + def save_json_file(self, json_string: str): + """Saves a raw JSON file of the report + + :param json_string: The JSON string + """ + file_dir = f"{PROTECTED_DATABASE_FILE_DIR}_json/{self.begin_date.toString('yyyy')}/{self.vendor.name}/" + file_name = f"{self.begin_date.toString('yyyy')}_{self.vendor.name}_{self.report_type}.json" + file_path = f"{file_dir}{file_name}" + + if not path.isdir(file_dir): + makedirs(file_dir) + + json_file = open(file_path, 'w', encoding="utf-8") + json_file.write(json_string) + json_file.close() + + def notify_worker_finished(self): + """Notifies any listeners that this worker has finished""" + self.worker_finished_signal.emit(self.worker_id) + +# El Psy Kongroo diff --git a/GeneralUtils.py b/GeneralUtils.py new file mode 100644 index 0000000..a06f61e --- /dev/null +++ b/GeneralUtils.py @@ -0,0 +1,140 @@ +import webbrowser +import shlex +import subprocess +import platform +import csv +from typing import Sequence, Any +from os import path, makedirs, system +from PyQt5.QtWidgets import QWidget, QMessageBox, QFileDialog +from PyQt5.QtCore import QDate + +main_window: QWidget = None + + +class JsonModel: + def from_json(self, json_dict: dict): + raise NotImplementedError("from_json method is not implemented") + + +def save_json_file(file_dir: str, file_name: str, json_string: str): + try: + if not path.isdir(file_dir): + makedirs(file_dir) + file = open(file_dir + file_name, 'w') + file.write(json_string) + file.close() + except IOError as e: + print(e) + + +def read_json_file(file_path: str) -> str: + json_string = "[]" + try: + file = open(file_path, 'r', encoding='utf-8-sig') + json_string = file.read() + file.close() + except IOError: + pass + finally: + return json_string + + +def show_message(message: str): + message_box = QMessageBox(main_window) + message_box.setMinimumSize(800, 800) + message_box.setWindowTitle("Info") + message_box.setText(message) + message_box.exec_() + + +def ask_confirmation(message: str = 'Are you sure you want to continue?') -> bool: + reply = QMessageBox.question(main_window, "Confirm", message, QMessageBox.Yes, QMessageBox.No) + return reply == QMessageBox.Yes + + +def open_file_or_dir(target_path: str): + if path.exists(target_path): + if platform.system() == "Darwin": + system("open " + shlex.quote(target_path)) + elif platform.system() == "Linux": + subprocess.call(["xdg-open", target_path]) + else: + webbrowser.open_new_tab(path.realpath(target_path)) + else: + show_message(f"\'{target_path}\' does not exist") + + +def choose_file(name_filters) -> str: + file_path = "" + dialog = QFileDialog(main_window, directory=".") + dialog.setFileMode(QFileDialog.ExistingFile) + dialog.setNameFilters(name_filters) + if dialog.exec_(): + file_path = dialog.selectedFiles()[0] + + return file_path + + +def choose_directory() -> str: + dir_path = "" + dialog = QFileDialog(main_window, directory=".") + dialog.setFileMode(QFileDialog.Directory) + if dialog.exec_(): + dir_path = dialog.selectedFiles()[0] + "/" + + return dir_path + + +def choose_save(name_filters) -> str: + file_path = "" + dialog = QFileDialog(main_window, directory=".") + dialog.setFileMode(QFileDialog.AnyFile) + dialog.setAcceptMode(QFileDialog.AcceptSave) + dialog.setNameFilters(name_filters) + if dialog.exec_(): + file_path = dialog.selectedFiles()[0] + + return file_path + + +def open_in_browser(url: str): + webbrowser.open_new_tab(url) + + +def get_yearly_file_dir(base_path: str, vendor_name: str, begin_date: QDate) -> str: + return f"{base_path}{begin_date.toString('yyyy')}/{vendor_name}/" + + +def get_yearly_file_name(vendor_name: str, report_type: str, begin_date: QDate) -> str: + return f"{begin_date.toString('yyyy')}_{vendor_name}_{report_type}.tsv" + + +def get_special_file_dir(base_path: str, vendor_name: str) -> str: + return f"{base_path}{vendor_name}/special/" + + +def get_special_file_name(vendor_name: str, report_type: str, begin_date: QDate, end_date: QDate) -> str: + return f"{vendor_name}_{report_type}_{begin_date.toString('yyyy-MMM')}_{end_date.toString('yyyy-MMM')}_S.tsv" + + +def get_other_file_dir(base_path: str, vendor_name: str) -> str: + return f"{base_path}{vendor_name}/" + + +def get_other_file_name(vendor_name: str, report_type: str, begin_date: QDate, end_date: QDate) -> str: + return f"{vendor_name}_{report_type}_{begin_date.toString('yyyy-MMM')}_{end_date.toString('yyyy-MMM')}.tsv" + + +def save_data_as_tsv(file_name: str, data: Sequence[Any]): + """Saves data in a TSV file + + :param file_name: the name and location to save the results at + :param data: the data to save in the file""" + file = open(file_name, 'w', newline="", encoding='utf-8-sig') + if file.mode == 'w': + output = csv.writer(file, delimiter='\t', quotechar='\"') + for row in data: + output.writerow(row) + file.close() + else: + print('Error: could not open file ' + file_name) \ No newline at end of file diff --git a/ImportFile.py b/ImportFile.py new file mode 100644 index 0000000..f237334 --- /dev/null +++ b/ImportFile.py @@ -0,0 +1,284 @@ +"""This module handles all operations involving importing reports.""" + +import shutil +import platform +import ctypes +import csv +from os import path, makedirs +from PyQt5.QtCore import QModelIndex, QDate, Qt +from PyQt5.QtWidgets import QWidget, QDialog, QDialogButtonBox +from PyQt5.QtGui import QStandardItemModel, QStandardItem +from PyQt5 import QtWidgets + +import GeneralUtils +from Constants import * +from ui import ImportReportTab, ReportResultWidget +from ManageVendors import Vendor +from FetchData import ALL_REPORTS, CompletionStatus +from Settings import SettingsModel +from ManageDB import UpdateDatabaseWorker + + +class ProcessResult: + """This holds the results of an import process + + :param vendor: The target vendor + :param report_type: The target report type + """ + def __init__(self, vendor: Vendor, report_type: str): + self.vendor = vendor + self.report_type = report_type + self.completion_status = CompletionStatus.SUCCESSFUL + self.message = "" + self.file_name = "" + self.file_dir = "" + self.file_path = "" + + +class ImportReportController: + """Controls the Import Report tab + + :param vendors: The list of vendors in the system + :param settings: The user's settings + :param import_report_widget: The import report widget. + :param import_report_ui: The UI for the import_report_widget. + """ + def __init__(self, vendors: list, settings: SettingsModel, import_report_widget: QWidget, + import_report_ui: ImportReportTab.Ui_import_report_tab): + + # region General + self.import_report_widget = import_report_widget + self.vendors = vendors + self.date = QDate.currentDate() + self.selected_vendor_index = -1 + self.selected_report_type_index = -1 + self.selected_file_path: str = "" + self.settings = settings + self.result_dialog = None + # endregion + + # region Vendors + self.vendor_list_view = import_report_ui.vendors_list_view_import + self.vendor_list_model = QStandardItemModel(self.vendor_list_view) + self.vendor_list_view.setModel(self.vendor_list_model) + self.vendor_list_view.clicked.connect(self.on_vendor_selected) + self.update_vendors_ui() + # endregion + + # region Report Types + self.report_type_list_view = import_report_ui.report_types_list_view_import + self.report_type_list_model = QStandardItemModel(self.report_type_list_view) + self.report_type_list_view.setModel(self.report_type_list_model) + self.report_type_list_view.clicked.connect(self.on_report_type_selected) + for report_type in ALL_REPORTS: + item = QStandardItem(report_type) + item.setEditable(False) + self.report_type_list_model.appendRow(item) + # endregion + + # region Others + self.year_date_edit = import_report_ui.report_year_date_edit + self.year_date_edit.setDate(self.date) + self.year_date_edit.dateChanged.connect(self.on_date_changed) + + self.select_file_btn = import_report_ui.select_file_button + self.select_file_btn.clicked.connect(self.on_select_file_clicked) + + self.selected_file_edit = import_report_ui.selected_file_edit + + self.import_report_button = import_report_ui.import_report_button + self.import_report_button.clicked.connect(self.on_import_clicked) + # endregion + + def on_vendors_changed(self, vendors: list): + """Handles the signal emitted when the system's vendor list is updated + + :param vendors: An updated list of the system's vendors + """ + self.selected_vendor_index = -1 + self.update_vendors(vendors) + self.update_vendors_ui() + + def update_vendors(self, vendors: list): + """ Updates the local copy of vendors that support report import + + :param vendors: A list of vendors + """ + self.vendors = vendors + + def update_vendors_ui(self): + """Updates the UI to show vendors that support report import""" + self.vendor_list_model.clear() + for vendor in self.vendors: + item = QStandardItem(vendor.name) + item.setEditable(False) + self.vendor_list_model.appendRow(item) + + def on_vendor_selected(self, model_index: QModelIndex): + """Handles the signal emitted when a vendor is selected""" + self.selected_vendor_index = model_index.row() + + def on_report_type_selected(self, model_index: QModelIndex): + """Handles the signal emitted when a report type is selected""" + self.selected_report_type_index = model_index.row() + + def on_date_changed(self, date: QDate): + """Handles the signal emitted when the target date is changed""" + self.date = date + + def on_select_file_clicked(self): + """Handles the signal emitted when the select file button is clicked""" + file_path = GeneralUtils.choose_file(TSV_FILTER + CSV_FILTER) + if file_path: + self.selected_file_path = file_path + self.selected_file_edit.setText(file_path) + + def on_import_clicked(self): + """Handles the signal emitted when the import button is clicked""" + if self.selected_vendor_index == -1: + GeneralUtils.show_message("Select a vendor") + return + elif self.selected_report_type_index == -1: + GeneralUtils.show_message("Select a report type") + return + elif self.selected_file_path == "": + GeneralUtils.show_message("Select a file") + return + + vendor = self.vendors[self.selected_vendor_index] + report_type = ALL_REPORTS[self.selected_report_type_index] + + process_result = self.import_report(vendor, report_type) + self.show_result(process_result) + + def import_report(self, vendor: Vendor, report_type: str) -> ProcessResult: + """ Imports the selected file using the selected parameters + + :param vendor: The target vendor + :param report_type: The target report type + :raises Exception: If anything goes wrong while importing the report + """ + process_result = ProcessResult(vendor, report_type) + + try: + + dest_file_dir = GeneralUtils.get_yearly_file_dir(self.settings.yearly_directory, vendor.name, self.date) + dest_file_name = GeneralUtils.get_yearly_file_name(vendor.name, report_type, self.date) + dest_file_path = f"{dest_file_dir}{dest_file_name}" + + # Verify that dest_file_dir exists + if not path.isdir(dest_file_dir): + makedirs(dest_file_dir) + + # Validate report header + delimiter = DELIMITERS[self.selected_file_path[-4:].lower()] + file = open(self.selected_file_path, 'r', encoding='utf-8-sig') + reader = csv.reader(file, delimiter=delimiter, quotechar='\"') + if file.mode == 'r': + header = {} + for row in range(HEADER_ROWS): # reads header row data + cells = next(reader) + if cells: + key = cells[0].lower() + if key != HEADER_ENTRIES[row]: + raise Exception('File has invalid header (missing row ' + HEADER_ENTRIES[row] + ')') + else: + header[key] = cells[1].strip() + else: + raise Exception('File has invalid header (missing row ' + HEADER_ENTRIES[row] + ')') + for row in range(BLANK_ROWS): + cells = next(reader) + if cells: + if cells[0].strip(): + raise Exception('File has invalid header (not enough blank rows)') + if header['report_id'] != report_type: + raise Exception('File has invalid header (wrong Report_Id)') + if not header['created']: + raise Exception('File has invalid header (no Created date)') + else: + raise Exception('Could not open file') + + # Copy selected_file_path to dest_file_path + self.copy_file(self.selected_file_path, dest_file_path) + + process_result.file_dir = dest_file_dir + process_result.file_name = dest_file_name + process_result.file_path = dest_file_path + process_result.completion_status = CompletionStatus.SUCCESSFUL + + # Save protected tsv file + protected_file_dir = f"{PROTECTED_DATABASE_FILE_DIR}{self.date.toString('yyyy')}/{vendor.name}/" + if not path.isdir(protected_file_dir): + makedirs(protected_file_dir) + if platform.system() == "Windows": + ctypes.windll.kernel32.SetFileAttributesW(PROTECTED_DATABASE_FILE_DIR, 2) # Hide folder + + protected_file_path = f"{protected_file_dir}{dest_file_name}" + self.copy_file(self.selected_file_path, protected_file_path) + + # Add file to database + database_worker = UpdateDatabaseWorker([{'file': protected_file_path, + 'vendor': vendor.name, + 'year': int(self.date.toString('yyyy'))}], + False) + database_worker.work() + + except Exception as e: + process_result.message = f"Exception: {e}" + process_result.completion_status = CompletionStatus.FAILED + + return process_result + + def copy_file(self, origin_path: str, dest_path: str): + """Copies a file from origin_path to dest_path""" + shutil.copy2(origin_path, dest_path) + + def show_result(self, process_result: ProcessResult): + """Shows the result of the import process to the user + + :param process_result: The result of the import process + """ + self.result_dialog = QDialog(self.import_report_widget, flags=Qt.WindowCloseButtonHint) + self.result_dialog.setWindowTitle("Import Result") + vertical_layout = QtWidgets.QVBoxLayout(self.result_dialog) + vertical_layout.setContentsMargins(5, 5, 5, 5) + + report_result_widget = QWidget(self.result_dialog) + report_result_ui = ReportResultWidget.Ui_ReportResultWidget() + report_result_ui.setupUi(report_result_widget) + + button_box = QtWidgets.QDialogButtonBox(QDialogButtonBox.Ok, self.result_dialog) + button_box.setCenterButtons(True) + button_box.accepted.connect(self.result_dialog.accept) + + vendor = process_result.vendor + report_type = process_result.report_type + + report_result_ui.report_type_label.setText(f"{vendor.name} - {report_type}") + report_result_ui.success_label.setText(process_result.completion_status.value) + + if process_result.completion_status == CompletionStatus.SUCCESSFUL: + report_result_ui.message_label.hide() + report_result_ui.retry_frame.hide() + + report_result_ui.file_label.setText(f"Saved as: {process_result.file_name}") + report_result_ui.file_label.mousePressEvent = \ + lambda event: GeneralUtils.open_file_or_dir(process_result.file_path) + + report_result_ui.folder_button.clicked.connect( + lambda: GeneralUtils.open_file_or_dir(process_result.file_dir)) + + report_result_ui.success_label.setText("Successful!") + report_result_ui.retry_frame.hide() + + elif process_result.completion_status == CompletionStatus.FAILED: + report_result_ui.file_frame.hide() + report_result_ui.retry_frame.hide() + + report_result_ui.message_label.setText(process_result.message) + + vertical_layout.addWidget(report_result_widget) + vertical_layout.addWidget(button_box) + self.result_dialog.show() + + diff --git a/JsonUtils.py b/JsonUtils.py new file mode 100644 index 0000000..6ac7d8e --- /dev/null +++ b/JsonUtils.py @@ -0,0 +1,3 @@ +class JsonModel: + def from_json(self, json_dict: dict): + raise NotImplementedError("from_json method is not implemented") diff --git a/MainDriver.py b/MainDriver.py new file mode 100644 index 0000000..f2ffae1 --- /dev/null +++ b/MainDriver.py @@ -0,0 +1,144 @@ +import sys +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QFrame, QHBoxLayout, QPushButton +from PyQt5.QtGui import QIcon, QPixmap +from ui import MainWindow, ManageVendorsTab, SettingsTab, FetchReportsTab, FetchSpecialReportsTab, ImportReportTab,\ + SearchTab, VisualTab, CostsTab +from ManageVendors import ManageVendorsController +from FetchData import FetchReportsController, FetchSpecialReportsController +from ImportFile import ImportReportController +from Costs import CostsController +from Search import SearchController +from Settings import SettingsController, SettingsModel +from Visual import VisualController +import GeneralUtils +import ManageDB +from Constants import * + +# region debug_stuff + +def trap_exc_during_debug(*args): + # when app raises uncaught exception, print info + print(args) + + +# install exception hook: without this, uncaught exception would cause application to exit +sys.excepthook = trap_exc_during_debug + +# endregion + +if hasattr(Qt, 'AA_EnableHighDpiScaling'): + QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) + +if hasattr(Qt, 'AA_UseHighDpiPixmaps'): + QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) + +if __name__ == "__main__": + app = QApplication(sys.argv) + app.setStyleSheet("QWidget {font-family: Segoe UI; font-size: 12pt;}") + + main_window = QMainWindow() + main_window_ui = MainWindow.Ui_mainWindow() + main_window_ui.setupUi(main_window) + + GeneralUtils.main_window = main_window + + # region Setup Tab Controllers + settings_tab = QWidget(main_window) + settings_ui = SettingsTab.Ui_settings_tab() + settings_ui.setupUi(settings_tab) + settings_controller = SettingsController(settings_tab, settings_ui) + + manage_vendors_tab = QWidget(main_window) + manage_vendors_ui = ManageVendorsTab.Ui_manage_vendors_tab() + manage_vendors_ui.setupUi(manage_vendors_tab) + manage_vendors_controller = ManageVendorsController(manage_vendors_tab, manage_vendors_ui, settings_controller.settings) + + ManageDB.update_settings(settings_controller.settings) + ManageDB.first_time_setup() + + fetch_reports_tab = QWidget(main_window) + fetch_reports_ui = FetchReportsTab.Ui_fetch_reports_tab() + fetch_reports_ui.setupUi(fetch_reports_tab) + fetch_reports_controller = FetchReportsController(manage_vendors_controller.vendors, settings_controller.settings, + fetch_reports_tab, fetch_reports_ui) + + fetch_special_reports_tab = QWidget(main_window) + fetch_special_reports_ui = FetchSpecialReportsTab.Ui_fetch_special_reports_tab() + fetch_special_reports_ui.setupUi(fetch_special_reports_tab) + fetch_special_reports_controller = FetchSpecialReportsController(manage_vendors_controller.vendors, + settings_controller.settings, + fetch_special_reports_tab, + fetch_special_reports_ui) + + costs_tab = QWidget(main_window) + costs_tab_ui = CostsTab.Ui_costs_tab() + costs_tab_ui.setupUi(costs_tab) + costs_controller = CostsController(costs_tab_ui, settings_controller.settings) + + import_report_tab = QWidget(main_window) + import_report_ui = ImportReportTab.Ui_import_report_tab() + import_report_ui.setupUi(import_report_tab) + import_report_controller = ImportReportController(manage_vendors_controller.vendors, settings_controller.settings, + import_report_tab, import_report_ui) + + search_tab = QWidget(main_window) + search_ui = SearchTab.Ui_search_tab() + search_ui.setupUi(search_tab) + search_controller = SearchController(search_ui, settings_controller.settings) + + visual_tab = QWidget(main_window) + visual_ui = VisualTab.Ui_visual_tab() + visual_ui.setupUi(visual_tab) + visual_controller = VisualController(visual_ui, settings_controller.settings) + + # # endregion + + # region Connect Signals + manage_vendors_controller.vendors_changed_signal.connect(fetch_reports_controller.on_vendors_changed) + manage_vendors_controller.vendors_changed_signal.connect(fetch_special_reports_controller.on_vendors_changed) + manage_vendors_controller.vendors_changed_signal.connect(import_report_controller.on_vendors_changed) + manage_vendors_controller.vendors_changed_signal.connect(costs_controller.load_vendor_list) + manage_vendors_controller.vendors_changed_signal.connect(visual_controller.load_vendor_list) + + settings_controller.settings_changed_signal.connect(ManageDB.update_settings) + settings_controller.settings_changed_signal.connect(search_controller.update_settings) + settings_controller.settings_changed_signal.connect(costs_controller.update_settings) + settings_controller.settings_changed_signal.connect(visual_controller.update_settings) + + ManageDB.managedb_signal_handler.database_changed_signal.connect(costs_controller.database_updated) + ManageDB.managedb_signal_handler.database_changed_signal.connect(visual_controller.database_updated) + # endregion + + # region Add tabs to main window + main_window_ui.tab_widget.addTab(manage_vendors_tab, manage_vendors_tab.windowIcon(), "Manage Vendors") + main_window_ui.tab_widget.addTab(fetch_reports_tab, fetch_reports_tab.windowIcon(), "Fetch Reports") + main_window_ui.tab_widget.addTab(fetch_special_reports_tab, fetch_special_reports_tab.windowIcon(), + "Fetch Special Reports") + main_window_ui.tab_widget.addTab(import_report_tab, import_report_tab.windowIcon(), "Import Report") + main_window_ui.tab_widget.addTab(costs_tab, costs_tab.windowIcon(), "Costs") + main_window_ui.tab_widget.addTab(search_tab, search_tab.windowIcon(), "Search") + main_window_ui.tab_widget.addTab(visual_tab, visual_tab.windowIcon(), "Visual") + main_window_ui.tab_widget.addTab(settings_tab, settings_tab.windowIcon(), "Settings") + + main_window_ui.tab_widget.setCurrentIndex(1) + # endregion + + # region Status Bar + status_bar = main_window_ui.statusbar + + help_frame = QFrame(status_bar) + help_frame_layout = QHBoxLayout(help_frame) + help_frame_layout.setContentsMargins(-1, 2, -1, 5) + help_frame.setLayout(help_frame_layout) + help_button = QPushButton("Help", help_frame) + help_pixmap = QPixmap(":/ui/resources/help_icon.png") + help_button.setIcon(QIcon(help_pixmap)) + help_button.clicked.connect(lambda: GeneralUtils.open_in_browser(HELP_SITE)) + help_frame_layout.addWidget(help_button) + + status_bar.addWidget(help_frame) + # endregion + + main_window.show() + sys.exit(app.exec_()) diff --git a/ManageDB.py b/ManageDB.py new file mode 100644 index 0000000..4c8166c --- /dev/null +++ b/ManageDB.py @@ -0,0 +1,1071 @@ +import sqlite3 +import os +from typing import Tuple, Dict, Union +from PyQt5.QtCore import Qt, QObject, QThread, pyqtSignal +from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel + +from Constants import * +from GeneralUtils import * +from Settings import SettingsModel +from ui import UpdateDatabaseProgressDialog + + +class ManageDBSignalHandler(QObject): + """Object for emitting the database changed signal""" + database_changed_signal = pyqtSignal(int) + + def __init__(self): + super().__init__() + + def emit_database_changed_signal(self): + """Emits the database changed signal""" + self.database_changed_signal.emit(0) + + +managedb_signal_handler = ManageDBSignalHandler() + + +class ManageDBSettingsHandler: + """Class for holding the settings for ManageDB""" + settings = None + + +def update_settings(settings: SettingsModel): + """Called when the settings are saved + + :param settings: the new settings""" + ManageDBSettingsHandler.settings = settings + + +def get_report_fields_list(report: str) -> Sequence[Dict[str, Any]]: + """Gets the fields in the report table + + :param report: the kind of the report + :returns: list of fields in this report's table""" + report_fields = REPORT_TYPE_SWITCHER[report[:2]]['report_fields'] + fields = [] + for field in report_fields: # fields specific to this report + if report in field[REPORTS_KEY]: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], OPTIONS_KEY: field[OPTIONS_KEY]}) + for field in ALL_REPORT_FIELDS: # fields in all reports + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], OPTIONS_KEY: field[OPTIONS_KEY]}) + return tuple(fields) + + +def get_view_report_fields_list(report: str) -> Sequence[Dict[str, Any]]: + """Gets the fields in the report month view + + :param report: the kind of the report + :returns: list of fields in this report's month view""" + report_fields = REPORT_TYPE_SWITCHER[report[:2]]['report_fields'] + fields = [] + for field in report_fields: # fields specific to this report + if report in field[REPORTS_KEY]: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], + SOURCE_KEY: report}) + for field in ALL_REPORT_FIELDS: # fields in all reports + if field[NAME_KEY] not in FIELDS_NOT_IN_VIEWS: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], + SOURCE_KEY: report}) + for field in COST_FIELDS: # cost table fields + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], + SOURCE_KEY: report[:2] + COST_TABLE_SUFFIX}) + fields.append({NAME_KEY: YEAR_TOTAL, TYPE_KEY: 'INTEGER', CALCULATION_KEY: YEAR_TOTAL_CALCULATION, + SOURCE_KEY: 'joined'}) # year total column + for key in sorted(MONTHS): # month columns + fields.append({NAME_KEY: MONTHS[key], TYPE_KEY: 'INTEGER', CALCULATION_KEY: MONTH_CALCULATION.format(key), + SOURCE_KEY: 'joined'}) + return tuple(fields) + + +def get_monthly_chart_report_fields_list(report: str) -> Sequence[Dict[str, Any]]: + """Gets the fields in the report chart + + :param report: the kind of the report + :returns: list of fields in this report's monthly chart""" + fields = [] + name_field = get_field_attributes(report, NAME_FIELD_SWITCHER[report[:2]]) # name field only + fields.append( + {NAME_KEY: name_field[NAME_KEY], TYPE_KEY: name_field[TYPE_KEY]}) + for field in ALL_REPORT_FIELDS: # fields in all reports + if field[NAME_KEY] not in FIELDS_NOT_IN_CHARTS: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY]}) + for key in sorted(MONTHS): # month columns + fields.append({NAME_KEY: MONTHS[key], TYPE_KEY: 'INTEGER', CALCULATION_KEY: MONTH_CALCULATION.format(key)}) + return tuple(fields) + + +def get_yearly_chart_report_fields_list(report: str) -> Sequence[Dict[str, Any]]: + """Gets the fields in the report chart + + :param report: the kind of the report + :returns: list of fields in this report's monthly chart""" + fields = [] + name_field = get_field_attributes(report, NAME_FIELD_SWITCHER[report[:2]]) # name field only + fields.append( + {NAME_KEY: name_field[NAME_KEY], TYPE_KEY: name_field[TYPE_KEY]}) + for field in ALL_REPORT_FIELDS: # fields in all reports + if field[NAME_KEY] not in FIELDS_NOT_IN_CHARTS: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY]}) + fields.append({NAME_KEY: YEAR_TOTAL, TYPE_KEY: 'INTEGER', + CALCULATION_KEY: YEAR_TOTAL_CALCULATION}) # year total column + return tuple(fields) + + +def get_cost_chart_report_fields_list(report: str) -> Sequence[Dict[str, Any]]: + """Gets the fields in the report chart + + :param report: the kind of the report + :returns: list of fields in this report's chart""" + fields = [] + name_field = get_field_attributes(report, NAME_FIELD_SWITCHER[report[:2]]) # name field only + fields.append( + {NAME_KEY: name_field[NAME_KEY], TYPE_KEY: name_field[TYPE_KEY]}) + for field in ALL_REPORT_FIELDS: # fields in all reports + if field[NAME_KEY] not in FIELDS_NOT_IN_CHARTS: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY]}) + for field in COST_FIELDS: # cost table fields + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY]}) + fields.append({NAME_KEY: YEAR_TOTAL, TYPE_KEY: 'INTEGER', + CALCULATION_KEY: 'SUM(' + YEAR_TOTAL + ')'}) # year total column + return tuple(fields) + + +def get_top_number_chart_report_fields_list(report: str) -> Sequence[Dict[str, Any]]: + """Gets the fields in the report top # chart + + :param report: the kind of the report + :returns: list of fields in this report's top # chart""" + fields = [] + name_field = get_field_attributes(report, NAME_FIELD_SWITCHER[report[:2]]) # name field only + fields.append({NAME_KEY: name_field[NAME_KEY], TYPE_KEY: name_field[TYPE_KEY]}) + for field in ALL_REPORT_FIELDS: # fields in all reports + if field[NAME_KEY] not in FIELDS_NOT_IN_TOP_NUMBER_CHARTS: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY]}) + fields.append({NAME_KEY: 'total_of_' + YEAR_TOTAL, TYPE_KEY: 'INTEGER', + CALCULATION_KEY: YEAR_TOTAL_CALCULATION}) # total over the entire period column + fields.append({NAME_KEY: RANKING, TYPE_KEY: 'INTEGER', + CALCULATION_KEY: RANKING_CALCULATION}) # rank of the total over the entire period column + return tuple(fields) + + +def get_cost_fields_list(report_type: str) -> Sequence[Dict[str, Any]]: + """Gets the fields in the report type cost table + + :param report_type: the type of the report (master report name) + :returns: list of fields in this report type's cost table""" + fields = [] + name_field = get_field_attributes(report_type, NAME_FIELD_SWITCHER[report_type]) # name field only + fields.append( + {NAME_KEY: name_field[NAME_KEY], TYPE_KEY: name_field[TYPE_KEY], OPTIONS_KEY: name_field[OPTIONS_KEY]}) + for field in ALL_REPORT_FIELDS: # fields in all reports + if field[NAME_KEY] in COSTS_KEY_FIELDS: + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], OPTIONS_KEY: field[OPTIONS_KEY]}) + for field in COST_FIELDS: # cost table fields + fields.append({NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], OPTIONS_KEY: field[OPTIONS_KEY]}) + return tuple(fields) + + +def get_field_attributes(report: str, field_name: str) -> Union[Dict[str, Any], None]: + """Gets the field attributes + + :param report: the kind of the report + :param field_name: the name of the field + :returns: attributes of the field""" + report_fields = REPORT_TYPE_SWITCHER[report[:2]]['report_fields'] + for field in report_fields: # fields specific to this report + if field[NAME_KEY] == field_name: + return {NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], OPTIONS_KEY: field[OPTIONS_KEY]} + for field in ALL_REPORT_FIELDS: # fields in all reports + if field[NAME_KEY] == field_name: + return {NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], OPTIONS_KEY: field[OPTIONS_KEY]} + for field in COST_FIELDS: # cost table fields + if field[NAME_KEY] == field_name: + return {NAME_KEY: field[NAME_KEY], TYPE_KEY: field[TYPE_KEY], OPTIONS_KEY: field[OPTIONS_KEY]} + return None + + +def create_table_sql_texts(report: str) -> str: + """Makes the SQL statement to create the tables from the table definition + + :param report: the kind of the report + :returns: the sql statement""" + sql_text = 'CREATE TABLE IF NOT EXISTS ' + report + '(' + report_fields = get_report_fields_list(report) + fields_and_options = [] + key_fields = [] + for field in report_fields: + field_text = field[NAME_KEY] + ' ' + field[TYPE_KEY] + if field[OPTIONS_KEY]: + field_text += ' ' + ' '.join(field[OPTIONS_KEY]) + fields_and_options.append(field_text) + if field[NAME_KEY] not in FIELDS_NOT_IN_KEYS: + key_fields.append(field[NAME_KEY]) + sql_text += '\n\t' + ', \n\t'.join(fields_and_options) + ',\n\tPRIMARY KEY(' + ', '.join(key_fields) + '));' + return sql_text + + +def create_view_sql_texts(report: str) -> str: + """Makes the SQL statement to create the views from the table definition + + :param report: the kind of the report + :returns: the sql statement""" + name_field = get_field_attributes(report[:2], NAME_FIELD_SWITCHER[report[:2]]) + sql_text = 'CREATE VIEW IF NOT EXISTS ' + report + VIEW_SUFFIX + ' AS SELECT' + report_fields = get_view_report_fields_list(report) + fields = [] + calcs = [] + key_fields = [] + for field in report_fields: + if CALCULATION_KEY not in field.keys(): + field_text = '' + if field[NAME_KEY] in COSTS_KEY_FIELDS or field[NAME_KEY] == name_field[NAME_KEY]: + key_fields.append(field[NAME_KEY]) + field_text = report + '.' + field_text += field[NAME_KEY] + fields.append(field_text) + else: + calcs.append(field[CALCULATION_KEY] + ' AS ' + field[NAME_KEY]) + sql_text += '\n\t' + ', \n\t'.join(fields) + ', \n\t' + ', \n\t'.join(calcs) + sql_text += '\nFROM ' + report + ' LEFT JOIN ' + report[:2] + COST_TABLE_SUFFIX + join_clauses = [] + for key_field in key_fields: + join_clauses.append(report + '.' + key_field + ' = ' + report[:2] + COST_TABLE_SUFFIX + '.' + key_field) + sql_text += ' ON ' + ' AND '.join(join_clauses) + sql_text += '\nGROUP BY ' + ', '.join(fields) + ';' + return sql_text + + +def create_cost_table_sql_texts(report_type: str) -> str: + """Makes the SQL statement to create the cost tables from the table definition + + :param report_type: the type of the report (master report name) + :returns: the sql statement""" + sql_text = 'CREATE TABLE IF NOT EXISTS ' + report_type + COST_TABLE_SUFFIX + '(' + report_fields = get_cost_fields_list(report_type) + name_field = get_field_attributes(report_type, NAME_FIELD_SWITCHER[report_type]) + fields_and_options = [] + key_fields = [] + for field in report_fields: + field_text = field[NAME_KEY] + ' ' + field[TYPE_KEY] + if field[OPTIONS_KEY]: + field_text += ' ' + ' '.join(field[OPTIONS_KEY]) + fields_and_options.append(field_text) + if field[NAME_KEY] in COSTS_KEY_FIELDS or field[NAME_KEY] == name_field[NAME_KEY]: + key_fields.append(field[NAME_KEY]) + sql_text += '\n\t' + ', \n\t'.join(fields_and_options) + sql_text += ',\n\tPRIMARY KEY(' + ', '.join(key_fields) + '));' + return sql_text + + +def replace_sql_text(file_name: str, report: str, data: Sequence[Dict[str, Any]]) \ + -> Tuple[str, Sequence[Sequence[Any]], str, Sequence[Sequence[Any]]]: + """Makes the SQL statements to delete old data from a table and 'replace or insert' data into a table + + :param file_name: the name of the file the data is from + :param report: the kind of the report + :param data: the data from the file + :returns: (sql_delete_text, delete_values, sql_replace_text, replace_values) a Tuple with the parameterized SQL + statement to delete the old data, the values for it, the parameterized SQL statement to 'replace or insert' + data into the table, and the values for it""" + sql_replace_text = 'REPLACE INTO ' + report + '(' + report_fields = get_report_fields_list(report) + fields = [] + for field in report_fields: + fields.append(field[NAME_KEY]) + sql_replace_text += ', '.join(fields) + ')' + sql_replace_text += '\nVALUES' + placeholders = ['?'] * len(fields) + sql_replace_text += '(' + ', '.join(placeholders) + ');' + replace_values = [] + for row in data: # gets data to fill parameters + row_values = [] + for key in fields: + value = None + if row.get(key): + value = row.get(key) + else: + value = '' # if empty, use empty string + row_values.append(value) + replace_values.append(row_values) + sql_delete_text = 'DELETE FROM ' + report + ' WHERE ' + 'file' + ' = ?;' + delete_values = ((file_name,),) + return sql_delete_text, tuple(delete_values), sql_replace_text, tuple(replace_values) + + +def update_vendor_name_sql_text(table: str, old_name: str, new_name: str) -> Tuple[str, Sequence[Sequence[Any]]]: + """Makes the SQL statement to update the vendor's name in a table + + :param table: the name of the table to replace in + :param old_name: the old name of the vendor + :param new_name: the new name of the vendor + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to update the vendor name in the table, + and the values for it""" + sql_text = 'UPDATE ' + table + ' SET' + values = [] + sql_text += '\n\t' + 'vendor' + ' = ?' + values.append(new_name) + if not table.endswith(COST_TABLE_SUFFIX): + sql_text += ',\n\t' + 'file' + ' = ' + 'REPLACE(' + 'file' + ', ' + '\"_\" || ? || \"_\"' + values.append(old_name) + sql_text += ', ' + '\"_\" || ? || \"_\")' + values.append(new_name) + sql_text += '\nWHERE ' + 'vendor' + ' = ?;' + values.append(old_name) + return sql_text, (values,) + + +def update_vendor_in_all_tables(old_name: str, new_name: str): + """Updates the vendor's name in all tables + + :param old_name: the old name of the vendor + :param new_name: the new name of the vendor""" + sql_texts = [] + for table in ALL_REPORTS: + sql_text, data = update_vendor_name_sql_text(table, old_name, new_name) + sql_texts.append({'sql_text': sql_text, 'data': data}) + for cost_table in REPORT_TYPE_SWITCHER.keys(): + sql_text, data = update_vendor_name_sql_text(cost_table + COST_TABLE_SUFFIX, old_name, new_name) + sql_texts.append({'sql_text': sql_text, 'data': data}) + + connection = create_connection(DATABASE_LOCATION) + if connection is not None: + for sql_text in sql_texts: + run_sql(connection, sql_text['sql_text'], sql_text['data'], emit_signal=False) + connection.close() + managedb_signal_handler.emit_database_changed_signal() + else: + print('Error, no connection') + + +def replace_costs_sql_text(report_type: str, data: Sequence[Dict[str, Any]]) -> Tuple[str, Sequence[Sequence[Any]]]: + """Makes the SQL statement to 'replace or insert' data into a cost table + + :param report_type: the type of the report (master report name) + :param data: the new data for the table + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to 'replace or insert' the costs, and the + values for it""" + sql_text = 'REPLACE INTO ' + report_type + COST_TABLE_SUFFIX + '(' + report_fields = get_cost_fields_list(report_type) + fields = [] + for field in report_fields: + fields.append(field[NAME_KEY]) + sql_text += ', '.join(fields) + ')' + sql_text += '\nVALUES' + placeholders = ['?'] * len(fields) + sql_text += '(' + ', '.join(placeholders) + ');' + values = [] + for row in data: # gets data to fill parameters + row_values = [] + for key in fields: + value = None + if row.get(key): + value = row.get(key) + else: + value = '' # if empty, use empty string + row_values.append(value) + values.append(row_values) + return sql_text, tuple(values) + + +def delete_costs_sql_text(report_type: str, vendor: str, year: int, name: str) -> Tuple[str, Sequence[Sequence[Any]]]: + """Makes the SQL statement to delete data from a cost table + + :param report_type: the type of the report (master report name) + :param vendor: the vendor name of the cost + :param year: the year of the cost + :param name: the name the cost is associated with (database/item/platform/title) + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to delete the costs row, and the values + for it""" + name_field = NAME_FIELD_SWITCHER[report_type] + values = [] + sql_text = 'DELETE FROM ' + report_type + COST_TABLE_SUFFIX + sql_text += '\nWHERE ' + sql_text += '\n\t' + 'vendor' + ' = ?' + values.append(vendor) + sql_text += '\n\tAND ' + 'year' + ' = ?' + values.append(year) + sql_text += '\n\tAND ' + name_field + ' = ?;' + values.append(name) + return sql_text, (values,) + + +def read_report_file(file_name: str, vendor: str, year: int) -> Union[Tuple[str, str, Sequence[Dict[str, Any]]], + None]: + """Reads a specific csv/tsv file and returns the kind of report and the values for inserting + + :param file_name: the name of the file the data is from + :param vendor: the vendor name of the data in the file + :param year: the year of the data in the file + :returns: (file_name, report, values) a Tuple with the file name, the kind of report, and the data from the file""" + if ManageDBSettingsHandler.settings.show_debug_messages: print('READ ' + file_name) + delimiter = DELIMITERS[file_name[-4:].lower()] + file = open(file_name, 'r', encoding='utf-8-sig') + reader = csv.reader(file, delimiter=delimiter, quotechar='\"') + if file.mode == 'r': + header = {} + for row in range(HEADER_ROWS): # reads header data + cells = next(reader) + key = cells[0].lower() + if len(cells) > 1: + header[key] = cells[1].strip() + else: + header[key] = None + if ManageDBSettingsHandler.settings.show_debug_messages: print(header) + for row in range(BLANK_ROWS): + next(reader) + column_headers = next(reader) + column_headers = list(map((lambda column_header: column_header.lower()), + column_headers)) # reads column headers + if ManageDBSettingsHandler.settings.show_debug_messages: print(column_headers) + values = [] + for cells in list(reader): + for month in MONTHS: # makes value from each month with metric > 0 for each row + month_header = MONTHS[month][:3].lower() + '-' + str(year) + if month_header in column_headers: + current_month = column_headers.index(month_header) + metric = int(cells[current_month]) + if metric > 0: + value = {} + last_column = column_headers.index(YEAR_TOTAL) + for i in range(last_column): # read columns before months + value[column_headers[i]] = cells[i] + if not value['metric_type']: # if no metric type column, use the metric type from header + value['metric_type'] = header['metric_types'] + # additional fields + value['year'] = year + value['month'] = month + value['metric'] = int(cells[current_month]) + value['vendor'] = vendor + value['updated_on'] = header['created'] + value['file'] = os.path.basename(file.name) + values.append(value) + return os.path.basename(file.name), header['report_id'], tuple(values) + else: + print('Error: could not open file ' + file_name) + + +def read_costs_file(file_name: str) -> Union[Sequence[Dict[str, Any]], None]: + """Reads a specific csv/tsv cost file and returns the values for inserting + + :param file_name: the name of the file the data is from + :returns: list of values from the file""" + if ManageDBSettingsHandler.settings.show_debug_messages: print('READ ' + file_name) + delimiter = DELIMITERS[file_name[-4:].lower()] + file = open(file_name, 'r', encoding='utf-8-sig') + reader = csv.reader(file, delimiter=delimiter, quotechar='\"') + if file.mode == 'r': + column_headers = next(reader) + column_headers = list(map((lambda column_header: column_header.lower()), + column_headers)) # reads column headers + if ManageDBSettingsHandler.settings.show_debug_messages: print(column_headers) + values = [] + for cells in list(reader): + value = {} + for i in range(len(cells)): # read columns + value[column_headers[i]] = cells[i] + values.append(value) + file.close() + return tuple(values) + else: + print('Error: could not open file ' + file_name) + + +def get_all_report_files() -> Sequence[Dict[str, Any]]: + """Gets the list of the report files in the protected directory + + :returns: list of report files""" + files = [] + for upper_directory in os.scandir(PROTECTED_DATABASE_FILE_DIR): # iterate over all files in the specified folder + if upper_directory.is_dir(): + for lower_directory in os.scandir(upper_directory): + if lower_directory.is_dir(): + directory_data = {FILE_SUBDIRECTORY_ORDER[0]: upper_directory.name, + FILE_SUBDIRECTORY_ORDER[1]: lower_directory.name} # get data from directory names + for file in os.scandir(lower_directory): + if file.name[-4:] in DELIMITERS: + files.append({'file': file.path, 'vendor': directory_data['vendor'], + 'year': directory_data['year']}) + return tuple(files) + + +def get_all_cost_files() -> Sequence[Dict[str, Any]]: + """Gets the list of the cost files in the costs directory + + :returns: list of cost files""" + files = [] + for file in os.scandir(COSTS_SAVE_FOLDER): # iterate over all files in the specified folder + if file.name[-4:] in DELIMITERS: + files.append({'file': file.path, 'report': file.name[:2]}) + return tuple(files) + + +def insert_single_file(file_path: str, vendor: str, year: int, emit_signal: bool = True): + """Inserts a single file's data into the database + + :param file_path: the path of the file the data is from + :param vendor: the vendor name of the data in the file + :param year: the year of the data in the file + :param emit_signal: whether to emit a signal upon completion""" + file, report, read_data = read_report_file(file_path, vendor, year) + delete, delete_data, replace, replace_data = replace_sql_text(file, report, read_data) + + connection = create_connection(DATABASE_LOCATION) + if connection is not None: + run_sql(connection, delete, delete_data, emit_signal=False) + run_sql(connection, replace, replace_data, emit_signal=False) + connection.close() + if emit_signal: # only emit the signal after the delete and replace operations both finish + managedb_signal_handler.emit_database_changed_signal() + else: + print('Error, no connection') + + +def insert_single_cost_file(report_type: str, file_path: str, emit_signal: bool = True): + """Inserts a single file's data into the database + + :param report_type: the type of the report (master report name) + :param file_path: the path of the file the data is from + :param emit_signal: whether to emit a signal upon completion""" + read_data = read_costs_file(file_path) + sql_text, data = replace_costs_sql_text(report_type, read_data) + + connection = create_connection(DATABASE_LOCATION) + if connection is not None: + run_sql(connection, sql_text, data, emit_signal) + connection.close() + else: + print('Error, no connection') + + +def search_sql_text(report: str, start_year: int, end_year: int, + search_parameters: Sequence[Sequence[Dict[str, Any]]]) -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to search the database based on a search + + :param report: the kind of the report + :param start_year: the starting year of the search + :param end_year: the ending year of the search + :param search_parameters: list of search parameters in POS form + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + clauses = [[{FIELD_KEY: 'year', COMPARISON_KEY: '>=', VALUE_KEY: start_year}], + [{FIELD_KEY: 'year', COMPARISON_KEY: '<=', VALUE_KEY: end_year}]] + clauses.extend(search_parameters) + clauses_texts = [] + data = [] + for clause in clauses: + sub_clauses_text = [] + for sub_clause in clause: + current_text = sub_clause[FIELD_KEY] + ' ' + sub_clause[COMPARISON_KEY] + if sub_clause[COMPARISON_KEY] not in NON_COMPARISONS: + current_text += ' ?' + data.append(sub_clause[VALUE_KEY]) + sub_clauses_text.append(current_text) + clauses_texts.append(tuple(sub_clauses_text)) + sql_text = get_sql_select_statement(('*',), (report + VIEW_SUFFIX,), tuple(clauses_texts)) + ';' + return sql_text, tuple(data) + + +def monthly_chart_search_sql_text(report: str, start_year: int, end_year: int, name: str, metric_type: str, + vendor: str) -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to search the database for monthly chart data + + :param report: the kind of the report + :param start_year: the starting year of the search + :param end_year: the ending year of the search + :param name: the name field (database/item/platform/title) value + :param metric_type: the metric type value + :param vendor: the vendor name you want to search for + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + chart_fields = get_monthly_chart_report_fields_list(report) + fields = [] + key_fields = [] + for field in chart_fields: + if CALCULATION_KEY not in field.keys(): + key_fields.append(field[NAME_KEY]) + fields.append(field[NAME_KEY]) + else: + fields.append(field[CALCULATION_KEY] + ' AS ' + field[NAME_KEY]) + clauses = [{FIELD_KEY: 'year', COMPARISON_KEY: '>=', VALUE_KEY: start_year}, + {FIELD_KEY: 'year', COMPARISON_KEY: '<=', VALUE_KEY: end_year}, + {FIELD_KEY: chart_fields[0][NAME_KEY], COMPARISON_KEY: 'LIKE', VALUE_KEY: name}, + {FIELD_KEY: 'metric_type', COMPARISON_KEY: 'LIKE', VALUE_KEY: metric_type}, + {FIELD_KEY: 'vendor', COMPARISON_KEY: 'LIKE', VALUE_KEY: vendor}] + clauses_texts = [] + data = [] + for clause in clauses: + clauses_texts.append((clause[FIELD_KEY] + ' ' + clause[COMPARISON_KEY] + ' ?',)) + data.append(clause[VALUE_KEY]) + sql_text = get_sql_select_statement(tuple(fields), (report,), tuple(clauses_texts), + group_by_fields=tuple(key_fields)) + ';' + return sql_text, tuple(data) + + +def yearly_chart_search_sql_text(report: str, start_year: int, end_year: int, name: str, metric_type: str, + vendor: str) -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to search the database for yearly chart data + + :param report: the kind of the report + :param start_year: the starting year of the search + :param end_year: the ending year of the search + :param name: the name field (database/item/platform/title) value + :param metric_type: the metric type value + :param vendor: the vendor name you want to search for + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + chart_fields = get_yearly_chart_report_fields_list(report) + fields = [] + key_fields = [] + for field in chart_fields: + if CALCULATION_KEY not in field.keys(): + key_fields.append(field[NAME_KEY]) + fields.append(field[NAME_KEY]) + else: + fields.append(field[CALCULATION_KEY] + ' AS ' + field[NAME_KEY]) + clauses = [{FIELD_KEY: 'year', COMPARISON_KEY: '>=', VALUE_KEY: start_year}, + {FIELD_KEY: 'year', COMPARISON_KEY: '<=', VALUE_KEY: end_year}, + {FIELD_KEY: chart_fields[0][NAME_KEY], COMPARISON_KEY: 'LIKE', VALUE_KEY: name}, + {FIELD_KEY: 'metric_type', COMPARISON_KEY: 'LIKE', VALUE_KEY: metric_type}, + {FIELD_KEY: 'vendor', COMPARISON_KEY: 'LIKE', VALUE_KEY: vendor}] + clauses_texts = [] + data = [] + for clause in clauses: + clauses_texts.append((clause[FIELD_KEY] + ' ' + clause[COMPARISON_KEY] + ' ?',)) + data.append(clause[VALUE_KEY]) + sql_text = get_sql_select_statement(tuple(fields), (report,), tuple(clauses_texts), + group_by_fields=tuple(key_fields)) + ';' + return sql_text, tuple(data) + + +def cost_chart_search_sql_text(report: str, start_year: int, end_year: int, name: str, metric_type: str, vendor: str) \ + -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to search the database for cost chart data + + :param report: the kind of the report + :param start_year: the starting year of the search + :param end_year: the ending year of the search + :param name: the name field (database/item/platform/title) value + :param metric_type: the metric type value + :param vendor: the vendor name you want to search for + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + chart_fields = get_cost_chart_report_fields_list(report) + fields = [] + key_fields = [] + for field in chart_fields: + if CALCULATION_KEY not in field.keys(): + key_fields.append(field[NAME_KEY]) + fields.append(field[NAME_KEY]) + else: + fields.append(field[CALCULATION_KEY] + ' AS ' + field[NAME_KEY]) + clauses = [{FIELD_KEY: 'year', COMPARISON_KEY: '>=', VALUE_KEY: start_year}, + {FIELD_KEY: 'year', COMPARISON_KEY: '<=', VALUE_KEY: end_year}, + {FIELD_KEY: chart_fields[0][NAME_KEY], COMPARISON_KEY: 'LIKE', VALUE_KEY: name}, + {FIELD_KEY: 'metric_type', COMPARISON_KEY: 'LIKE', VALUE_KEY: metric_type}, + {FIELD_KEY: 'vendor', COMPARISON_KEY: 'LIKE', VALUE_KEY: vendor}] + clauses_texts = [] + data = [] + for clause in clauses: + clauses_texts.append((clause[FIELD_KEY] + ' ' + clause[COMPARISON_KEY] + ' ?',)) + data.append(clause[VALUE_KEY]) + sql_text = get_sql_select_statement(tuple(fields), (report + VIEW_SUFFIX,), tuple(clauses_texts), + group_by_fields=tuple(key_fields)) + ';' + return sql_text, tuple(data) + + +def top_number_chart_search_sql_text(report: str, start_year: int, end_year: int, metric_type: str, + vendor: str = None, number: int = None) -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to search the database for ranking chart data + + :param report: the kind of the report + :param start_year: the starting year of the search + :param end_year: the ending year of the search + :param metric_type: the metric type value + :param vendor: the vendor name you want to search for + :param number: the number to show of the top months + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + name_field = get_field_attributes(report[:2], NAME_FIELD_SWITCHER[report[:2]]) + chart_fields = get_top_number_chart_report_fields_list(report) + fields = [] + key_fields = [] + for field in chart_fields: + if field[NAME_KEY] in CHART_KEY_FIELDS or field[NAME_KEY] == name_field[NAME_KEY]: + key_fields.append(field[NAME_KEY]) + if CALCULATION_KEY not in field.keys(): + fields.append(field[NAME_KEY]) + else: + fields.append(field[CALCULATION_KEY] + ' AS ' + field[NAME_KEY]) + clauses = [{FIELD_KEY: 'year', COMPARISON_KEY: '>=', VALUE_KEY: start_year}, + {FIELD_KEY: 'year', COMPARISON_KEY: '<=', VALUE_KEY: end_year}, + {FIELD_KEY: 'metric_type', COMPARISON_KEY: 'LIKE', VALUE_KEY: metric_type}] + if vendor: + clauses.append({FIELD_KEY: 'vendor', COMPARISON_KEY: 'LIKE', VALUE_KEY: vendor}) + clauses_texts = [] + data = [] + for clause in clauses: + clauses_texts.append((clause[FIELD_KEY] + ' ' + clause[COMPARISON_KEY] + ' ?',)) + data.append(clause[VALUE_KEY]) + if number: + data.append(number) + # runs a query as the source of the next query + sql_text = get_sql_select_statement(('*',), + ('(' + get_sql_select_statement(tuple(fields), (report,), tuple(clauses_texts), + group_by_fields=tuple(key_fields), + num_extra_tabs=1) + ')',), + ((RANKING + ' <= ?',),) if number else (), order_by_fields=(RANKING,)) + ';' + return sql_text, tuple(data) + + +def get_names_sql_text(report: str, vendor: str) -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to get all the unique name values for a report and vendor + + :param report: the kind of the report + :param vendor: the vendor name of the data in the file + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + name_field = NAME_FIELD_SWITCHER[report[:2]] + sql_text = get_sql_select_statement((name_field,), (report,), ((name_field + ' <> \"\"',), ('vendor' + ' = ?',)), + order_by_fields=(name_field + ' COLLATE NOCASE ASC',), distinct=True, + is_multiline=False) + ';' + data = (vendor,) + return sql_text, data + + +def get_names_with_costs_sql_text(report: str, vendor: str, start_year: int, end_year: int) \ + -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to get all the unique name values with costs data in a period for a report and vendor + + :param report: the kind of the report + :param vendor: the vendor name of the data in the file + :param start_year: the starting year to check for costs data for + :param end_year: the ending year to check for costs data for + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + name_field = NAME_FIELD_SWITCHER[report[:2]] + sql_text = get_sql_select_statement((name_field,), (report[:2] + COST_TABLE_SUFFIX,), + ((name_field + ' <> \"\"',), ('vendor' + ' = ?',), ('year' + ' >= ?',), + ('year' + ' <= ?',)), order_by_fields=(name_field + ' COLLATE NOCASE ASC',), + distinct=True, is_multiline=False) + ';' + data = (vendor, start_year, end_year) + return sql_text, data + + +def get_costs_sql_text(report_type: str, vendor: str, year: int, name: str) -> Tuple[str, Sequence[Any]]: + """Makes the SQL statement to get costs from the database + + :param report_type: the type of the report (master report name) + :param vendor: the vendor name of the cost + :param year: the year of the cost + :param name: the name the cost is associated with (database/item/platform/title) + :returns: (sql_text, values) a Tuple with the parameterized SQL statement to search the database, and the values + for it""" + name_field = NAME_FIELD_SWITCHER[report_type] + sql_text = get_sql_select_statement([field[NAME_KEY] for field in COST_FIELDS], (report_type + COST_TABLE_SUFFIX,), + (('vendor' + ' = ?',), ('year' + ' = ?',), (name_field + ' = ?',)), + is_multiline=False) + ';' + values = (vendor, year, name) + return sql_text, values + + +def get_sql_select_statement(select_fields: Sequence[str], from_tables: Sequence[str], + where_conditions: Sequence[Sequence[str]] = None, group_by_fields: Sequence[str] = None, + order_by_fields: Sequence[str] = None, distinct: bool = None, num_extra_tabs: int = 0, + is_multiline: bool = True) -> str: + """Makes a select SQL statement + + :param select_fields: a list of fields to get; use a list containing only '*' to get all the fields from the tables + :param from_tables: a list of tables to get fields from; assumes inner join + :param where_conditions: a list of lists of conditions for the WHERE keyword; assumes in POS form + :param group_by_fields: a list of fields for the GROUP BY keyword + :param order_by_fields: a list of fields for the ORDER BY keyword + :param distinct: whether to only get distinct rows from the database + :param num_extra_tabs: the number of tabs to put at the start of each line of the statement + :param is_multiline: whether or not to break the statement into multiple lines + :returns: the SQL statement to send the database""" + tabs = '\t' * num_extra_tabs + sql_text = 'SELECT' + if distinct: + sql_text += ' DISTINCT' + sql_text += '\n\t' + tabs if is_multiline else ' ' + sql_text += (',' + ('\n\t' + tabs if is_multiline else ' ')).join(select_fields) + sql_text += '\n' + tabs if is_multiline else ' ' + sql_text += 'FROM' + sql_text += '\n\t' + tabs if is_multiline else ' ' + sql_text += (',' + ('\n\t' + tabs if is_multiline else ' ')).join(from_tables) + if where_conditions: + sql_text += '\n' + tabs if is_multiline else ' ' + sql_text += 'WHERE' + sql_text += '\n\t' + tabs if is_multiline else ' ' + conditions_text = [] + for condition in where_conditions: + if len(condition) > 1: + conditions_text.append('(' + ' OR '.join(condition) + ')') + else: + conditions_text.append(condition[0]) + sql_text += (' AND' + ('\n\t' + tabs if is_multiline else ' ')).join(conditions_text) + if group_by_fields: + sql_text += '\n' + tabs if is_multiline else ' ' + sql_text += 'GROUP BY' + sql_text += '\n\t' + tabs if is_multiline else ' ' + sql_text += (',' + ('\n\t' + tabs if is_multiline else ' ')).join(group_by_fields) + if order_by_fields: + sql_text += '\n' + tabs if is_multiline else ' ' + sql_text += 'ORDER BY' + sql_text += '\n\t' + tabs if is_multiline else ' ' + sql_text += (',' + ('\n\t' + tabs if is_multiline else ' ')).join(order_by_fields) + return sql_text + + +def create_connection(db_file: str) -> sqlite3.Connection: + """Creates the connection to the database + + :param db_file: the file the database is in + :returns: the connection to the database""" + connection = None + try: + connection = sqlite3.connect(db_file) + return connection + except sqlite3.Error as error: + print(error) + connection.close() + return connection + + +def run_sql(connection: sqlite3.Connection, sql_text: str, data: Sequence[Sequence[Any]] = None, + emit_signal: bool = True): + """Runs the SQL statement to modify the database + + :param connection: the connection to the database + :param sql_text: the SQL statement + :param data: the parameters to the SQL statement + :param emit_signal: whether to emit a signal upon completion""" + try: + cursor = connection.cursor() + if ManageDBSettingsHandler.settings.show_debug_messages: print(sql_text) + if data is not None: + if ManageDBSettingsHandler.settings.show_debug_messages: print(data) + cursor.executemany(sql_text, data) + else: + cursor.execute(sql_text) + connection.commit() # commits the changes to the database + if emit_signal: + managedb_signal_handler.emit_database_changed_signal() + except sqlite3.Error as error: + print(error) + + +def run_select_sql(connection: sqlite3.Connection, sql_text: str, data: Sequence[Any] = None) \ + -> Union[Sequence[Sequence[Any]], None]: + """Runs the SQL statement to get data from the database + + :param connection: the connection to the database + :param sql_text: the SQL statement + :param data: the parameters to the SQL statement + :returns: a list of rows that return from the statement""" + try: + cursor = connection.cursor() + if ManageDBSettingsHandler.settings.show_debug_messages: print(sql_text) + if data is not None: + if ManageDBSettingsHandler.settings.show_debug_messages: print(data) + cursor.execute(sql_text, data) + else: + cursor.execute(sql_text) + return cursor.fetchall() # gets the results + except sqlite3.Error as error: + print(error) + + +def setup_database(drop_tables: bool, emit_signal: bool = True): + """Sets up the database + + :param drop_tables: whether to drop the tables before creating them + :param emit_signal: whether to emit a signal upon completion""" + sql_texts = {} + sql_texts.update({report: create_table_sql_texts(report) for report in ALL_REPORTS}) + sql_texts.update({report_type + COST_TABLE_SUFFIX: create_cost_table_sql_texts(report_type) for report_type in + REPORT_TYPE_SWITCHER.keys()}) + sql_texts.update({report + VIEW_SUFFIX: create_view_sql_texts(report) for report in ALL_REPORTS}) + + connection = create_connection(DATABASE_LOCATION) + if connection is not None: + for key in sql_texts: + if drop_tables: + if ManageDBSettingsHandler.settings.show_debug_messages: print('DROP ' + key) + run_sql(connection, + 'DROP ' + ('VIEW' if key.endswith(VIEW_SUFFIX) else 'TABLE') + ' IF EXISTS ' + key + ';', + emit_signal=False) + if ManageDBSettingsHandler.settings.show_debug_messages: print('CREATE ' + key) + run_sql(connection, sql_texts[key], emit_signal=False) + connection.close() + if emit_signal: + managedb_signal_handler.emit_database_changed_signal() + else: + print('Error, no connection') + + +def first_time_setup(): + """Sets up the folders and database when the program is set up for the first time""" + if not os.path.exists(DATABASE_FOLDER): + if ManageDBSettingsHandler.settings.show_debug_messages: print('CREATE ' + DATABASE_FOLDER) + os.makedirs(DATABASE_FOLDER) + if not os.path.exists(DATABASE_LOCATION): + if ManageDBSettingsHandler.settings.show_debug_messages: print('CREATE ' + DATABASE_LOCATION) + setup_database(False) + if not os.path.exists(COSTS_SAVE_FOLDER): + if ManageDBSettingsHandler.settings.show_debug_messages: print('CREATE ' + COSTS_SAVE_FOLDER) + os.makedirs(COSTS_SAVE_FOLDER) + + +def backup_costs_data(report_type: str): + """Backs up the data in the costs table in a file in the costs directory + + :param report_type: the type of the report (master report name)""" + if not os.path.exists(COSTS_SAVE_FOLDER): + os.mkdir(COSTS_SAVE_FOLDER) + connection = create_connection(DATABASE_LOCATION) + if connection is not None: + headers = [] + for field in get_cost_fields_list(report_type): + headers.append(field[NAME_KEY]) + sql_text = get_sql_select_statement(('*',), (report_type + COST_TABLE_SUFFIX,), + order_by_fields=COSTS_KEY_FIELDS + ( + NAME_FIELD_SWITCHER[report_type] + ' COLLATE NOCASE ASC',), + is_multiline=False) + results = run_select_sql(connection, sql_text) + connection.close() + results.insert(0, headers) + if ManageDBSettingsHandler.settings.show_debug_messages: + print('CREATE ' + COSTS_SAVE_FOLDER + report_type + COST_TABLE_SUFFIX) + file_name = COSTS_SAVE_FOLDER + report_type + COST_TABLE_SUFFIX + '.tsv' + save_data_as_tsv(file_name, results) + else: + print('Error, no connection') + + +class UpdateDatabaseProgressDialogController: + """Controls the Update Database Progress Dialog""" + + def __init__(self, parent_widget: QObject = None): + self.parent_widget = parent_widget + self.update_database_progress_dialog = None + + self.update_database_thread = None + + self.database_worker = None + + self.update_status_label = None + self.update_progress_bar = None + self.update_task_finished_widget = None + self.update_task_finished_scrollarea = None + + self.is_updating_database = False + + def update_database(self, files: Sequence[Dict[str, Any]], recreate_tables: bool): + """Updates the database with the given files + + :param files: a list of files to insert into the database + :param recreate_tables: whether or not to drop the tables and recreated the tables before inserting""" + self.update_database_progress_dialog = QDialog(self.parent_widget) + + dialog_ui = UpdateDatabaseProgressDialog.Ui_update_database_dialog() + dialog_ui.setupUi(self.update_database_progress_dialog) + + self.update_status_label = dialog_ui.status_label + self.update_progress_bar = dialog_ui.progressbar + self.update_task_finished_scrollarea = dialog_ui.scrollarea + + self.update_task_finished_widget = QWidget() + self.update_task_finished_widget.setLayout(QVBoxLayout()) + self.update_task_finished_scrollarea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) + self.update_task_finished_scrollarea.setWidget(self.update_task_finished_widget) + + self.update_progress_bar.setMaximum(len(files)) + + self.update_database_progress_dialog.show() + + self.update_database_thread = QThread() + + self.database_worker = UpdateDatabaseWorker(files, recreate_tables) + + self.database_worker.status_changed_signal.connect(lambda status: self.on_status_changed(status)) + self.database_worker.progress_changed_signal.connect(lambda progress: self.on_progress_changed(progress)) + self.database_worker.task_finished_signal.connect(lambda task: self.on_task_finished(task)) + self.database_worker.worker_finished_signal.connect(lambda code: self.on_thread_finish(code)) + + self.database_worker.moveToThread(self.update_database_thread) + + self.update_database_thread.started.connect(self.database_worker.work) + + self.update_database_thread.start() + + def on_status_changed(self, status: str): + """Invoked when the status of the worker changes + + :param status: the new status of the worker""" + self.update_status_label.setText(status) + + def on_progress_changed(self, progress: int): + """Invoked when the progress of the worker changes + + :param progress: the new progress completed""" + self.update_progress_bar.setValue(progress) + + def on_task_finished(self, task: str): + """Invoked when the worker finishes a task + + :param task: the name of the task that was completed""" + label = QLabel(task) + self.update_task_finished_widget.layout().addWidget(label) + + def on_thread_finish(self, code: int): + """Invoked when the worker's thread finishes + + :param code: the exit code of the thread""" + if ManageDBSettingsHandler.settings.show_debug_messages: + print('update database thread exited with code ' + str(code)) + # exit thread + self.update_database_thread.quit() + self.update_database_thread.wait() + + +class UpdateDatabaseWorker(QObject): + """The worker that updates the database + + :param files: a list of files to insert into the database + :param recreate_tables: whether or not to drop the tables and recreated the tables before inserting""" + worker_finished_signal = pyqtSignal(int) + status_changed_signal = pyqtSignal(str) + progress_changed_signal = pyqtSignal(int) + task_finished_signal = pyqtSignal(str) + + def __init__(self, files: Sequence[Dict[str, Any]], recreate_tables: bool): + super().__init__() + self.recreate_tables = recreate_tables + self.files = files + + def work(self): + """Performs the work of the worker""" + current = 0 + if self.recreate_tables: + self.status_changed_signal.emit('Recreating tables...') + setup_database(True, emit_signal=False) + current += 1 + self.progress_changed_signal.emit(current) + self.task_finished_signal.emit('Recreated tables') + else: + self.progress_changed_signal.emit(len(self.files)) + self.status_changed_signal.emit('Filling tables...') + for file in self.files: + filename = os.path.basename(file['file']) + if not filename[:-4].endswith(COST_TABLE_SUFFIX): + insert_single_file(file['file'], file['vendor'], file['year'], emit_signal=False) + else: + insert_single_cost_file(file['report'], file['file'], emit_signal=False) + self.task_finished_signal.emit(filename) + current += 1 + self.progress_changed_signal.emit(current) + managedb_signal_handler.emit_database_changed_signal() + self.status_changed_signal.emit('Done') + self.worker_finished_signal.emit(0) diff --git a/ManageVendors.py b/ManageVendors.py new file mode 100644 index 0000000..15e0cd0 --- /dev/null +++ b/ManageVendors.py @@ -0,0 +1,648 @@ +"""This module handles all operations involving managing vendors.""" + +import csv +import os +import json +import validators +from PyQt5.QtWidgets import QDialog, QLabel, QDialogButtonBox, QWidget +from PyQt5.QtGui import QStandardItemModel, QStandardItem +from PyQt5.QtCore import Qt, QObject, QModelIndex, pyqtSignal +from ui import ManageVendorsTab, AddVendorDialog, RemoveVendorDialog +import ManageDB +import GeneralUtils +from GeneralUtils import JsonModel +from Constants import * +from Settings import SettingsModel + + +class Vendor(JsonModel): + """This holds a vendor's information + + :param name: The vendor's unique name (Mandatory) + :param base_url: The base URL for making sushi report calls (must end with '/reports', mandatory) + :param customer_id: The customer id used in sushi report calls + :param requestor_id: The requestor id id used in sushi report calls + :param api_key: The api key id used in sushi report calls + :param platform: The platform id used in sushi report calls + :param is_non_sushi: This indicates if this vendor is sushi compatible + :param description: A description of this vendor + :param companies: More information about the vendor + """ + def __init__(self, name: str, base_url: str, customer_id: str, requestor_id: str, api_key: str, platform: str, + is_non_sushi: bool, description: str, companies: str): + self.name = name + self.base_url = base_url + self.customer_id = customer_id + self.requestor_id = requestor_id + self.api_key = api_key + self.platform = platform + self.is_non_sushi = is_non_sushi + self.description = description + self.companies = companies + + @classmethod + def from_json(cls, json_dict: dict): + """This returns a vendor object using the parameters in a json dict + + :param json_dict: A dict containing a vendor's details + :return: Vendor + """ + name = json_dict["name"] if "name" in json_dict else "" + customer_id = json_dict["customer_id"] if "customer_id" in json_dict else "" + base_url = json_dict["base_url"] if "base_url" in json_dict else "" + requestor_id = json_dict["requestor_id"] if "requestor_id" in json_dict else "" + api_key = json_dict["api_key"] if "api_key" in json_dict else "" + platform = json_dict["platform"] if "platform" in json_dict else "" + is_non_sushi = json_dict["is_non_sushi"] if "is_non_sushi" in json_dict else False + description = json_dict["description"] if "description" in json_dict else "" + companies = json_dict["companies"] if "companies" in json_dict else "" + + return cls(name, base_url, customer_id, requestor_id, api_key, platform, is_non_sushi, description, companies) + + +class ManageVendorsController(QObject): + """Controls the Manage Vendors tab + + :param manage_vendors_widget: The manage vendors widget. + :param manage_vendors_ui: The UI for the manage_vendors_widget. + """ + vendors_changed_signal = pyqtSignal(list) + + def __init__(self, manage_vendors_widget: QWidget, manage_vendors_ui: ManageVendorsTab.Ui_manage_vendors_tab, + settings: SettingsModel): + super().__init__() + self.manage_vendors_widget = manage_vendors_widget + self.selected_index = -1 + + self.edit_vendor_details_frame = manage_vendors_ui.edit_vendor_details_frame + self.edit_vendor_options_frame = manage_vendors_ui.edit_vendor_options_frame + + self.name_line_edit = manage_vendors_ui.nameEdit + self.customer_id_line_edit = manage_vendors_ui.customerIdEdit + self.base_url_line_edit = manage_vendors_ui.baseUrlEdit + self.requestor_id_line_edit = manage_vendors_ui.requestorIdEdit + self.api_key_line_edit = manage_vendors_ui.apiKeyEdit + self.platform_line_edit = manage_vendors_ui.platformEdit + self.non_Sushi_check_box = manage_vendors_ui.non_Sushi_check_box + self.description_text_edit = manage_vendors_ui.descriptionEdit + self.companies_text_edit = manage_vendors_ui.companiesEdit + + manage_vendors_ui.non_sushi_help_button.clicked.connect( + lambda: GeneralUtils.show_message("Vendors that don't provide SUSHI service can be added to the list for " + "use with Import Reports")) + + self.name_validation_label = manage_vendors_ui.name_validation_label + self.name_validation_label.hide() + self.url_validation_label = manage_vendors_ui.url_validation_label + self.url_validation_label.hide() + + self.save_vendor_changes_button = manage_vendors_ui.saveVendorChangesButton + self.undo_vendor_changes_button = manage_vendors_ui.undoVendorChangesButton + self.remove_vendor_button = manage_vendors_ui.removeVendorButton + self.add_vendor_button = manage_vendors_ui.addVendorButton + self.export_vendors_button = manage_vendors_ui.exportVendorsButton + self.import_vendors_button = manage_vendors_ui.importVendorsButton + + self.save_vendor_changes_button.clicked.connect(self.modify_vendor) + self.undo_vendor_changes_button.clicked.connect(self.populate_edit_vendor_view) + self.remove_vendor_button.clicked.connect(self.on_remove_vendor_clicked) + self.add_vendor_button.clicked.connect(self.on_add_vendor_clicked) + self.export_vendors_button.clicked.connect(self.on_export_vendors_clicked) + self.import_vendors_button.clicked.connect(self.on_import_vendors_clicked) + + self.vendor_list_view = manage_vendors_ui.vendorsListView + self.vendor_list_model = QStandardItemModel(self.vendor_list_view) + self.vendor_list_view.setModel(self.vendor_list_model) + self.vendor_list_view.clicked.connect(self.on_vendor_selected) + + self.settings = settings + + self.vendors = [] + self.vendor_names = set() # Hash set for faster operations + vendors_json_string = GeneralUtils.read_json_file(VENDORS_FILE_PATH) + vendor_dicts = json.loads(vendors_json_string) + for json_dict in vendor_dicts: + vendor = Vendor.from_json(json_dict) + self.vendors.append(vendor) + self.vendor_names.add(vendor.name.lower()) + + self.update_vendors_ui() + + def on_vendor_selected(self, model_index: QModelIndex): + """Handles the signal emitted when a vendor is selected + + :param model_index: An object containing the location of the vendor on the vendor list + """ + self.selected_index = model_index.row() + self.populate_edit_vendor_view() + + def on_name_text_changed(self, new_name: str, original_name: str, validation_label: QLabel, validate: bool = True): + """Handles the signal emitted when a vendor's name is changed + + :param new_name: The new name entered in the text field + :param original_name: The vendor's original name + :param validation_label: The label to show validation messages + :param validate: This indicates whether the new_name should be validated + """ + if not validate: + validation_label.hide() + return + + is_valid, message = self.validate_new_name(new_name, original_name) + if is_valid: + validation_label.hide() + else: + validation_label.show() + validation_label.setText(message) + + def on_url_text_changed(self, url: str, validation_label: QLabel, validate: bool = True): + """Handles the signal emitted when a vendor's URL is changed + + :param url: The URL entered in the text field + :param validation_label: The label to show validation messages + :param validate: This indicates whether the url should be validated + """ + if not validate: + validation_label.hide() + return + + is_valid, message = self.validate_url(url) + if is_valid: + validation_label.hide() + else: + validation_label.show() + validation_label.setText(message) + + def validate_new_name(self, new_name: str, original_name: str = "") -> (bool, str): + """Validates a new vendor name + + :param new_name: The new name to be validated + :param original_name: The original name + :returns: (is_successful, message) A Tuple with the completion status and a message + """ + if not new_name: + return False, "Vendor name can't be empty" + elif new_name.lower() in self.vendor_names: + if original_name and original_name.lower() == new_name.lower(): + return True, "" + else: + return False, "Duplicate vendor name" + else: + return True, "" + + def validate_url(self, url: str) -> (bool, str): + """Validates a new url + + :param url: The URL to be validated + :returns: (is_successful, message) A Tuple with the completion status and a message + """ + if not validators.url(url): + return False, "Invalid Url" + elif not url.endswith("/reports"): + return False, "URL must end with '/reports'" + else: + return True, "" + + def update_vendors_ui(self): + """Updates the UI to show all vendors""" + self.vendor_list_model.clear() + for vendor in self.vendors: + item = QStandardItem(vendor.name) + item.setEditable(False) + self.vendor_list_model.appendRow(item) + + self.populate_edit_vendor_view() + + def update_vendor_names(self): + """Updates the local set of vendor names used for validation""" + self.vendor_names.clear() + for vendor in self.vendors: + self.vendor_names.add(vendor.name.lower()) + + def add_vendor(self, new_vendor: Vendor) -> (bool, str): + """Adds a new vendor to the system if the vendor is valid + + :param new_vendor: The new vendor to be added + :returns: (is_successful, message) A Tuple with the completion status and a message + """ + # Check if vendor is valid + is_valid, message = self.validate_new_name(new_vendor.name) + if not is_valid: + return is_valid, message + + if not new_vendor.is_non_sushi: + is_valid, message = self.validate_url(new_vendor.base_url) + if not is_valid: + return is_valid, message + + self.vendors.append(new_vendor) + self.vendor_names.add(new_vendor.name.lower()) + + return True, "" + + def modify_vendor(self): + """Updates a vendor's information in the system if the vendor is valid""" + if self.selected_index < 0: + if self.settings.show_debug_messages: print("No vendor selected") + return + + selected_vendor = self.vendors[self.selected_index] + + # Check if entries are valid + new_name = self.name_line_edit.text() + original_name = selected_vendor.name + is_valid, message = self.validate_new_name(new_name, original_name) + if not is_valid: + GeneralUtils.show_message(message) + return + + if not self.non_Sushi_check_box.isChecked(): + url = self.base_url_line_edit.text() + is_valid, message = self.validate_url(url) + if not is_valid: + GeneralUtils.show_message(message) + return + + # Apply Changes + if original_name != new_name: + self.update_name_of_file_and_folder(original_name, new_name) + ManageDB.update_vendor_in_all_tables(original_name, new_name) + for report_type in REPORT_TYPE_SWITCHER.keys(): + ManageDB.backup_costs_data(report_type) + + selected_vendor.name = self.name_line_edit.text() + selected_vendor.base_url = self.base_url_line_edit.text() + selected_vendor.customer_id = self.customer_id_line_edit.text() + selected_vendor.requestor_id = self.requestor_id_line_edit.text() + selected_vendor.api_key = self.api_key_line_edit.text() + selected_vendor.platform = self.platform_line_edit.text() + selected_vendor.is_non_sushi = self.non_Sushi_check_box.checkState() == Qt.Checked + selected_vendor.description = self.description_text_edit.toPlainText() + selected_vendor.companies = self.companies_text_edit.toPlainText() + + self.update_vendors_ui() + self.update_vendor_names() + self.vendors_changed_signal.emit(self.vendors) + self.save_all_vendors_to_disk() + + GeneralUtils.show_message("Changes Saved!") + + def update_name_of_file_and_folder(self,original_name, new_name): + DO_NOT_MODIFY_json_path = os.getcwd() + os.path.sep + "all_data" + os.path.sep + ".DO_NOT_MODIFY" + os.path.sep + "_json" + DO_NOT_MODIFY_year_path = os.getcwd() + os.path.sep + "all_data" + os.path.sep + ".DO_NOT_MODIFY" + default_year_path = os.getcwd() + os.path.sep + "all_data" + os.path.sep + "yearly_files" + default_other_path = os.getcwd() + os.path.sep + "all_data" + os.path.sep + "other_files" + + custom_year_path = self.settings.yearly_directory + custom_other_path = self.settings.other_directory + + if os.path.exists(DO_NOT_MODIFY_json_path): + folderList = os.listdir(DO_NOT_MODIFY_json_path) + for folder in folderList: + if folder[0] == "2" and folder[1] == "0": + year_path = DO_NOT_MODIFY_json_path + os.path.sep + folder + + original_folder_path = year_path + os.path.sep + original_name + new_folder_path = year_path + os.path.sep + new_name + + if os.path.exists(original_folder_path): + filesList = os.listdir(original_folder_path) + + for theFile in filesList: + old_file_path = original_folder_path + os.path.sep + theFile + new_file_path = original_folder_path + os.path.sep + theFile.replace(original_name,new_name) + os.rename(old_file_path,new_file_path) + + os.rename(original_folder_path,new_folder_path) + + if (os.path.exists(DO_NOT_MODIFY_year_path)): + folderList = os.listdir(DO_NOT_MODIFY_json_path) + for folder in folderList: + if folder[0] == "2" and folder[1] == "0": + year_path = DO_NOT_MODIFY_year_path + os.path.sep + folder + + original_folder_path = year_path + os.path.sep + original_name + new_folder_path = year_path + os.path.sep + new_name + + if os.path.exists(original_folder_path): + filesList = os.listdir(original_folder_path) + + for theFile in filesList: + old_file_path = original_folder_path + os.path.sep + theFile + new_file_path = original_folder_path + os.path.sep + theFile.replace(original_name, new_name) + + os.rename(old_file_path, new_file_path) + + os.rename(original_folder_path, new_folder_path) + + if (os.path.exists(default_year_path)): + folderList = os.listdir(DO_NOT_MODIFY_json_path) + for folder in folderList: + if folder[0] == "2" and folder[1] == "0": + year_path = default_year_path + os.path.sep + folder + + original_folder_path = year_path + os.path.sep + original_name + new_folder_path = year_path + os.path.sep + new_name + + if os.path.exists(original_folder_path): + filesList = os.listdir(original_folder_path) + + for theFile in filesList: + old_file_path = original_folder_path + os.path.sep + theFile + new_file_path = original_folder_path + os.path.sep + theFile.replace(original_name, new_name) + + os.rename(old_file_path, new_file_path) + + os.rename(original_folder_path, new_folder_path) + + if (os.path.exists(default_other_path)): + original_folder_path = default_other_path + os.path.sep + original_name + new_folder_path = default_other_path + os.path.sep + new_name + + if os.path.exists(original_folder_path): + filesList = os.listdir(original_folder_path) + + for theFile in filesList: + old_file_path = original_folder_path + os.path.sep + theFile + new_file_path = original_folder_path + os.path.sep + theFile.replace(original_name, + new_name) + os.rename(old_file_path, new_file_path) + + os.rename(original_folder_path, new_folder_path) + + if (os.path.exists(custom_year_path)): + folderList = os.listdir(custom_year_path) + for folder in folderList: + if folder[0] == "2" and folder[1] == "0": + year_path = custom_year_path + os.path.sep + folder + + original_folder_path = year_path + os.path.sep + original_name + new_folder_path = year_path + os.path.sep + new_name + + if os.path.exists(original_folder_path): + filesList = os.listdir(original_folder_path) + + for theFile in filesList: + old_file_path = original_folder_path + os.path.sep + theFile + new_file_path = original_folder_path + os.path.sep + theFile.replace(original_name, + new_name) + os.rename(old_file_path, new_file_path) + + os.rename(original_folder_path, new_folder_path) + + if (os.path.exists(custom_other_path)): + original_folder_path = custom_other_path + os.path.sep + original_name + new_folder_path = custom_other_path + os.path.sep + new_name + + if os.path.exists(original_folder_path): + filesList = os.listdir(original_folder_path) + + for theFile in filesList: + old_file_path = original_folder_path + os.path.sep + theFile + new_file_path = original_folder_path + os.path.sep + theFile.replace(original_name, + new_name) + os.rename(old_file_path, new_file_path) + + os.rename(original_folder_path, new_folder_path) + + def on_add_vendor_clicked(self): + """Handles the signal emitted when the add vendor button is clicked + + A dialog is show to allow the user to enter a new vendor's information. If the information entered is valid, + the vendor is added to the system + """ + vendor_dialog = QDialog() + vendor_dialog_ui = AddVendorDialog.Ui_addVendorDialog() + vendor_dialog_ui.setupUi(vendor_dialog) + + name_edit = vendor_dialog_ui.nameEdit + base_url_edit = vendor_dialog_ui.baseUrlEdit + customer_id_edit = vendor_dialog_ui.customerIdEdit + requestor_id_edit = vendor_dialog_ui.requestorIdEdit + api_key_edit = vendor_dialog_ui.apiKeyEdit + platform_edit = vendor_dialog_ui.platformEdit + non_sushi_check_box = vendor_dialog_ui.non_Sushi_check_box + description_edit = vendor_dialog_ui.descriptionEdit + companies_edit = vendor_dialog_ui.companiesEdit + + vendor_dialog_ui.non_sushi_help_button.clicked.connect( + lambda: GeneralUtils.show_message("Vendors that don't provide SUSHI service can be added to the list for " + "use with Import Reports")) + + name_validation_label = vendor_dialog_ui.name_validation_label + name_validation_label.hide() + url_validation_label = vendor_dialog_ui.url_validation_label + url_validation_label.hide() + + name_edit.textChanged.connect( + lambda new_name: self.on_name_text_changed(new_name, "", name_validation_label)) + base_url_edit.textChanged.connect( + lambda url: self.on_url_text_changed(url, url_validation_label)) + + def attempt_add_vendor(): + vendor = Vendor(name_edit.text(), base_url_edit.text(), customer_id_edit.text(), requestor_id_edit.text(), + api_key_edit.text(), platform_edit.text(), non_sushi_check_box.checkState() == Qt.Checked, + description_edit.toPlainText(), companies_edit.toPlainText()) + + is_valid, message = self.add_vendor(vendor) + if is_valid: + self.sort_vendors() + self.selected_index = -1 + self.update_vendors_ui() + self.populate_edit_vendor_view() + self.vendors_changed_signal.emit(self.vendors) + self.save_all_vendors_to_disk() + vendor_dialog.close() + else: + GeneralUtils.show_message(message) + + button_box = vendor_dialog_ui.buttonBox + ok_button = button_box.button(QDialogButtonBox.Ok) + ok_button.clicked.connect(attempt_add_vendor) + cancel_button = button_box.button(QDialogButtonBox.Cancel) + cancel_button.clicked.connect(lambda: vendor_dialog.close()) + + vendor_dialog.exec_() + + def on_import_vendors_clicked(self): + """Handles the signal emitted when the import vendors button is clicked. + + A file select dialog is shown to allow the user to select the vendors TSV file to import. The selected file is + then imported. + """ + file_path = GeneralUtils.choose_file(TSV_FILTER) + if file_path: + self.import_vendors_tsv(file_path) + + def on_export_vendors_clicked(self): + """Handles the signal emitted when the export vendors button is clicked. + + A folder select dialog is shown to allow the user to select the target directory to export the vendors file to. + A vendors TSV file containing all the vendors in the system is then exported + """ + dir_path = GeneralUtils.choose_directory() + if dir_path: + self.export_vendors_tsv(dir_path) + + def populate_edit_vendor_view(self): + """Populates the edit vendor view with the selected vendors's information""" + if self.selected_index >= 0: + selected_vendor = self.vendors[self.selected_index] + + self.name_line_edit.textChanged.connect( + lambda new_name: self.on_name_text_changed(new_name, selected_vendor.name, self.name_validation_label)) + self.name_line_edit.setText(selected_vendor.name) + + self.base_url_line_edit.textChanged.connect( + lambda url: self.on_url_text_changed(url, self.url_validation_label)) + self.base_url_line_edit.setText(selected_vendor.base_url) + + self.customer_id_line_edit.setText(selected_vendor.customer_id) + self.requestor_id_line_edit.setText(selected_vendor.requestor_id) + self.api_key_line_edit.setText(selected_vendor.api_key) + self.platform_line_edit.setText(selected_vendor.platform) + self.non_Sushi_check_box.setChecked(selected_vendor.is_non_sushi) + self.description_text_edit.setPlainText(selected_vendor.description) + self.companies_text_edit.setPlainText(selected_vendor.companies) + + self.set_edit_vendor_view_state(True) + else: + self.name_line_edit.textChanged.connect( + lambda new_name: self.on_name_text_changed(new_name, "", self.name_validation_label, False)) + self.name_line_edit.setText("") + self.name_line_edit.textChanged.emit("") # Hide validation_label if showing + + self.base_url_line_edit.textChanged.connect( + lambda url: self.on_url_text_changed(url, self.url_validation_label, False)) + self.base_url_line_edit.setText("") + self.base_url_line_edit.textChanged.emit("") + + self.customer_id_line_edit.setText("") + self.base_url_line_edit.setText("") + self.requestor_id_line_edit.setText("") + self.api_key_line_edit.setText("") + self.platform_line_edit.setText("") + self.non_Sushi_check_box.setChecked(False) + self.description_text_edit.setPlainText("") + self.companies_text_edit.setPlainText("") + + self.set_edit_vendor_view_state(False) + + def set_edit_vendor_view_state(self, is_enabled): + """Enables or disables the elements in the edit vendor view + + :param is_enabled: This indicates whether the edit vendor view should be enabled + """ + if is_enabled: + self.edit_vendor_details_frame.setEnabled(True) + self.edit_vendor_options_frame.setEnabled(True) + else: + self.edit_vendor_details_frame.setEnabled(False) + self.edit_vendor_options_frame.setEnabled(False) + + def on_remove_vendor_clicked(self): + """Handles the signal emitted when the remove vendor button is clicked. + + A confirmation dialog is shown to confirm the removal of the vendor. The vendor is removed from the system if + confirmed + """ + dialog_remove = QDialog() + dialog_remove_ui = RemoveVendorDialog.Ui_dialog_remove() + dialog_remove_ui.setupUi(dialog_remove) + + def remove_vendor(): + if self.selected_index >= 0: + self.vendors.pop(self.selected_index) + self.selected_index = -1 + + self.update_vendors_ui() + self.update_vendor_names() + self.populate_edit_vendor_view() + self.vendors_changed_signal.emit(self.vendors) + self.save_all_vendors_to_disk() + + button_box = dialog_remove_ui.buttonBox + button_box.accepted.connect(remove_vendor) + dialog_remove.exec_() + + def save_all_vendors_to_disk(self): + """Saves all the vendors in the system to disk""" + json_string = json.dumps(self.vendors, default=lambda o: o.__dict__, indent=4) + GeneralUtils.save_json_file(VENDORS_FILE_DIR, VENDORS_FILE_NAME, json_string) + + def sort_vendors(self): + """Sorts the vendors alphabetically based their names""" + self.vendors = sorted(self.vendors, key=lambda vendor: vendor.name.lower()) + + def import_vendors_tsv(self, file_path): + """Imports the vendors in a TSV file path to the system + + :param file_path: The file path of the vendors TSV file + """ + try: + tsv_file = open(file_path, 'r', encoding="utf-8", newline='') + reader = csv.DictReader(tsv_file, delimiter='\t') + for row in reader: + if 'is_non_sushi' in row: + is_non_sushi = row['is_non_sushi'].lower() == "true" + else: + is_non_sushi = False + vendor = Vendor(row['name'] if 'name' in row else "", + row['base_url'] if 'base_url' in row else "", + row['customer_id'] if 'customer_id' in row else "", + row['requestor_id'] if 'requestor_id' in row else "", + row['api_key'] if 'api_key' in row else "", + row['platform'] if 'platform' in row else "", + is_non_sushi, + row['description'] if 'description' in row else "", + row['companies'] if 'companies' in row else "") + + is_valid, message = self.add_vendor(vendor) + if not is_valid: + if self.settings.show_debug_messages: print(message) + + tsv_file.close() + + self.sort_vendors() + self.selected_index = -1 + self.update_vendors_ui() + self.update_vendor_names() + self.populate_edit_vendor_view() + self.vendors_changed_signal.emit(self.vendors) + self.save_all_vendors_to_disk() + + GeneralUtils.show_message(f"Import successful!") + except Exception as e: + if self.settings.show_debug_messages: print(f"File import failed: {e}") + GeneralUtils.show_message(f"File import failed: {e}") + + def export_vendors_tsv(self, dir_path): + """Exports all vendor information as a TSV file to a directory + + :param dir_path: The directory path to export the vendors TSV file to + """ + file_path = f"{dir_path}{EXPORT_VENDORS_FILE_NAME}" + column_names = ["name", + "base_url", + "customer_id", + "requestor_id", + "api_key", + "platform", + "is_non_sushi", + "description", + "companies"] + try: + tsv_file = open(file_path, 'w', encoding="utf-8", newline='') + tsv_dict_writer = csv.DictWriter(tsv_file, column_names, delimiter='\t') + tsv_dict_writer.writeheader() + + for vendor in self.vendors: + tsv_dict_writer.writerow(vendor.__dict__) + + tsv_file.close() + GeneralUtils.show_message(f"Exported to {file_path}") + + except Exception as e: + if self.settings.show_debug_messages: print(f"File export failed: {e}") + GeneralUtils.show_message(f"File export failed: {e}") + diff --git a/PyToExecutable.py b/PyToExecutable.py new file mode 100644 index 0000000..de2fca0 --- /dev/null +++ b/PyToExecutable.py @@ -0,0 +1,11 @@ +import PyInstaller.__main__ +from os import path + +if __name__ == "__main__": + PyInstaller.__main__.run([ + '--name=Counter 5 Report Tool', + '--onefile', + '--console', + '--icon=%s' % path.join('main_icon.ico'), + path.join('MainDriver.py'), + ]) diff --git a/README.md b/README.md new file mode 100644 index 0000000..c5ce629 --- /dev/null +++ b/README.md @@ -0,0 +1,75 @@ +# COUNTER 5 Report Tool + +This project uses the SUSHI API to request usage reports from library vendors. The JSON data received is used to generate TSV reports that follow the COUNTER 5 standards. +The project is written with Python 3.7. The PyQt GUI framework is used to create a user friendly (hopefully) GUI. + +## Features +- Manage library vendor credentials +- Fetch reports that strictly adhere to the COUNTER 5 standards +- Fetch customized reports using the available parameters in the SUSHI API +- Import COUNTER 5 reports into the local directories and database +- Specify the costs of subscribed items +- Search the sqlite database of fetched and imported reports +- Generate charts using the data in the database + +## Download Project +https://github.com/CS-4820-Library-Project/Libly/releases + +## Developer Documentation +https://counter-5-report-tool.readthedocs.io/en/latest/ + +## How to use pyinstaller for executables +https://github.com/CS-4820-Library-Project/Libly/blob/master/docs/pyinstaller-how-to.md + +## Setup Instructions (Windows) +- Install Python 3.8.2 [https://www.python.org/ftp/python/3.8.2/python-3.8.2-amd64.exe] +### Add Python to environment variables [https://datatofish.com/add-python-to-windows-path/] +- Open control panel +- System and Security -> System -> Advanced System Settings -> Advanced -> Environment Variables +- System Variables: Click on Path, Click Edit... +- Click Browse +- Browse to where python is downloaded, by default: C:\Users\USER_NAME\AppData\Local\Programs\Python\Python38 + +- Add another variable in the same way that we just did. +- Except this time set the filepath to C:\Users\apjm4\AppData\Local\Programs\Python\Python38\Scripts +Python should now be accessible in Windows Command Prompt. +Open command prompt and type Python --version. This should return the version of python that is installed. +If not the path may be wrong or python was not installed correctly. + +type pip -v into command prompt, this should return the version of PIP that is installed with Python. + +### Download the project from Github +- Close and re-open command prompt +- type cd +- Open the location you downloaded the project to and drag the folder into the command prompt window +- Your command prompt window should now show "C:\Users\NAME>cd C:\Users\NAME\DOWNLOAD_LOCATION +- Hit ENTER +- type: pip install -r requirements.txt +- This installs all the neccessary packages to run the project. + +### Run the project +- Type: python maindriver.py +- A User-Interface window should open with the project working +- To run the project from now on, you only need to double click or right click and open MainDriver.py and the project should open + + + +## Developer Setup (using Anaconda and Pycharm) +- Download and install Anaconda: https://www.anaconda.com/distribution/#download-section +- Download and install PyCharm: https://www.jetbrains.com/pycharm/download/ + +### Using Anaconda +- Launch Anaconda Navigator (Anaconda GUI) +- Go to Environments on the left pane +- Search for and ensure that pyqt and requests packages are installed + +### Using PyCharm +- Download and open the project using PyCharm +- Go to File->Settings +- On the left pane, select Project->Project Interpreter +- Click the cog wheel on the right of the project interpreter drop down, click add +- Choose Existing environment and set the location to anaconda_install_location/python.exe, OK, OK +- Allow the IDE to complete set up then launch the program from MainDriver.py. There should be a play icon next to the line "if __name__ == "__main__":" +- We Good To Go! + + diff --git a/Resources.qrc b/Resources.qrc new file mode 100644 index 0000000..1a69947 --- /dev/null +++ b/Resources.qrc @@ -0,0 +1,21 @@ + + + ui/resources/main_icon.png + ui/resources/help_icon.png + ui/resources/undo_icon.png + ui/resources/save_icon.png + ui/resources/add_vendor_icon.png + ui/resources/export_vendors_icon.png + ui/resources/import_vendors_icon.png + ui/resources/remove_vendor_icon.png + ui/resources/folder_icon.png + ui/resources/tab_icons/costs_icon.png + ui/resources/tab_icons/fetch_reports_icon.png + ui/resources/tab_icons/fetch_special_reports_icon.png + ui/resources/tab_icons/import_report_icon.png + ui/resources/tab_icons/manage_vendors_icon.png + ui/resources/tab_icons/search_icon.png + ui/resources/tab_icons/settings_icon.png + ui/resources/tab_icons/visual_icon.png + + diff --git a/Resources_rc.py b/Resources_rc.py new file mode 100644 index 0000000..d173893 --- /dev/null +++ b/Resources_rc.py @@ -0,0 +1,5902 @@ +# -*- coding: utf-8 -*- + +# Resource object code +# +# Created by: The Resource Compiler for PyQt5 (Qt v5.12.5) +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore + +qt_resource_data = b"\ +\x00\x00\x2a\x17\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x02\x00\x00\x00\x02\x00\x08\x04\x00\x00\x00\x5e\x71\x1c\x71\ +\x00\x00\x02\xe7\x7a\x54\x58\x74\x52\x61\x77\x20\x70\x72\x6f\x66\ +\x69\x6c\x65\x20\x74\x79\x70\x65\x20\x65\x78\x69\x66\x00\x00\x78\ +\xda\xed\x97\x5b\x92\x1b\x21\x0c\x45\xff\x59\x45\x96\x80\x24\x84\ +\xc4\x72\x68\x1e\x55\xd9\x41\x96\x9f\x0b\xdd\xf6\x78\x1e\x49\x66\ +\x2a\xf9\x4a\xb9\xa9\x86\xb6\x00\x49\x7d\x0f\x60\x3b\x8c\x1f\xdf\ +\x67\xf8\x86\x8b\x4a\xe6\x90\xd4\x3c\x97\x9c\x23\xae\x54\x52\xe1\ +\x8a\x07\x8f\xe7\x55\x76\x4d\x31\xed\xfa\x76\xf1\x65\x7d\x65\x0f\ +\xf7\x0e\x86\x49\xd0\xca\xd9\x61\xf5\x6c\xa9\xc2\xae\x2f\x13\x6e\ +\x31\xe8\x78\x6d\x0f\x7e\xf5\xb0\x5f\x8e\x6e\x91\x2f\x87\xb2\x22\ +\xaf\x50\xfd\x31\x49\xd8\xf9\xb4\x53\xba\x1c\x95\x71\x3e\xe4\xe2\ +\xf6\x98\xea\x71\xe6\x19\xdb\x35\x70\xa7\x72\xdd\xc7\xe5\x74\x67\ +\x14\xcf\xcf\xe1\xd1\x90\x0c\x2a\x75\x45\x20\x61\x1e\x42\x12\x77\ +\xed\x67\x06\x72\xde\x15\xb7\xa0\x66\x31\x8c\x43\xc6\x78\x4e\x42\ +\x61\x9b\xf2\x95\x09\x04\x79\xf5\x7a\x2f\x02\x3f\x0a\xf4\x56\x7c\ +\x3a\xc5\xa6\x8f\x3b\xde\x8a\xcf\xf5\xb2\xcb\x1b\x2d\xf3\x8d\x5a\ +\xfe\xb8\x83\xf4\x63\xf1\xb7\xc4\x0f\x81\xe5\x9e\x11\xbf\xee\x30\ +\xb9\xb9\x7a\x2f\xf2\x9c\xdd\xe7\x1c\xe7\xdb\xd5\x94\xa1\x68\xbe\ +\x56\xd4\x16\x9b\x6e\x6e\x30\xf0\x80\xe4\xb2\xa7\x65\x14\xc3\xad\ +\x78\xb6\x5d\x0a\x8a\xc7\x1a\x1b\x90\xf7\xd8\xe2\x81\xd2\xa8\x10\ +\x83\xca\x0c\x94\xa8\x53\xa5\x49\x63\xb7\x8d\x1a\x52\x4c\x3c\x18\ +\x4c\x98\xb9\xb1\x6c\x9b\x83\x51\xe1\xb6\x29\xa5\x55\x68\xb2\x49\ +\x91\x2e\x0e\x58\x8d\x47\x00\xca\x24\x7c\xcf\x85\x76\xdc\xb2\xe3\ +\x35\x72\x44\xee\x84\xa1\x4c\x70\x46\x98\xf2\xcb\x12\x7e\xd7\xf9\ +\x95\x12\xe6\x6c\x4b\x22\x8a\x7e\xd7\x0a\x79\xf1\x5a\xd7\x48\x63\ +\x91\x5b\x35\x46\x01\x08\xcd\x8b\x9b\x6e\x81\x6f\xe5\xbe\x69\xe3\ +\x03\x58\x01\x41\xdd\x32\x3b\x5e\xb0\xc6\xe3\x74\x71\x28\xbd\xac\ +\x2d\xd9\x9c\x05\xe3\x14\xed\xb9\x85\x28\x58\xbf\x1c\x40\x22\xc4\ +\x56\x24\x43\x02\x02\x31\x93\x28\x65\x8a\xc6\x6c\x44\xd0\xd1\x01\ +\xa8\x22\x73\x96\xc4\x07\x08\x90\x2a\x77\x24\xc9\x49\x04\xe7\x91\ +\xb1\xf3\x8a\x8d\x39\x46\x7b\x2c\x2b\x67\x5e\x66\x9c\x4d\x00\xa1\ +\x92\xc5\xc0\xa6\x48\x05\xac\x94\x14\xeb\xc7\x92\x63\x0d\x55\x15\ +\x4d\xaa\x9a\xd5\xd4\x83\x16\xad\x59\x72\xca\x9a\x73\xb6\xbc\x0e\ +\xb9\x6a\x62\xc9\xd4\xb2\x99\xb9\x15\xab\x2e\x9e\x5c\x3d\xbb\xb9\ +\x7b\xf1\x5a\xb8\x08\xce\x40\x2d\xb9\x58\xf1\x52\x4a\xad\x1c\x2a\ +\x02\x55\xf8\xaa\x18\x5f\x61\x39\xf8\x90\x23\x1d\x7a\xe4\xc3\x0e\ +\x3f\xca\x51\x1b\x96\x4f\x4b\x4d\x5b\x6e\xd6\xbc\x95\x56\x3b\x77\ +\xe9\x38\x26\x7a\xee\xd6\xbd\x97\x5e\x07\x85\x81\x93\x62\xa4\xa1\ +\x23\x0f\x1b\x3e\xca\xa8\x13\x6b\x6d\xca\x4c\x53\x67\x9e\x36\x7d\ +\x96\x59\xef\xd4\x2e\xaa\xef\xca\x17\xa8\xd1\x45\x8d\x37\xa9\x35\ +\xce\xee\xd4\x60\x0d\x66\x37\x17\xb4\x8e\x13\x5d\xcc\x40\x8c\x13\ +\x81\xb8\x2d\x02\x58\xd0\xbc\x98\x45\xa7\x94\x78\x91\x5b\xcc\x62\ +\x61\x6c\x0a\x65\x24\xa9\x8b\x4d\xe8\xb4\x88\x01\x61\x1a\xc4\x3a\ +\xe9\xce\xee\x85\xdc\xa7\xb8\x05\xf5\x4f\x71\xe3\x3f\x91\x0b\x0b\ +\xdd\xbf\x20\x17\x80\xee\x3d\xb7\x0f\xa8\xf5\xf5\x3d\xd7\x36\xb1\ +\x73\x17\x2e\x4d\xa3\x60\xf7\xa1\x7f\x78\x0d\xec\x75\x7d\xa9\xd5\ +\xbf\x6d\x9f\x8e\x9e\x8e\x9e\x8e\x9e\x8e\x9e\x8e\x9e\x8e\x9e\x8e\ +\xfe\x03\x47\x13\x3f\x1e\xf0\x27\x36\xfc\x04\x5e\x6f\x9e\x2d\x7a\ +\x8f\x5e\xda\x00\x00\x01\x23\x69\x43\x43\x50\x49\x43\x43\x20\x70\ +\x72\x6f\x66\x69\x6c\x65\x00\x00\x28\x91\x9d\x90\xbf\x4a\xc3\x50\ +\x14\xc6\x7f\x69\x8b\x16\xd1\x49\x71\xf0\x0f\x64\x70\x2d\xb8\xd8\ +\xc9\xa5\x2a\x04\x41\x21\xc6\x0a\x46\xa7\x34\x49\xb1\x98\xc4\x90\ +\xa4\x14\xdf\xc0\x37\xd1\x87\xe9\x20\x08\x3e\x81\x4f\xa0\xe0\xec\ +\x77\xa3\x83\x83\x59\xbc\x70\xf8\x7e\x1c\xce\xf9\xbe\x7b\x2f\xb4\ +\xec\x24\x4c\xcb\xce\x2e\xa4\x59\x55\x38\xde\xc0\xbf\xf4\xaf\xec\ +\xc5\x37\xda\x6c\xd2\x61\x9b\x6e\x10\x96\xf9\xc0\x75\x4f\x68\x3c\ +\x9f\xaf\x58\x46\x5f\x7a\xc6\xab\x79\xee\xcf\xb3\x10\xc5\x65\x28\ +\x9d\xab\xb2\x30\x2f\x2a\xb0\xf6\xc5\xfd\x59\x95\x1b\x56\xb1\x76\ +\x3b\xf4\x0e\xc5\x0f\x62\x3b\x4a\xb3\x48\xfc\x24\xde\x89\xd2\xc8\ +\xb0\xd9\xf5\xd2\x64\x1a\xfe\x78\x9a\xdb\x2c\xc7\xd9\xc5\xb9\xe9\ +\xab\xb6\x70\x38\xe6\x14\x17\x9b\x11\x53\x26\x24\x54\xf4\xa4\x99\ +\x3a\x47\xf4\xd9\x93\x3a\x14\x04\xdc\x53\x12\x4a\x13\x62\xf5\x66\ +\x9a\xa9\xb8\x11\x95\x72\x72\x38\x10\x0d\x45\xba\x4d\x43\xde\x46\ +\x9d\xe7\x2a\x65\x24\x8f\x89\xbc\x4c\xc2\x1d\xa9\x3c\x4d\x1e\xe6\ +\x7f\xbf\xd7\x3e\xce\xea\x4d\x6b\x7d\x9e\x07\x45\x50\xb7\xda\xaa\ +\xd6\x78\x0c\xef\x8f\xb0\xe2\xc3\xea\x33\x2c\x5d\x37\x64\x75\x7f\ +\xbf\xad\x61\xa6\x5f\xcf\xfc\xf3\x8d\x5f\xb4\xf4\x50\x4b\xa7\xc8\ +\x6e\xd4\x00\x00\x00\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\ +\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95\x2b\x0e\x1b\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\ +\x1b\x04\x29\x1c\xd6\xf7\x32\x02\x00\x00\x20\x00\x49\x44\x41\x54\ +\x78\xda\xed\x9d\x67\x98\x56\xd5\xb9\x86\xef\x61\x2a\x0c\x4d\x10\ +\x18\x8a\x46\x29\x02\x36\xac\x58\x41\xb1\x52\x54\x44\x11\x8c\xf5\ +\x44\x0d\x16\x88\x1a\x4f\xe2\x39\x89\xe9\x39\xc6\x1c\x0b\x6a\xc4\ +\x82\xbd\xc7\x8a\x82\xa2\x1e\x1b\x44\x44\x11\xc1\x8a\x8a\x82\x54\ +\x61\x06\x95\x5e\xa6\xf3\x9d\x1f\x4a\x44\x1d\x60\xe6\x7b\xd7\xda\ +\xf5\xb9\xf7\x9f\x5c\x86\xbd\xbe\xd9\xcf\x7a\xd7\xb3\xde\xbd\xf6\ +\x2a\x20\x84\x10\x42\x08\x21\x84\x10\x42\x08\x21\x84\x10\x42\x08\ +\x21\x84\x10\x42\x08\x21\x84\x10\x42\x24\x8c\x7c\xfa\x31\x86\x77\ +\x59\x4d\x2d\xab\x98\xc9\x8d\x1c\x46\xbe\x64\x11\x22\x0d\x0c\xe2\ +\x43\x32\x3f\xba\x3e\x60\x80\xa4\x11\x22\xd9\xe4\x31\xba\x8e\xc6\ +\xbf\xe9\x1a\x4d\x9e\x24\x12\x22\xa9\x34\xe2\x89\xad\x34\xff\x0c\ +\x19\x1e\x27\x57\x32\x09\x91\xcc\x37\xff\x7f\x6e\xa3\xf9\x67\xc8\ +\xf0\xa8\x46\x03\x84\x48\x62\xef\xff\x78\x3d\x9a\x7f\x86\x0c\x4f\ +\x2a\x0b\x10\x22\x8d\xbd\xbf\xb2\x00\x21\x52\xdd\xfb\x2b\x0b\x10\ +\x22\xd5\xbd\xff\x77\x59\x40\x81\xa4\x13\x22\x7d\xbd\xbf\xb2\x00\ +\x21\x52\xdd\xfb\x2b\x0b\x10\x22\xd5\xbd\xff\x77\x59\x40\x23\xc9\ +\x28\x44\xfa\x7a\x7f\x65\x01\x42\xa4\xba\xf7\x57\x16\x20\x44\x6c\ +\x7b\xff\x47\x1c\x35\xff\x0c\x19\x1e\x53\x16\x20\x44\x9c\x7a\xff\ +\xc7\x1c\x36\xff\x0c\x19\x9e\x50\x16\x20\x44\x1a\x7b\x7f\x65\x01\ +\x42\xa4\xba\xf7\x57\x16\x20\x44\xaa\x7b\x7f\x65\x01\x42\xa4\xba\ +\xf7\x57\x16\x20\x44\xaa\x7b\x7f\x65\x01\x42\xa4\xba\xf7\x57\x16\ +\x20\x44\xaa\x7b\x7f\x65\x01\x42\xa4\xba\xf7\x57\x16\x20\x44\xaa\ +\x7b\x7f\x65\x01\x42\xa4\xba\xf7\x57\x16\x20\x44\xaa\x7b\x7f\x65\ +\x01\x42\xa4\xba\xf7\x57\x16\x20\x44\xaa\x7b\x7f\x65\x01\x42\xa4\ +\xba\xf7\x57\x16\x20\x44\xaa\x7b\x7f\x65\x01\x42\xa4\xba\xf7\x57\ +\x16\x20\x44\xaa\x7b\x7f\x65\x01\x42\xa4\xba\xf7\x57\x16\x20\x44\ +\xaa\x7b\x7f\x65\x01\x42\xa4\xba\xf7\x57\x16\x20\x84\x67\x0a\x22\ +\xdd\xfb\x2b\x0b\x10\x22\xd5\xbd\xff\xa6\xeb\x71\x65\x01\x42\xb8\ +\xee\xfd\x1f\x8d\x49\xf3\x57\x16\x20\x84\xf3\xde\x3f\x4e\xcd\x5f\ +\x59\x80\x10\xa9\xed\xfd\xbf\xb3\x00\x65\x01\x42\xa4\xe8\xdd\x5f\ +\x59\x80\x10\xea\xfd\x95\x05\x08\x91\xf6\xde\x5f\x59\x80\x10\xa9\ +\xee\xfd\x95\x05\x08\x61\xea\xfd\x93\xd0\xfc\x95\x05\x08\x91\xda\ +\xde\x5f\x59\x80\x10\xa9\xee\xfd\x95\x05\x08\x91\xea\xde\x5f\x59\ +\x80\x10\xa9\xee\xfd\x95\x05\x08\x91\xea\xde\x5f\x59\x80\x10\xa9\ +\xee\xfd\x95\x05\x08\x91\xea\xde\x5f\x59\x80\x10\xa9\xee\xfd\x95\ +\x05\x08\xb1\xc5\xde\xff\xb1\x94\x34\x7f\x65\x01\x42\xa4\xb6\xf7\ +\x57\x16\x20\xc4\x8f\x7a\xff\x27\xbc\x35\xb4\x55\xc6\xfb\x9f\x61\ +\x8d\xa7\xbf\xec\xd1\xa0\xb2\x00\x39\x8d\x88\x76\xef\xff\x00\x27\ +\x7b\x2a\xfb\x3e\x6e\x31\x96\xf0\x0e\x47\xb3\xd6\xcb\xdf\x36\x8c\ +\x87\x82\x69\x9b\x32\x00\x11\xe5\xde\xff\x11\x86\x79\x2a\xfb\x21\ +\x7e\x46\xad\xb9\x94\xb7\x38\x82\x35\x5e\xfe\xbe\xa1\xc1\x64\x01\ +\x32\x00\x11\xe5\xde\xff\x14\x4f\x65\xdf\xcb\x59\x64\x9c\x94\x34\ +\x83\xfe\xac\xf7\x64\x01\x0f\xfa\x6f\x9f\x32\x00\x91\xc6\xde\xff\ +\x1c\x36\x3a\x2b\xed\x4d\x8e\x64\x9d\x97\xbf\xf3\x14\xff\x59\x80\ +\x0c\x40\xa8\xf7\xb7\xbf\x08\x1c\x1b\xd7\x2c\x40\x06\x20\xd2\xd5\ +\xfb\x3f\xe8\xb4\xf7\xdf\xc4\x1b\x1c\xe5\x2d\x0b\xf0\x7a\x8e\x80\ +\x0c\x40\xa4\xa9\xf7\xbf\x8b\xb3\x1d\xf7\xfe\x9b\x98\xe6\x6d\x2c\ +\xe0\x64\x9f\x59\x80\x0c\x40\xa4\xa7\xf7\xbf\x8f\x9f\x7b\xe8\xfd\ +\x37\x31\x95\xa3\x3c\x59\x80\xc7\x2c\x40\x06\x20\xd2\xd2\xfb\xdf\ +\xc9\xcf\x3c\xf5\xfe\x31\xce\x02\x64\x00\x22\x1d\xbd\xff\xbd\x8c\ +\xf0\xdc\xfc\x01\x5e\xe7\xe8\x78\x65\x01\x32\x00\x11\x9d\xde\xff\ +\x7e\x8f\xbd\xff\x39\x01\x34\x7f\x80\x37\x19\xc8\x86\xf8\x64\x01\ +\x32\x00\x11\x9d\xde\x7f\x78\xac\x7b\xff\x4d\xbc\xc6\xb1\x9e\x2c\ +\xc0\x43\x16\x20\x03\x10\x49\xef\xfd\xef\x08\xac\xf7\xff\xee\x45\ +\x60\x20\xe5\xf1\xc8\x02\x64\x00\x22\xd9\xbd\xff\x3d\x9c\x1f\x70\ +\xf3\x07\xf8\x17\xc7\x7a\xb2\x00\xc7\x59\x80\x0c\x40\x24\xbb\xf7\ +\x3f\x37\x84\xe6\x0f\x30\x25\x1e\x59\x80\x0c\x40\x84\x4b\x7e\xe2\ +\x7a\xff\x4d\x4c\xa6\x7f\xf4\xb3\x00\x19\x80\x08\xb7\xf7\xf7\xf7\ +\xdd\xff\xf6\xc0\xdf\xfd\x7f\xc8\x6b\xd1\xcf\x02\x64\x00\x22\x99\ +\xbd\xff\xdd\x5c\x10\x81\x27\x8c\x7c\x16\x20\x03\x10\x49\xec\xfd\ +\xc7\x86\xf6\xee\xff\xe3\x2c\xe0\x38\x2a\xa2\x9b\x05\xc8\x00\x44\ +\x38\x14\xf0\xa8\xb7\xde\xff\x8e\x48\xf4\xfe\x9b\x78\x95\x81\x54\ +\x46\x35\x0b\x90\x01\x88\x70\xb8\x97\xa1\x9e\x4a\xbe\x85\x11\x11\ +\x7b\xd6\x49\x1e\xb3\x80\xbb\x65\x00\x22\x7e\x5c\xc9\x4f\x3d\x95\ +\x7c\x3b\x23\x23\xf8\xbc\x2f\x73\x9c\xa7\x2c\xe0\x74\xfe\xa2\x70\ +\x12\xf1\x62\x4f\xaa\x3c\x6d\xa7\x3d\xa6\x41\x7f\xc7\x5f\x8d\xbf\ +\xf6\xe7\x06\xfd\xda\x31\x54\x78\x79\xe6\x4a\x76\x57\x06\x20\xe2\ +\xc4\x9f\xc9\xf7\xd4\xfb\x8f\x8a\xf0\x53\xbf\xc8\x20\x2f\x59\x40\ +\x01\x7f\x92\x01\x88\xf8\xd0\x9e\x13\xbd\x94\x7b\x33\xe7\x47\xfc\ +\xc9\x5f\x61\xb0\x17\x0b\x38\x99\x76\x32\x00\x11\x17\x4e\xf0\x52\ +\xea\xd8\x48\xf7\xfe\x9b\xf8\x3f\x8e\xa3\x2a\x4a\x9a\xca\x00\x44\ +\xd0\xec\xe7\xa5\xf7\xbf\x20\x26\x4f\xff\x32\x27\x7a\xc8\x02\xf6\ +\x95\x01\x88\xb8\xd0\x21\xa5\xbd\xff\x26\x9e\x67\xb0\xf3\x2c\xa0\ +\x93\x0c\x40\xc4\x85\x3c\xc7\xe5\x8d\x89\x4d\xef\xff\xdd\x8b\x80\ +\xeb\x2c\x20\x4f\x06\x20\xe2\xc2\x0a\xa7\xa5\xdd\xca\x2f\x62\xa8\ +\xc1\xf3\x9c\x48\xb5\xc3\xf2\x96\xcb\x00\x44\x5c\xf8\xc8\x61\x59\ +\xff\xe0\xa2\x98\xaa\xf0\x82\xd3\x2c\xe0\x13\x19\x80\x88\x0b\x2f\ +\x39\x2b\xe9\x66\x2e\x89\xb1\x0e\xcf\x31\xc4\x59\x16\xf0\xa2\x0c\ +\x40\xc4\x85\xb7\xf8\xcc\x49\x39\x37\xc6\x6a\xe8\xaf\xee\x17\x81\ +\x21\x4e\x86\x03\xe7\x30\x5d\x06\x20\xe2\xc3\xef\x1d\x94\x31\x86\ +\x4b\x13\xa0\xc4\x44\x4e\x72\x90\x05\x18\xf4\x94\x01\x88\xe0\x19\ +\xc7\xab\xc6\x12\x6e\x88\xe5\xd0\x5f\xdd\x16\x30\xd4\x98\x05\x4c\ +\xe6\x49\x19\x80\x88\x13\x35\xfc\x94\x2f\x0c\xf7\xdf\xc4\x2f\x13\ +\xa4\xc6\x04\x86\x52\x93\xf5\xdd\x4b\x19\x6e\xb8\x5b\x06\x20\x42\ +\xe1\x4b\xfa\x65\x6d\x01\x37\x70\x71\xc2\xd4\x78\x26\xeb\x2c\x60\ +\x29\x87\xf3\xa5\xe5\xa7\x65\x00\x22\x1c\xe6\xd2\x97\xb2\xd4\xf7\ +\xfe\x9b\x18\x9f\x55\x16\xf0\x15\x87\x31\xc7\xf6\xc3\x32\x00\x11\ +\x16\xf3\x39\xb4\xc1\x59\xc0\xf5\x89\xeb\xfd\x37\xcf\x02\x1a\x36\ +\x1c\x58\x46\x5f\xe6\x2a\x8c\x44\x9c\xd9\x99\xd2\x06\x6c\x7d\xf1\ +\x0f\xc7\xbf\x1e\xec\x86\x20\xdb\xe6\x04\x6a\xea\xfd\xdb\x5f\xb1\ +\x8b\x8b\x9f\x54\x06\x20\xe2\x92\x05\x24\xb7\xf7\xdf\xc4\x84\x7a\ +\x67\x01\x65\xf4\x71\x34\x9b\x42\x88\x58\x64\x01\x37\x7a\xf8\xe5\ +\xa8\x65\x00\x00\x83\xeb\x91\x05\x7c\x4d\x77\x57\x3f\xa7\x0c\x40\ +\xc4\x21\x0b\xb8\x2e\xd6\x93\x7e\x1b\xc2\x78\x86\x6f\x23\x0b\x28\ +\xa3\x0f\x9f\x2a\x6c\x44\x92\xe8\xc4\xc7\x5b\xec\xef\x6a\xf9\x2f\ +\x4f\xbf\x1a\xc5\x0c\x00\xe0\x18\xd6\x6c\xf1\x37\x3f\x63\x67\x85\ +\x8b\x48\x1e\x8d\xb9\x92\xf5\x75\x04\xfc\x54\x0e\xf6\xf6\x9b\x51\ +\x35\x00\xd8\x89\x27\xeb\xf8\xbd\x0a\x46\xd3\x44\xa1\x22\x92\x4a\ +\x2b\x2e\x63\x1a\xeb\xa9\xa4\x92\x72\x16\x73\x27\x07\x90\xeb\xf1\ +\xf7\xa2\x6b\x00\x90\x43\x4f\x6e\x64\x1e\xe5\x54\x52\xc9\x06\x66\ +\xf0\x5f\xb4\x51\x88\x88\xe4\x93\x43\x09\x9d\x68\x16\xc0\x2f\x45\ +\xd9\x00\x36\x51\x4c\x27\x4a\xfc\xd9\x60\x9e\xe2\x4d\x44\x8c\x4c\ +\x56\x33\x04\x93\xca\x7a\xd6\xfb\x2c\x5e\x5f\x01\x84\x48\x31\x32\ +\x00\x21\x64\x00\x42\x08\x19\x80\x10\x42\x06\x20\x84\x90\x01\x08\ +\x21\x64\x00\x42\x08\x19\x80\x10\x42\x06\x20\x84\x90\x01\x08\x21\ +\x64\x00\x42\x08\x19\x80\x10\x42\x06\x20\x84\x90\x01\x08\x21\x62\ +\x82\x96\x03\xfb\xb5\xd7\x42\x0a\x28\x20\x9f\x16\x74\xa5\x23\x6d\ +\x69\x47\x3b\x4a\x68\x49\x53\x72\x68\x49\x0e\x4d\x81\x75\x64\x58\ +\x45\x86\x75\xac\xa4\x8c\x2f\x29\xe3\x2b\x96\x30\x97\xd5\x54\x53\ +\x45\x15\x95\x6c\x94\x94\x42\x06\x10\x1f\x1a\xb3\x33\x5d\xd9\x99\ +\xee\xec\x48\x09\x25\x74\x20\x67\x2b\xff\xba\x19\xd0\xbc\x8e\xff\ +\xbe\x91\x52\xca\x28\x65\x11\xf3\x59\xcc\x5c\x3e\x63\xad\xa4\x15\ +\x32\x80\x28\x73\x00\xc7\xd1\x9f\x8e\x14\x51\x44\xd1\x56\x9b\x7d\ +\x7d\xf2\x87\x8e\x74\xfc\xf6\x7f\x57\x51\x4e\x39\xcb\x99\xc4\xb3\ +\xbc\x4c\xad\x84\x16\x32\x80\xa8\x50\x40\x53\x76\x64\x30\x83\xd8\ +\xdf\xe3\x6f\x14\xd0\x82\x12\x76\x63\x14\x30\x9d\x89\x8c\x67\x29\ +\x6b\xa9\x90\xfc\x42\x06\x10\x16\xb9\x1c\xc8\xfe\xec\xc7\x41\x74\ +\x0e\xf4\x77\x7b\xd3\x9b\x3f\x53\xca\x9b\xbc\xce\x74\xde\xd4\x18\ +\x81\x90\x01\x04\x4d\x7b\x2e\xe4\x4c\x5a\xd3\xd4\x98\xe8\x5b\xfe\ +\x82\x93\x18\xc2\x3a\x56\xf2\x0c\xd7\x31\x5f\x55\x22\x44\x10\x09\ +\x7f\x07\xce\xe2\x6d\xe3\x7e\xb2\xee\xaf\xb9\x5c\xc8\x0e\x14\xa9\ +\x82\x1a\x40\x1c\x76\x05\x56\x06\x10\x21\x9a\x71\x3c\xc7\x31\x90\ +\x16\x11\xfc\xdb\xba\x70\x0b\x37\xf0\x1c\x13\x18\xcf\x0a\x55\x95\ +\x10\x6e\xe9\xc4\x9d\x7c\x49\x65\xe4\x7a\xfe\x1f\x5e\xd5\x7c\xcd\ +\x1d\xff\xfe\x7a\x20\x94\x01\x6c\x15\xcd\x04\xdc\x36\x85\x74\x66\ +\x2c\x8b\x39\x97\x36\x14\xc4\x20\xa7\x6b\xcd\x79\x7c\xc1\x23\x74\ +\xd3\x0b\x81\x90\x01\xd8\xc8\xe7\x74\x1e\xe3\x73\x46\xc4\xee\x2f\ +\x1f\xce\x67\x8c\xe3\x34\xbd\xe4\x09\x19\x40\xb6\x0c\xe1\x13\xee\ +\xe6\x84\xd8\xfe\xfd\x03\xb8\x87\x4f\xf9\xa9\x2a\x52\xc8\x00\x1a\ +\x9a\xf6\xef\xc3\x64\xc6\xd1\x25\x06\x49\xff\xd6\x28\xa0\x33\x0f\ +\xf3\x0e\xbd\x29\x54\xa5\x0a\x19\x40\xfd\x38\x9a\xa7\x98\xc9\x61\ +\x89\x79\x9e\xbd\x79\x8b\x71\x31\xce\x64\x84\x0c\x20\x30\xda\xf1\ +\x22\x4f\x33\x20\x71\xcf\x35\x90\x47\x79\x89\x4e\xaa\x60\x21\x03\ +\xd8\x12\xcd\x18\xc1\x62\x8e\xa6\x49\x22\x9f\xae\x88\xa3\x58\xc0\ +\xa8\x40\x0e\xde\x16\x32\x80\xd8\x31\x84\xd7\x19\x4b\x7e\xa2\x9f\ +\x31\x97\x9b\x98\xc2\x50\x55\xb6\x90\x01\x6c\x4e\x21\x13\x78\x8c\ +\x3d\x53\xf1\xac\xbd\xf8\x27\xcf\x27\x34\xcb\x11\x32\x80\x06\x93\ +\xcf\x20\x96\x73\x7c\x8a\xbe\x98\xe7\xd1\x9f\xe5\x0c\xd5\x1c\x01\ +\x21\x03\x68\xcf\x5d\x3c\x4b\x71\xea\x9e\xbb\x88\xc7\xb9\x3b\xe0\ +\x65\xcc\x42\x06\x10\x31\x06\x32\x9d\x33\x53\xfb\xf4\x67\xf2\x3a\ +\x83\xd5\x08\x64\x00\xe9\xa4\x31\x97\x33\x31\xe5\x1f\xc6\xda\xf3\ +\x34\xbf\xd5\x9a\x01\x19\x40\xfa\xd8\x85\xe7\xf9\x5f\x05\x00\x70\ +\x25\xcf\xd1\x53\x32\xc8\x00\xd2\xc4\x61\xbc\x91\xa0\x99\x7e\x56\ +\xfa\x31\x85\x3e\x92\x41\x06\x90\x96\x67\xbe\x88\xc9\xb4\x56\xe5\ +\x6f\x46\x6b\x5e\xe3\x37\xa1\x6d\x6e\x26\x64\x00\x81\x51\xc4\xbd\ +\xdc\xac\x8a\xaf\x83\xbf\x71\x9f\x66\x07\xc8\x00\x92\x4d\x21\xd3\ +\x52\x3c\xea\xbf\x2d\xce\x64\x7a\x0a\x3f\x88\xca\x00\x52\x43\x57\ +\xe6\xd1\x4b\x95\xbe\x15\x76\xe3\x73\xba\x4b\x06\x19\x40\xd2\xc8\ +\x01\x8e\x66\x12\x1d\x54\xe5\xdb\xa0\x1d\xaf\x72\xd4\xb7\x8a\x09\ +\x19\x40\x42\xc8\x70\x4a\xea\xbf\xf9\xd7\x97\x0e\x4c\xe4\x74\x32\ +\x12\x22\x0d\xa4\x65\x36\xf8\xa9\x3c\x14\x9a\xd9\x55\x53\x41\x05\ +\x95\x2c\xe7\x13\x3e\x65\x11\x65\x2c\xa3\x94\x72\x56\x6e\xf6\x6f\ +\xb6\xa3\x31\xed\x29\xa1\x84\x1d\xe8\x41\x0f\x5a\x53\x48\x11\x45\ +\x21\xad\x4d\x2c\xe0\x3e\x6a\x78\x54\xcd\x43\x06\x90\x0c\xce\xe3\ +\xa6\x10\x9a\x7f\x0d\x1f\x31\x8b\xb9\x7c\xce\x3c\xe6\x51\xba\xd5\ +\x7f\xbb\x92\x95\x2c\xfd\xde\x7f\x69\x4f\x67\xba\xd0\x99\x6e\xec\ +\xce\x6e\xe4\x06\xfc\xb7\xe7\x72\x2f\xc5\xdc\xad\x06\x22\xe2\xcf\ +\xef\xa9\x0d\x78\x6f\xfe\x95\xdc\xca\xb1\x74\xa4\xa5\x83\x1d\x05\ +\x0b\x68\x49\x27\xfa\x73\x1b\xab\x03\x7e\x8a\x5a\xae\x48\x78\x64\ +\xe8\x5c\x80\x54\xf4\xfe\xc1\x35\xff\xb5\xcc\xe7\x1a\xf6\xf0\xf6\ +\x2c\xbd\xb8\x96\x79\x01\x1a\x41\x2d\xe7\xca\x00\x64\x00\x71\x25\ +\x07\x38\x95\x0d\x81\x34\x95\x1a\x5e\xe2\x8a\x80\xa6\xd3\xee\xc3\ +\x7f\xf3\x72\x40\xb6\x56\xc1\x30\x92\xfb\x45\x40\x06\x90\x70\x7e\ +\x1a\x48\x33\xa9\x65\x34\x5d\x03\x9e\x40\x53\x4c\x67\xfe\x18\xc8\ +\x41\x65\x35\x9c\xa2\x0c\x40\x06\x10\x47\x8e\xa1\xca\x7b\xff\xf8\ +\x39\x23\x43\x7d\xc6\x51\x7c\x4e\xb9\xe7\xa7\xac\xe4\x48\x19\x80\ +\x0c\x20\x6e\x74\xe7\x0b\xcf\x0d\xe3\x71\x86\x05\x3e\x3a\xff\x63\ +\xf2\x18\xc2\x13\x9e\x9f\x74\x09\xdd\x64\x00\x32\x80\x38\x51\xc0\ +\x52\xaf\x4d\xe2\x05\x7a\x44\x68\xff\xe0\x02\xba\x31\xde\xeb\xf3\ +\x2e\xa5\xb1\x0c\x40\xa7\x03\xc7\x85\xc6\xbc\x4d\x7b\x4f\x65\x57\ +\x31\x8b\x7e\xf4\x67\x36\xd5\x91\x79\xde\x2a\xe6\x30\x98\xde\xbc\ +\xe7\xed\x6f\x6a\xcf\xdb\x89\xb4\x80\xd4\x93\x44\x03\xc8\xe1\x76\ +\x6f\x1b\x7c\xbf\xcb\xb9\xec\xc1\xe4\x48\x3e\xf7\xdb\xec\xcd\xb9\ +\xbc\xed\xa9\xf4\xdd\x18\xab\x2d\x64\x45\x1c\xb8\xc4\x5b\x22\x7c\ +\x7e\x0c\xd6\xcb\x37\xe6\x42\x6f\xcf\x3f\x32\x61\x91\xa2\x57\x80\ +\x04\xd2\xc7\xd3\xc7\xb0\xa9\xb4\x8d\x8d\x06\xdb\xf1\x32\x35\x5e\ +\x74\x38\x5c\x06\xa0\x31\x80\x28\xd3\x93\xa7\x3c\x94\x3a\x9f\x0b\ +\x38\x84\x2f\x63\xa3\xc2\x4a\x8e\xe6\x42\x16\x78\x28\xf9\x49\x7a\ +\xa8\x8f\x11\xd1\x4d\x7f\x27\x7b\xe8\xf5\x9e\xf6\x36\xa0\xe8\x97\ +\x0e\x4c\xf0\xa0\xc6\x2b\x09\xda\x44\x5c\x19\x40\xc2\x32\x80\x8b\ +\x9d\xef\xf4\xbb\x81\xbf\x73\xe2\x36\x56\xf2\x45\x95\xa5\x9c\xc0\ +\x5f\x29\x77\x5c\xea\x11\xfc\x52\x3d\x8d\x88\x22\x83\x9c\xf7\x76\ +\x1f\x72\x48\x02\xc6\x44\xde\x77\xae\x4b\x7f\x65\x00\xca\x00\xa2\ +\xc6\x4e\xdc\xee\xb8\xc4\x49\xf4\x65\x6a\xec\x75\x99\x42\x5f\x5e\ +\x72\x5c\xe6\x9d\xec\xa0\xfe\x26\x19\x24\xc5\x00\xf2\xf9\xab\xe3\ +\xfd\xfe\xae\xe3\x88\xef\xed\xd9\x13\x5f\x56\x73\x0c\x37\x38\x2d\ +\xb1\x23\xff\xa3\x93\x85\x45\x94\x18\xec\xf8\xa3\xdf\x88\xc4\x29\ +\x74\xbe\xe3\xa5\x51\xc7\xeb\x15\x40\xf3\x00\xa2\x42\x63\xc7\xab\ +\xfe\x0f\x4e\xa4\x4a\xbd\x9d\x2e\x8e\x5e\x4f\xa1\x0c\x40\xaf\x00\ +\xd1\xe0\x71\x87\xf3\xd4\xbf\x62\x17\xde\x48\xa4\x01\x4c\x67\x57\ +\xbe\x72\x56\x5a\x13\x1e\x57\xcf\x23\x03\x88\x02\x43\x39\xd6\x59\ +\x59\x4b\x18\xc0\x9c\xc4\xd6\xf6\xa7\x1c\xc3\x42\x67\xa5\x0d\x60\ +\x88\x1a\x90\x0c\x20\x6c\x9a\xf1\x07\x67\x03\x52\xcb\xd8\x8f\x99\ +\x89\xae\xef\xf7\x38\xd0\xd9\x8c\xc6\x3c\xfe\x40\x53\x35\x21\x19\ +\x40\xb8\x9c\xe1\x6c\x13\xce\xa5\xec\x4d\x59\xe2\x6b\xbc\x8c\x3d\ +\xf9\xc2\x51\x59\x7b\x71\xb6\x9a\x90\x0c\x20\x4c\x3a\xf0\x0f\x67\ +\xef\xfe\x83\x63\x3a\xe3\xaf\xe1\x79\xce\x89\x7c\xed\xa8\xac\x1b\ +\x29\x51\x23\x92\x01\x84\xc7\xfd\x8e\xd2\xff\x0c\x87\x30\x23\x35\ +\xb5\x3e\xd3\xd9\xfe\xc5\xb9\xdc\xa7\x46\x24\x03\x08\x8b\x41\x8e\ +\xa6\xea\xd6\x70\x70\x82\x87\xfe\xea\x62\x36\x87\x50\xeb\xa4\xa4\ +\x3e\x89\x99\x18\x2c\x03\x88\x19\x85\x8c\x74\xb4\x32\x6d\x14\xd3\ +\x52\x57\xf3\x6f\x30\xd2\xc9\x01\xa0\x8d\x19\x95\x80\x19\x01\x32\ +\x80\x18\xd2\x8b\x01\x4e\xca\xb9\x8e\xb1\xa9\xac\xfb\xb1\x8e\xc6\ +\x4f\x06\x79\x3c\x0b\x49\xc8\x00\xb6\xc8\x2d\x4e\x4a\x79\x99\x5f\ +\xa5\xb6\xf6\x2f\xe5\x55\x27\xe5\xdc\xa4\x86\x24\x03\x08\x9a\xa1\ +\xec\xeb\xa0\x94\x8f\x19\x9e\xea\xfa\x1f\xca\x47\x0e\x4a\x39\x50\ +\x53\x82\x64\x00\xc1\x92\xcf\xff\x3a\x28\x65\x03\x23\x58\x91\xea\ +\xfa\x5f\xc9\x45\x4e\xb6\x0c\xb9\x5a\xab\x03\x65\x00\x41\x32\x9c\ +\x4e\x0e\x4a\xb9\x31\x01\xeb\xfd\xad\xbc\xc6\x68\x07\xa5\xec\x98\ +\xe0\x13\x04\x65\x00\x91\xa3\x90\x61\x14\x98\x4b\x79\x96\xdf\x2a\ +\x00\x80\xdf\x31\xc1\x5c\x46\x01\xa7\xea\x5b\x80\x0c\x20\x28\x76\ +\x74\xb0\x1a\x7d\x3e\xe7\xab\xfa\xbf\xe5\x42\x07\x4b\x84\x4e\x70\ +\x92\x93\x09\x19\x40\x3d\xf8\xa3\xb9\x84\x5a\xae\x62\xa9\xaa\xff\ +\x5b\x96\xf2\x57\x07\xd3\x82\xfe\x20\x21\x45\x10\x74\x72\xb0\x9d\ +\xc5\x64\x72\x24\xe4\x66\xe4\x30\xc5\x81\xaa\x1d\x62\xf6\xd4\xda\ +\x10\x24\x96\x19\x80\x0b\xd9\x4f\x71\x32\x0b\x2e\x39\x64\x38\xc9\ +\x41\x29\x7f\x92\x90\x32\x00\xdf\xb4\xe0\x04\x73\x19\xe7\x3b\xdc\ +\x19\x27\x29\x7c\xc5\x28\x73\x19\x43\x68\x21\x21\x65\x00\x7e\x19\ +\x6c\x0e\xb2\x77\x79\x50\x15\x5f\x07\x77\xf3\x8e\xd9\x9c\x8f\x97\ +\x8c\x32\x00\x9f\x14\x32\x88\x7c\x53\x09\x55\x5c\xc7\x06\x55\x7c\ +\x1d\x94\x73\x03\xd5\xa6\x12\xf2\x19\xa4\x8f\x81\x32\x00\x9f\x6c\ +\xcf\x40\x63\x09\x9f\xf0\x90\xaa\x7d\x0b\x3c\xc0\x6c\x63\x09\x03\ +\x69\x2d\x19\x65\x00\xfe\x18\x60\xde\x85\xee\x22\x55\xfa\x56\x18\ +\x69\xbc\xbf\x39\xc7\x48\x44\x19\x80\x3f\xac\x07\x53\x4e\x48\xe8\ +\x96\xdf\xae\x98\xc2\x73\xc6\x12\x2e\x4d\x91\x5a\x19\x19\x40\xb0\ +\xec\xc4\xae\xc6\x12\x2e\x57\x1b\xf7\xdc\x80\x7b\xc5\xe8\xdc\x40\ +\xeb\x32\xa8\xf5\x32\x80\x78\x05\xe7\x13\xcc\x57\x0b\xdf\x06\x0b\ +\x19\x67\x2c\xe1\x92\x18\x3d\xab\x0d\x45\x53\xa0\xe4\xb0\xc0\x34\ +\x6f\xab\x9c\xa1\x12\xb1\x1e\x0c\xa3\xc2\xa4\xf3\x82\xd8\x74\x2b\ +\x5d\x8d\x33\x01\x3b\x2a\x58\x82\xe4\x50\xd6\x98\xaa\x6b\x9e\xd6\ +\xac\xd7\x8b\x5c\x3e\x37\xe9\xbc\x9a\x83\x62\xf2\xa4\x05\xcc\x31\ +\x3c\xe7\x8c\x24\xc4\x53\x9c\x5e\x01\xf6\x37\x7e\x01\xb8\x96\x1a\ +\xb5\xee\x7a\x50\x6b\x3c\x4c\xbc\x19\x07\xc4\xe4\x49\xab\x4c\x9f\ +\x84\xef\x50\x3c\x05\x49\x21\x8f\x98\xfa\xa5\x6a\x49\xd8\x00\x6a\ +\x4c\x5a\x3f\xec\x60\xb7\x86\x60\x68\xc5\xf2\x2c\x9f\xf1\x63\x72\ +\x15\x26\x41\xd2\x9a\xf9\xa6\xa0\xbc\x4a\x12\x36\x80\x6b\x4c\x5a\ +\xcf\xa5\x55\x6c\x9e\xf4\xc4\x2c\x0f\x47\xdf\x55\x41\x12\x2c\x7b\ +\x99\x42\xb2\x86\x2e\x92\xb0\x01\x74\xa5\xd6\xa4\xf7\x9e\x31\x7a\ +\xd6\x3f\x65\x31\x9c\x7c\x82\x42\x24\x68\xfe\x60\x0a\xc8\x97\x28\ +\x96\x84\x0d\xa0\x98\x57\x4c\x7a\x5f\x11\xab\xa7\xbd\xb4\x41\x76\ +\x57\xca\xc1\x0a\x90\xe0\x99\x9e\xa2\x80\x8c\x02\xbf\x35\xe9\x1d\ +\xb7\xf9\x96\xbd\x99\x56\xaf\xe7\x5a\xc7\x58\x5a\x2a\x38\xc2\xc0\ +\x12\x8e\x6b\x62\x33\x2e\x1d\x1d\x0e\x36\x7e\x74\x8d\x1b\x39\x1c\ +\xc4\x75\xbc\xcd\xaa\x3a\xb3\x81\x0a\x16\xf0\x34\x17\xc4\x6e\xcf\ +\xa3\xc4\x70\xb8\x29\x18\x3f\x97\x80\x59\x60\x1b\x74\xed\x23\x01\ +\xe3\x40\x5c\xe6\x01\x0c\x32\xdd\x3d\x4e\x15\x9d\x05\x4f\x9b\xee\ +\x1e\x28\x01\x85\x3b\xde\x35\xf5\x46\x3a\xbc\x32\x1b\xf6\x36\x69\ +\xfe\x8e\x04\x14\xae\x68\x46\xa9\x21\x14\x57\x4a\xc0\x2c\xb1\x8c\ +\x02\x94\xea\xbb\x8b\x5e\x01\x5c\xd1\x99\x22\xc3\xdd\xff\x54\x35\ +\x67\xc9\xa3\x86\x7b\x8b\xe8\x2c\x01\x65\x00\x51\x30\x80\xf1\xaa\ +\xe6\x2c\x79\x4a\x06\x20\x03\x88\xbb\x01\xd4\x30\x4b\xd5\x9c\x25\ +\x1f\xb0\x51\x06\x20\x03\x08\xff\x6f\xb4\x4c\xe3\xfd\x28\x09\xfb\ +\xb6\x84\xc4\x3a\x93\x79\x76\x89\xed\xd9\xd3\x32\x80\x48\x51\xc8\ +\x8e\x26\x03\xd0\x26\xe0\xd9\xb2\x81\x8f\x0c\x77\xef\xa8\x2d\xc2\ +\x65\x00\x2e\x28\xa0\xc4\x70\xf7\x67\x54\xa9\x9a\xb3\xa4\x8a\xcf\ +\x0c\x77\x97\xc4\x66\x51\xb0\x0c\x20\xe2\x06\xd0\x3e\xeb\x7b\xab\ +\x35\x0b\xd0\xc4\x7c\xc3\x3e\x0a\x25\xc6\x23\x5c\x84\x0c\x00\x80\ +\x7c\x83\x01\x54\x30\x4f\x95\x6c\x60\x1e\x15\x59\xdf\xdb\x41\x06\ +\x20\x03\x70\x41\x4b\xc3\x51\xde\x32\x80\xf0\x0c\x20\x57\x47\x85\ +\xca\x00\x5c\xb0\x8b\xe1\xde\x4a\xca\x54\xc9\x06\x96\x98\x46\x50\ +\x76\x91\x80\x32\x00\x3b\x96\x83\x26\x96\xab\x8a\x8d\x58\x26\x52\ +\x77\x92\x7c\x32\x00\x3b\xed\x0d\xf7\x7e\xa2\x2a\x36\xf2\x49\x48\ +\x35\x27\x64\x00\xdf\x62\xf9\x08\x38\x5b\x55\x6c\x64\x76\x48\x35\ +\x27\x64\x00\x0e\xc2\x68\xb1\xaa\xd8\xc8\x62\x19\x80\x0c\x20\xbe\ +\xaf\x00\x1a\x02\xb4\x52\xaa\x57\x00\x19\x40\xb8\xb4\x36\xdc\xbb\ +\x4c\x55\x6c\xc4\x62\xa1\x6d\x24\x9f\x0c\xc0\x4e\xe3\x90\xfa\x2f\ +\x61\x35\x80\x22\xc9\x27\x03\xb0\x63\x59\x52\xb2\x56\x55\x6c\x64\ +\x8d\x0c\x40\x06\x10\xdf\x0c\xa0\x5c\x55\x6c\xa4\x42\x06\x90\x64\ +\x72\x62\xf0\x37\x66\x12\xfe\x7c\xd2\x5f\x28\x03\x10\x42\xc8\x00\ +\x84\x10\x32\x00\x91\xfa\x97\x07\x21\x03\xf8\x16\xcb\x40\x5e\x9e\ +\xaa\xd8\x88\xe5\x1b\x8c\x36\x63\x93\x01\x38\xc0\x12\x46\x8d\x55\ +\xc5\x32\x00\x91\x5e\x03\x68\xa6\x2a\x36\xd2\x42\x06\x20\x03\x88\ +\xef\x2b\x80\x66\xa3\x5b\x29\x91\x01\xc8\x00\xc2\x65\x7d\x48\xe1\ +\x2b\xac\x16\x2a\x03\x90\x01\x38\x60\x85\x0c\x20\x44\xda\x19\xee\ +\xd5\x7e\x4c\x32\x00\x07\x2c\x30\xdc\xab\x4d\xa9\xac\x58\x0e\x65\ +\x59\x28\xf9\x64\x00\x76\x2c\x5b\x52\xf4\x54\x15\x1b\xe9\x6e\xb8\ +\x77\x91\xe4\x93\x01\xd8\xb1\x84\x51\x0f\x55\xb1\x91\x9e\x32\x00\ +\x19\x40\xb8\x58\x12\xc9\xd6\xaa\x62\x13\x39\x6c\xa7\x57\x00\x19\ +\x40\x7c\x5f\x01\x0a\xf5\x21\xd0\x44\x07\xd3\xf9\x7e\x4b\x24\xa0\ +\x0c\xc0\x8e\xe5\x63\x92\x4e\xa9\xb7\xd1\xd9\xb4\xa6\x5f\xbb\x31\ +\xc8\x00\x1c\x50\x6d\xd8\xd9\x4f\x06\x60\xa3\x8b\xc1\x00\x4a\x75\ +\x2e\xb3\x0c\xc0\x05\x15\x86\xc1\xa4\x7c\xba\xa8\x92\x0d\xec\x6c\ +\x38\xe0\x73\x11\x95\x12\x50\x06\x60\xa7\xd2\x34\x98\xd4\x4d\xa7\ +\xd4\x67\x4d\x01\xdd\x0c\x77\xcb\x00\x64\x00\x11\x30\x80\xdd\x69\ +\xa2\x6a\xce\x92\x26\xec\x2e\x03\x90\x01\x84\xcf\x62\xc3\xd6\x12\ +\xbb\xd1\x54\xd5\x9c\x25\x4d\x0d\x06\x90\xd1\x47\x40\x19\x80\x2b\ +\x2c\x7d\x49\xae\xa9\x17\x4b\x37\xbd\x0c\x9b\x7a\x56\x68\x1a\x90\ +\x0c\xc0\x15\x0b\x4d\xc9\xe4\x10\x55\x73\x08\xca\x55\x2a\x03\x10\ +\xee\x52\xd1\xa5\x64\xb2\xbe\x56\x49\xc0\x2c\x59\x67\x50\x7d\xa9\ +\xc6\x5e\x94\x01\xb8\x0b\xc4\x05\x86\xbb\x5b\xd0\x4b\x15\x9d\x05\ +\xfb\x50\x6c\xb8\x7b\x9e\x76\x03\x90\x01\xb8\xe3\x45\xd3\xdd\x67\ +\xaa\xa2\xb3\xe0\x2c\xd3\xdd\x2f\x48\x40\xe1\x8e\x03\x0d\xc9\x68\ +\x86\x79\x12\x30\x0b\xe6\x9b\x34\xef\x2d\x01\x85\x4b\x2c\xc1\xb8\ +\x86\x83\x24\x60\x03\x39\x84\xb5\x26\xcd\x85\x5e\x01\x9c\x32\xcd\ +\x70\x6f\x33\x8e\x50\x55\x37\x90\x23\x4c\xf3\x27\xa6\x4a\x40\x19\ +\x80\x5b\x9e\x33\xdd\xdd\xcf\x34\xa0\x95\x3e\x8a\xe9\x67\xba\xff\ +\x79\x49\x28\xdc\xb2\x87\x29\x21\xad\xa5\xab\x24\x6c\x00\x5d\xd9\ +\x68\xd2\x5b\x93\xaf\x94\x01\x38\x66\x09\x73\x4d\xcf\xf9\x73\x55\ +\x76\x03\xb8\xc0\x74\xb0\xf7\x67\x2c\x95\x84\xc2\x2d\x05\x3c\x68\ +\xea\x93\x6a\x24\x61\x03\xa8\x35\x69\x7d\xbf\x61\x11\xb1\x50\x06\ +\x50\x27\x55\xbc\xc5\x46\xc3\xfd\xb9\x8c\x54\x75\xd7\x93\x8b\x4d\ +\x71\xb1\x91\xb7\xa8\x96\x88\xc2\x35\xbd\x59\x6d\x9c\x0d\xa0\x7e\ +\xa9\x3e\xe4\x31\xcf\xa4\xf3\x2a\xf6\x95\x88\xc2\x3d\x39\x2c\x34\ +\x05\x66\x05\xa7\x48\xc4\x7a\x70\x2a\x15\x26\x9d\x17\x48\x42\xbd\ +\x02\xf8\x20\xc3\x04\xd3\xfd\x85\x0c\xd7\xee\x40\xf5\x18\x6b\x19\ +\x6e\x3a\x12\x1c\xc6\x49\x44\xe1\x87\x1d\x4c\x3d\x53\x86\x8c\xe9\ +\x9c\x9b\x74\xb0\x8b\x59\xe3\x0e\x12\x51\xf8\x62\x86\x31\x38\x9f\ +\x91\x84\xdb\xe0\x05\xa3\xc2\x6f\x4a\x42\xe1\x8f\xe1\xe6\xfe\xa9\ +\x8f\x44\xdc\x0a\x87\x99\xf5\x3d\x59\x22\x0a\x7f\x94\xf0\xb5\x31\ +\x40\x3f\x90\x88\x5b\xe1\x43\xa3\xba\x5f\x9a\x8e\x13\x17\x81\xd3\ +\x28\x66\x7f\xef\x4a\x5e\x31\x96\xd0\x5d\xbb\x03\x6c\x91\xff\x30\ +\x8f\x91\xbc\xcc\x4a\xc9\x28\x7c\xf2\x33\xaa\x8d\xbd\xd4\xbb\x5a\ +\x18\x54\x27\xc5\xbc\x6f\x54\xb6\x8a\xb3\x25\xa3\xf0\x4b\x73\xbe\ +\x34\xbf\xa7\x5e\x28\x19\xeb\xe0\x17\x66\x5d\xbf\xa4\x99\x64\x14\ +\xbe\xb9\xde\x1c\xa8\x19\xda\x4a\xc6\x1f\xd0\xc6\x81\xaa\xd7\x48\ +\x46\xe1\x9f\x96\x0e\x42\xf5\x5f\xb1\x1b\xfd\xf0\x4b\x0e\xaf\x3b\ +\x50\xb5\xb9\x84\x14\x41\x70\xbb\x39\x54\x6b\x18\x21\x19\x37\x63\ +\x04\x35\x66\x4d\x6f\x95\x8c\x22\x18\xba\x3a\xe8\xad\xe6\x6b\xc6\ +\xda\xbf\xe9\x68\x5c\x65\xf1\xcd\xa5\x73\x98\x45\x40\x14\xf1\xac\ +\x83\x80\xd5\xac\xc0\x4d\x4c\x74\xa2\x66\x91\x84\x14\x41\x31\xcc\ +\xb8\x62\xed\x9b\xeb\x2a\x09\x09\x5c\xe9\x40\xc9\x0a\x86\x4a\x48\ +\x11\x24\x9f\x38\x08\xdb\x0d\xf4\x4d\xbd\x8e\xfd\x28\x77\xa0\xe4\ +\x47\x0a\x48\x11\x2c\x03\x1c\x84\x6d\x86\x8f\x69\x9d\x6a\x15\x5b\ +\xf1\x91\x13\x1d\xfb\x2b\x20\x45\xd0\x4c\x75\x12\xba\xaf\xa5\x5a\ +\xc3\x49\x4e\x34\x9c\xa2\x60\x14\xc1\xb3\x9f\x93\xe0\xcd\x70\x7d\ +\x6a\x15\x1c\xe3\x48\x41\x6d\x01\x26\x42\xa0\x90\x67\x1c\x05\x70\ +\x3a\xa7\x06\x5f\xe4\x48\xbd\x67\x8d\x3b\x08\x09\x91\x25\x47\xb3\ +\xde\x49\x08\xd7\x72\x58\xea\xb4\x3b\xd4\xc1\xd4\x9f\x0c\x19\xd6\ +\x73\x94\x02\x51\x84\xc5\x44\x47\xbd\x58\xda\x36\x0b\xeb\xe9\x4c\ +\x37\xcd\xa6\x10\x21\xb2\xbd\x93\x8f\x58\x19\x32\xac\x60\xff\xd4\ +\xa8\xb6\xaf\x79\x5b\x95\x4d\x57\x65\xca\xbf\xa2\x88\xd0\x39\xdb\ +\x59\x5f\xb6\x24\x25\x93\x83\xdb\xb1\xd8\x99\x66\xe7\x28\x00\x45\ +\xb8\x34\xe5\x6d\x67\xe1\x5c\x46\xfb\xc4\xeb\xd5\x9e\x65\xce\xf4\ +\x9a\x6e\x3a\x42\x5c\x08\x27\x9c\x6c\xde\x23\xe8\xbb\xeb\x0b\xf6\ +\x4b\xb4\x56\xfb\xb0\xc4\x99\x56\xd5\x0c\x51\xf0\x89\x28\xf0\x94\ +\xb3\xa0\xce\xf0\x15\x3d\x12\xab\xd3\xae\xce\xde\xfd\x33\x64\x78\ +\x52\x81\x27\xa2\x41\x0e\x2b\x1d\x06\x76\x86\x43\x13\xa9\x52\x5f\ +\xa7\x1a\xad\x22\x57\x81\x27\xa2\xc2\x51\x6c\x74\x18\xdc\x35\x09\ +\x9c\x1a\x34\xd2\x78\xe4\xf7\x0f\xaf\x23\x14\x74\x22\x3a\xe4\x72\ +\x87\xd3\xf0\xce\x70\x43\xa2\xf4\x19\xe3\x58\x9d\xdb\xd5\xff\x8b\ +\x68\xb1\x13\x5f\x38\x0e\xf2\x49\x09\xf9\xc6\xdd\x9a\xd7\x1c\x2b\ +\xb3\x84\x9f\x28\xe0\x44\xd4\x18\xe0\x38\xcc\x33\xcc\x4e\xc0\x14\ +\xe1\x7e\x7c\xec\x5c\x97\x81\x0a\x36\x11\x45\x2e\x74\x1e\xea\x1b\ +\xb8\x3a\xd6\x8a\x5c\xe9\x6c\xa6\xe4\x77\xd7\x28\x05\x9a\x88\x26\ +\x85\x4c\x70\x1e\xee\x19\x26\xd2\x31\x96\x6a\x74\xe0\x69\x0f\x6a\ +\x3c\x4d\x81\x02\x4d\x44\x95\x2e\x94\x79\x08\xfa\x05\x5c\x10\xb3\ +\x41\xaf\x1c\x46\x38\xd9\xe9\xf7\x87\xd7\x32\x3a\x2b\xc8\x44\x94\ +\x39\xc4\x43\xd8\x67\xa8\x65\x6a\x8c\xce\xbd\x6d\xc7\x64\x47\x8b\ +\x7d\x7f\x78\x69\x0f\x45\x11\x79\x2e\xf1\x12\xfa\xdf\xbc\xfd\x46\ +\x7f\xee\x7b\xb1\x83\x33\xfe\xb6\x74\x8d\x54\x70\x89\x38\x70\x9b\ +\xb7\x26\x30\x8b\xb3\x22\xfd\xe4\x67\xf3\x9e\xb7\x67\xbf\x45\x81\ +\x25\xe2\x41\x1e\x6f\x79\x6b\x06\x55\xcc\xa2\x5f\x24\x9f\xba\x37\ +\xef\x51\xe5\xed\xb9\x67\x90\xaf\xc0\x12\xf1\xb1\x80\xc5\xde\x9a\ +\x42\x86\x0c\x93\xe9\x19\xa1\xd1\xf0\x42\x76\x61\xbc\xd7\xe7\x5d\ +\xac\x99\x7f\x22\x5e\x74\x75\xb8\xf0\xb5\xee\x6b\x1c\xc3\xc8\x0b\ +\xfd\x39\xf3\x19\xee\x74\x35\x64\xdd\x8b\xa4\x75\xee\x9f\x88\x1d\ +\x7d\xa9\xf4\xdc\x30\x2a\x98\xc7\x2f\x42\x7d\xc6\x8b\x99\xef\xe4\ +\x90\xb4\xad\x3f\x65\x1f\x05\x93\x88\x23\xc7\x78\xfa\x18\xf6\xfd\ +\x6b\x23\xa3\xe9\x46\x71\xa0\x4f\x56\x4c\x17\xfe\xe8\xf1\x8d\x7f\ +\xf3\x6d\x3f\x8e\x53\x20\x89\xb8\x32\x98\xb5\x01\x34\x92\x0c\xb5\ +\xbc\xcc\xef\x02\xea\x29\xf7\xe1\x37\xbc\xea\x74\xf9\xf3\xd6\x36\ +\xfd\x3e\x5e\x41\x24\xe2\xcc\x19\x81\x64\x01\xdf\x5c\x6b\x99\xcf\ +\x68\xf6\xf2\xd8\xf4\x6f\x60\x3e\xab\x02\x7b\x9e\x1a\x4e\x53\x00\ +\x89\xb8\xf3\x6b\xc7\x5b\x61\x6c\xfb\x5a\xc3\x1d\x0c\xa4\x13\x2d\ +\x1d\x7c\x29\x28\xa0\x25\x9d\x18\xc4\x5d\x01\xe5\x32\x9b\x67\x35\ +\x97\x29\x78\x92\x4e\x4e\x2a\x9e\xf2\x1c\xc6\xd0\x38\xf0\x5f\xad\ +\xe5\x63\x66\x31\x87\xcf\x99\xcf\xe7\x2c\x6d\x60\xbd\x74\xa0\x33\ +\x9d\xe9\x42\x57\x76\x67\x37\x1a\x05\xfe\xd7\x6f\xe0\x22\xee\x53\ +\x03\x91\x01\x24\x65\x2c\xe0\xc9\xd0\xbe\x64\x57\x53\x41\x05\x55\ +\xac\x60\x36\xb3\x59\x44\x19\xcb\x28\x65\x03\xab\xa9\xfd\xf6\x5f\ +\xe4\xd2\x82\x26\xb4\xa7\x1d\x25\xec\x48\x0f\x7a\xb2\x1d\x05\x14\ +\x51\x14\xda\xe4\x9b\x5a\x4e\x62\x82\x9a\x87\x48\x0e\x47\xb2\x21\ +\xe0\x14\x3a\xbe\x57\x39\x03\x14\x30\x22\x69\xf4\xf6\xb2\x40\x36\ +\x79\xd7\x62\x0e\x56\xb0\xe8\x15\x20\x89\x74\x67\x32\x25\xaa\xf4\ +\xad\x52\x4a\x3f\x3e\x95\x0c\x32\x80\x64\x52\xc0\xd4\x84\x9f\xfd\ +\x63\xe3\x3d\x0e\x61\x83\x64\x48\x0f\x8d\x52\xf6\xbc\x55\x1c\xc8\ +\x18\x55\xfb\x16\xb8\x8d\xde\x6a\xfe\x22\xf9\x9c\xa5\x37\xfd\x3a\ +\xae\xf3\x52\x97\x0f\x8a\xd4\x72\x98\xd3\x53\xf2\xe2\x7f\x7d\xad\ +\xcd\xbe\x44\xba\xe8\xca\x38\x35\xfc\x6f\xaf\x17\xe8\xae\x80\x10\ +\x69\xa3\x80\x11\x6a\xfc\x64\x18\x45\x91\x82\x41\xa4\x93\xc3\x53\ +\x3e\x37\x60\x21\xc7\x28\x08\xd2\x4c\xda\x37\x7a\x5a\xcc\x46\xfa\ +\xa7\xf8\xf9\xaf\xe6\x51\x6a\xd4\x0c\x44\x1a\xd9\x83\x5b\x29\x4b\ +\xf9\x04\xe1\x4a\x96\xf1\x30\x87\x28\x18\x44\x7a\xc8\xa3\x35\x67\ +\x33\x47\x6f\xff\x9b\x5d\x9f\x71\x16\x6d\x74\xe8\x57\xfa\x48\xdf\ +\x4c\xc0\x81\x1c\xcf\xe0\x84\x1c\xfc\xed\x96\xd5\x3c\xcb\x44\x9e\ +\x63\xb5\xa4\x10\x49\xa4\x90\xbf\xb1\xc8\xc3\x69\xb9\xc9\x7a\x25\ +\xf8\x82\xd1\xfa\x2a\x20\x92\x45\x11\xdd\x18\xad\xe6\xdd\x80\xeb\ +\x2e\xba\xcb\x06\xf4\x0a\x90\x8c\xc6\x3f\x94\xe1\x0c\x4c\xdd\xaa\ +\x07\x2b\xb5\x3c\xc7\xe3\x3c\xc5\x3a\x49\x21\xe2\xcb\x48\xe6\x07\ +\xb2\x79\x76\x32\xaf\x6a\x16\x71\x85\x82\x48\xc4\x91\xd6\x9c\x17\ +\xf8\x36\x9a\xc9\xbc\xd6\x70\x1e\x6d\x14\x50\x7a\x05\x88\x0f\xed\ +\x39\x87\x33\xe8\xa1\xea\x75\xc6\x42\xee\xe1\x5e\x16\x4a\x08\x11\ +\x75\xf2\xb8\x46\x2b\xfd\xbc\x5c\x2b\xb8\x9d\x42\x05\x98\x88\x2e\ +\x2d\x39\xd7\xfb\x79\x80\xe9\xbe\x6a\xb9\x58\x73\x28\xf4\x0a\x10\ +\x45\x9a\x70\x0e\xe7\x7a\x3c\x95\x47\x6c\xe2\x53\x6e\xe3\x5e\x56\ +\x49\x08\x11\x1d\xce\x60\x41\xe0\xe7\xff\xa4\xf7\xda\xc8\x42\x4e\ +\x57\xd0\x89\x28\xd0\x88\xdd\x98\xaa\x46\x19\xc2\xf5\x36\xbd\x52\ +\xbf\x9a\x54\x84\x4c\x2f\x6e\xa3\x42\x8d\x31\xb4\x4c\xe0\x4e\xf6\ +\x54\x10\x8a\xb0\xf8\x0b\x2b\xd4\x0c\x43\xff\x36\x70\x9d\x02\x51\ +\x83\x80\x41\x93\x4b\x2f\x1e\xa5\xab\x2a\x30\x12\x2c\x62\x18\x33\ +\xb5\xb1\x48\x3c\xdf\xa0\xe3\x48\x77\xee\x60\xa6\x9a\x7f\x64\xd8\ +\x91\x69\xdc\xab\x8d\x45\xe3\xd9\x93\xc6\x8f\x5f\xf1\x10\x07\xa8\ +\xea\x22\xc6\x9e\x9c\x4d\x23\xa6\x48\x08\xbd\x02\xf8\x64\x67\xee\ +\xa2\x5f\x0c\xfe\xce\x1a\xaa\xa8\xa2\x9a\x2a\x6a\x58\x46\x19\x65\ +\x94\x51\x4a\x29\xcb\xd8\xc0\x3a\x36\xb2\x96\x0c\x2b\x36\xfb\xf7\ +\xad\xc8\xa1\x19\x8d\x68\x4a\x13\xda\xd1\x81\x12\x4a\x28\xa1\x3d\ +\x6d\xc9\xa3\x80\x7c\x0a\x28\x20\x2f\x06\xcf\x3d\x89\x73\x58\xa0\ +\x66\x25\x03\xf0\x41\x31\x97\x72\x39\xcd\x23\xfc\x17\x2e\x66\x01\ +\xa5\x94\x51\xfa\x6d\x93\x2f\x63\x19\x19\x73\x0d\xb5\xfb\xd6\x0c\ +\xbe\x31\x85\x0e\xec\x44\xa7\x08\x6b\xb0\x9a\xab\xb9\x41\x07\x8c\ +\xc9\x00\x5c\xb3\x13\xe3\x23\xfa\xc9\xa9\x96\x29\xcc\xe0\x75\x66\ +\x52\x41\x25\x55\x54\x99\x1b\xfd\x96\x6b\xab\x80\x02\x0a\x29\xa4\ +\x0b\x7d\xd8\x8f\xc3\xd8\x2e\x92\x8a\xbc\xcf\x60\x2d\x1c\x12\xee\ +\xc8\x8b\xdc\x11\x1e\xeb\xf9\x9a\x59\xdc\xc0\x40\x9a\x85\xaa\x4c\ +\x73\x06\x72\x23\xb3\xf8\x92\x35\x11\x9b\x09\x79\x2e\xf9\x0a\x5c\ +\x65\x00\x2e\xd8\x95\xab\x19\x14\x91\xbf\xa5\x82\x0f\xf8\x90\x4f\ +\x98\xc5\xec\x48\xf5\x71\x6d\xe9\xce\x5e\xec\xc5\xee\xf4\x8a\xcc\ +\x8a\xbd\x17\xb8\x8c\x4f\xd4\xc4\x84\x8d\x73\x59\x17\x91\x3e\xed\ +\x19\x4e\xa6\x84\xe6\x11\x1e\x8c\xcb\xa3\x39\x25\x1c\xcd\x03\x11\ +\x59\x13\xb9\x96\xff\x54\x00\x8b\xec\xd9\x8e\x31\xa1\x2f\x7f\x5d\ +\xcd\x62\x1e\xe4\xe8\xd8\x69\xd7\x87\x3b\x59\xc8\x6a\x36\x86\xac\ +\xe0\xd8\x88\x8e\x53\x88\xc8\xd3\x97\xe9\xa1\x86\x6e\x39\x2f\x72\ +\x29\xfb\xc7\xfa\x05\x6f\x7f\x2e\xe5\x69\xd6\x84\xaa\xe3\x34\x8e\ +\x50\x30\x8b\x86\x72\x29\xab\x43\x0c\xda\x77\x39\x97\xb6\x34\x49\ +\x84\x92\x45\xb4\xe5\x24\xa6\x84\xfa\x2a\xf0\x6b\x05\xb4\xa8\x3f\ +\x4d\xb8\x2e\xa4\x50\x5d\xc7\x1c\xae\x62\xc7\x44\xaa\xda\x92\xff\ +\x66\x76\x68\x23\x2a\x63\x68\xac\xc0\x16\xf5\x61\xcf\x90\x52\xff\ +\x05\x5c\x1f\x8b\x59\x86\x36\x8e\xe0\x5a\x66\x87\xf4\x2a\xa0\xfd\ +\x9a\xc4\x36\x39\x3e\x94\xd4\x7f\x32\xc7\xd0\x32\x35\x27\x25\x36\ +\xe7\x58\xfe\x15\x82\xca\xab\x23\xf3\x39\x57\x44\x74\xd0\xea\xb2\ +\xc0\xb7\xb4\x58\xce\x83\xf4\x4c\xa5\xda\x9d\xb9\x93\xaf\xa9\x09\ +\x58\xf1\xdf\xa4\xee\x40\x5a\x51\x4f\x8a\xb9\x3f\xe0\x60\x7c\x8f\ +\x5f\xd3\x21\xd5\x9a\xb7\xe1\x62\xde\x0c\x58\xf5\x87\x43\x9e\x3d\ +\x29\x22\x49\xab\x80\xdf\x4c\x67\xd2\x87\x16\x92\x1d\x68\xc6\x01\ +\xbc\x16\xa8\xf6\x73\x68\x2b\xd9\xc5\xe6\xf4\x0e\xf0\x18\xaf\xf5\ +\x4c\xa5\xaf\x24\xff\x01\x87\xf1\x26\x1b\x02\x1c\x0d\xd8\x4f\x92\ +\x8b\x4d\x0c\x0f\x6c\x6f\xbf\x5a\x1e\xa0\x8f\x04\xdf\x02\x7d\xb9\ +\x9d\xea\x80\x6a\x62\x39\xa7\x49\x70\x01\xf0\x97\xc0\x82\xee\x19\ +\xba\x53\x20\xc1\xb7\x42\x1e\x5d\x79\x2a\xb0\xb3\x87\xaf\x92\xe0\ +\x69\xa7\x80\xab\x03\x09\xb6\x4a\x66\x72\xa0\xe4\xae\x27\x07\x31\ +\x33\xa0\xcd\xd6\xff\x2e\x43\x4e\x77\x7f\x73\x47\x20\x61\x36\x91\ +\xc1\x12\xbb\x81\xf4\x67\x62\x20\x75\x73\xa7\x0e\x17\x49\x2b\x8d\ +\x03\x99\x9f\x5e\xc6\x51\x9a\x84\x9a\x65\xfd\xf4\x65\x7e\x00\x35\ +\xf4\x06\xc5\x12\x3b\x7d\x6c\xcf\x4b\x01\x8c\x35\x8f\xd5\xbe\x34\ +\x26\x72\xb9\x29\x80\xb5\x84\xaf\xb2\xbd\xa4\x4e\x17\x3b\x30\xcd\ +\x7b\x58\xdd\xcd\x6e\x12\xda\x01\x7b\x70\xa7\xf7\xba\x7a\x93\x9d\ +\x24\x74\x7a\xd8\x99\xb9\x9e\x03\xea\x7d\x76\x8d\xc5\x36\xda\x71\ +\xc9\x03\x76\xe5\x43\xcf\x35\x36\x57\x16\x90\x16\x3a\x79\x0e\xa6\ +\x35\xfc\x5d\x22\x7b\xe0\x2f\xac\xf4\x5a\x6f\x1f\xf3\x13\x89\x9c\ +\x7c\x7a\x7a\x6e\xfe\xcf\x72\xa8\x44\xf6\xc4\x7e\x8c\xf7\x3c\x45\ +\x78\x77\x89\x9c\x6c\x76\x65\x91\xd7\x79\x7e\x3f\xd3\x88\xbf\x57\ +\x8a\x18\x4e\x95\xc7\x1a\xfc\x42\xc7\x8d\x27\x7b\xe8\xcf\xdf\x82\ +\x9f\x8d\xbc\xa6\xed\x27\x03\xa1\x15\xaf\x79\x3c\x83\xe0\x93\x84\ +\xee\xc7\x24\xd8\x99\x0f\x3c\xee\x3a\x77\xa9\x04\x0e\x90\x0b\x59\ +\xe5\x71\x2c\xa0\x9b\x04\x4e\x62\xf3\x9f\xe7\x2d\x64\xa6\x47\xfa\ +\xb4\xbc\x64\xd2\x91\x19\xde\xea\x73\x9e\x86\x03\x93\xc6\xf6\xbc\ +\xe1\x29\x58\x2a\xb8\x49\xf2\x86\xc4\x75\x94\x7b\xdb\x3f\xb0\x8d\ +\xe4\x4d\x0e\xc5\xbc\xec\x29\x50\x66\x31\x44\xf2\x86\xc8\xf1\xcc\ +\xf2\x36\x3b\xb0\x89\xe4\x4d\x06\x05\x4c\xf5\x14\x24\x4f\x69\x67\ +\x99\xd0\x69\xcb\x63\xde\x66\x07\x16\x49\xde\x24\x34\xff\x7b\xbd\ +\x84\x47\x15\x63\x24\x6e\x44\xb8\xd9\xd3\xe2\xe1\xbb\xb5\x58\x38\ +\xfe\xfc\xcd\x4b\x68\x94\x32\x5c\xd2\x46\x88\x13\x58\xe2\x69\xbf\ +\x00\xa1\xe6\x5f\xc7\x28\x71\x17\x49\x1b\x31\x76\xe2\x33\x2f\x75\ +\xfd\x3f\x92\x36\xbe\x9c\xe1\x65\xd7\xf9\xe7\x24\x6c\x44\xf1\x31\ +\x51\xb8\x9a\xd3\x25\x6c\x3c\xd9\xdf\xcb\xe2\x91\xdf\x49\xd8\x08\ +\xf3\x5b\x0f\x35\xbe\x92\xde\x12\x36\x7e\xb4\xf2\x32\x57\x4c\x1f\ +\xfd\xa2\xce\x20\x2f\xeb\x3b\xf5\xb5\x27\x66\x34\xe5\x53\xe7\x61\ +\xb0\x82\x03\x24\x6c\x0c\xd8\x87\xe5\x1e\x76\x0b\xd0\x69\x42\x31\ +\xa2\x11\x8f\x38\x0f\x81\x4f\xe9\x25\x61\x63\xc2\x5e\x7c\xe4\xbc\ +\xfe\x1f\xa5\x91\x84\x8d\x0b\x57\x38\xaf\xfe\xd7\x69\x2e\x59\x63\ +\x95\x01\x4e\x72\x1e\x03\x97\x4b\xd6\x78\x70\x9c\xf3\xaa\x1f\x2f\ +\xf7\x8f\x1d\xb9\x4c\x70\x1e\x07\xfd\x25\x6b\xf4\xd9\xcf\xf9\x29\ +\x7f\x0f\x69\xdb\xe8\x58\xd2\x98\x87\x9d\x0f\x06\xee\x2d\x59\xa3\ +\x4d\x13\xe7\x8b\x44\x6f\xd0\xc1\x11\xb1\x25\x8f\x6b\x1c\x47\xc3\ +\x3b\x5a\x22\x14\x6d\x6e\x73\x5c\xe1\x37\x93\x23\x51\x63\xcd\x8d\ +\x8e\x23\x42\x8b\xbf\x23\xcc\x25\x8e\x2b\xfb\x5a\xf5\xfe\xb1\xa7\ +\x11\x7f\x77\x1c\x15\xda\xfb\x29\xa2\x1c\xe9\xf8\xed\xff\x62\xf5\ +\xfe\x09\xe1\x62\xc7\x23\x01\xfd\x24\x69\xf4\x68\xc5\x4c\xa7\xd5\ +\x3c\x5a\xcd\x3f\x41\xb8\x3d\x03\x7a\x3a\xad\x24\x69\xd4\xb8\xd5\ +\x69\x15\xdf\xaa\x0f\x7f\x89\x22\x87\x9b\x9c\xc6\xc7\xed\x92\x34\ +\x5a\x8c\x74\x3c\xd0\xa3\xde\x3f\x79\x8c\x76\x1a\x23\xe7\x4b\xd0\ +\xe8\xb0\x3b\xeb\x9c\x7e\xf7\xd7\xd0\x5f\x12\xc9\x75\x3a\x2f\x60\ +\x1d\xbb\x4a\xd2\x68\x90\xcf\x0b\x0e\x2b\x76\x02\x4d\x25\x69\x42\ +\x69\xc2\x33\x0e\x23\xe5\x79\x1d\xff\x9a\xbc\xf4\x7f\x8a\xde\xfd\ +\x13\x9e\x05\x4c\xd2\x6b\x40\xb2\xe8\xe2\xf4\x58\x28\x2d\xfa\x4c\ +\x3a\x2d\x9c\x1e\x0f\xa7\x33\x84\x42\xa6\xd8\xe1\x71\x5f\xcb\x35\ +\xd3\x3b\x15\xec\xc5\x0a\x87\xe7\x42\x68\x9d\x48\xa8\xb8\x5c\xf8\ +\xbb\xbf\xe4\x4c\x09\xbd\x1d\x46\xcd\xef\x25\x67\x98\xe9\xff\x6a\ +\x67\x15\x79\xb2\xe4\x4c\x11\x83\x1d\x1e\x0b\xab\x1d\xa2\x43\xe3\ +\x75\x6d\xf5\x29\x42\xcf\x1d\xff\x25\x31\xc3\xe1\x4f\x0e\x37\xfc\ +\x10\xe9\x63\xa2\x5e\x03\xe2\x4c\x37\x67\xe9\xff\x5c\x89\x99\x52\ +\x3e\x73\xb6\x3c\xa8\xbb\xc4\x0c\x96\x3c\xee\x77\x54\x79\x4b\xe9\ +\x2c\x39\x53\xca\x4e\xce\x0e\x14\xbb\x5f\xb3\x47\x83\xe5\x40\x67\ +\x47\x7c\x9e\x22\x31\x53\xcc\x70\xaa\x1c\x45\xd2\xc1\x12\x33\x48\ +\x5c\x39\xb7\x4e\xf8\x4d\x3b\xae\x56\x91\x2e\x92\x94\xc1\x71\xad\ +\xa3\x4a\x7b\x5c\x52\x0a\xc6\xe9\x24\xe1\x78\xe1\x6a\x26\xd7\x87\ +\xb4\x91\x98\x82\x76\x7c\xec\x68\x26\xe9\x1e\x12\xd3\x3f\xb9\xdc\ +\xed\xa4\xba\xca\x19\x2c\x31\x05\x00\x43\x28\x77\x12\x53\xf7\x68\ +\x7d\xa0\x7f\xf6\xa6\xd6\x49\x65\xdd\x28\x29\xc5\xbf\x19\xe3\x24\ +\xa6\x6a\xd8\x47\x52\xfa\xc6\xcd\xce\x7f\xd3\x25\xa4\xf8\x1e\x6e\ +\xce\x93\x78\x57\x42\xfa\xe5\x4c\x47\xf3\xb7\x77\x94\x94\xe2\x7b\ +\xec\xc0\x1a\x27\xb1\x75\xaa\xa4\xf4\x47\x4b\x16\x3a\xa8\xa2\x5a\ +\x46\x49\x4a\xf1\x23\x46\x39\x79\xb9\x5c\xa0\x63\x64\xfd\xf1\x2b\ +\x36\x6a\xf1\x86\xf0\xc6\x14\x07\xd1\xb5\x91\xcb\x24\xa4\x1f\x5a\ +\x3b\xd9\xcb\xa5\x86\xd6\x92\x52\x6c\x21\xc3\xac\x71\x10\x61\x1f\ +\x2b\xc2\x7c\xa5\x68\x2e\xde\xd1\x4e\x93\x90\xc2\xf3\x18\xd3\x05\ +\x12\xd2\x3d\xf9\x4e\xdc\xf9\x59\x8a\x24\xa5\xd8\x22\x45\x4c\x70\ +\xb2\xc2\x44\xf3\x01\x9c\xe3\x62\xce\xf6\x2a\x0e\x94\x90\x62\xab\ +\x1c\xec\x64\x99\xb9\xce\x10\x76\x4c\x67\x56\x39\xa8\x96\xbf\x49\ +\x48\xb1\x4d\x5c\x9c\x26\xbc\x92\x9d\x24\xa4\x4b\xfe\xec\xa0\x52\ +\xde\x93\x8c\xa2\x5e\x7c\xe8\x20\xda\xfe\x28\x19\xdd\xd1\x86\x79\ +\x0e\xaa\x44\x47\x39\x89\xfa\xb1\xbb\x83\x68\x9b\xc3\xf6\x12\xd2\ +\xb9\x43\x7b\xf7\x00\x00\x05\x7a\x49\x44\x41\x54\x15\x17\x3a\xa8\ +\x90\xbb\xb4\x67\x8b\xa8\x27\x79\xdc\xa3\x73\x83\xa2\x84\x7d\xad\ +\xd6\x6a\x76\x93\x8c\xa2\xde\xec\xe9\x60\x62\xf0\x06\xc9\xe8\x86\ +\xcb\x1d\xb8\xf1\xcd\x92\x51\x34\x88\x9b\x1c\x44\xdd\x2f\x25\xa3\ +\x9d\x62\x07\xf3\xff\xbf\x50\xfa\x2f\x1a\x48\x23\x96\x3a\x58\x17\ +\xd0\x58\x42\x5a\x39\x8b\x6a\x73\x45\x1c\x25\x19\x45\x83\x39\xd2\ +\x1c\x77\xd5\x9c\x2d\x19\x6d\x34\xe6\x39\xcd\xfe\x13\xb1\x8d\xbd\ +\x67\x14\x7b\x36\x7a\x98\xd7\xff\x55\x30\x48\x32\x8a\xac\x38\x81\ +\x4a\xf3\xd2\x73\x1d\x19\x62\xe2\x0e\xb3\x07\xcf\x90\x88\x22\x6b\ +\xec\xfb\x4f\xdd\x2a\x11\xb3\xa7\xd8\xc1\x48\xac\x66\xff\x8b\xec\ +\x39\xc4\x41\x04\x36\x91\x8c\xd9\x72\xbd\x59\xfc\xa7\x25\xa2\x30\ +\x61\x5f\x1d\x78\x9d\x44\xcc\x8e\xe6\x7c\x61\x7e\x03\xeb\x26\x19\ +\x85\x89\x6e\xe6\x65\xe8\x8b\x69\x2a\x19\xb7\x44\xa3\xad\xfc\x7f\ +\x03\xcd\xb3\xa9\x1f\x60\x81\x24\x16\x26\x16\xf2\x4f\x63\x09\x6d\ +\x18\x28\x19\x1b\x4e\x3e\x0f\x1a\x9d\x77\x3d\x7d\x24\xa3\x30\x73\ +\x18\x1b\x8c\x91\xf8\x20\xf9\x92\xb1\xa1\xb4\x65\xad\x51\xf6\xa9\ +\x12\x51\x38\x61\x9a\x79\x25\x8a\x0e\xa1\x6b\x30\x3f\x37\x0f\xbe\ +\xa8\xff\x17\x6e\xe8\x67\x8e\xc5\xff\x90\x88\x0d\xe5\x73\xa3\xe4\ +\x6f\x4b\x42\xe1\x8c\x19\xe6\xdd\x01\x44\x83\x38\xd0\xec\xb9\x87\ +\x4a\x44\xe1\x0c\xfb\x7c\x80\xfd\x24\x62\x43\x78\xc2\x28\xf7\x34\ +\x9a\x49\x44\xe1\x8c\x66\x4c\x37\x46\xe4\x23\x12\xb1\xfe\xb4\xe5\ +\x6b\xe3\xf7\x7f\xad\xc4\x16\x6e\xf9\x4f\xe3\xc1\x61\x5f\xe9\xb8\ +\x90\xfa\x73\x9a\x71\x11\xc6\x72\xda\x4b\x44\xe1\x94\x0e\x2c\x37\ +\xc5\x64\x25\xc3\x24\xe2\x8f\xa9\x6b\x22\x50\x2e\x87\x53\x60\x2a\ +\x75\x22\xa5\x92\x56\x38\x65\x29\xcf\x9b\xee\x2f\xe0\x48\x6d\x4b\ +\x53\x3f\xb6\x63\xbe\xf1\x7d\xab\x8b\x44\x14\xce\xe9\x66\xfe\x12\ +\xd0\x52\x22\xd6\x07\xeb\x88\xeb\x64\x49\x28\xbc\x60\x3d\x3d\xf8\ +\x00\x49\x58\x1f\xee\x35\xca\x7c\xb4\x24\x14\x5e\x18\x60\x8c\xcc\ +\xb1\x92\xb0\x3e\x54\x1a\x13\xad\x16\x92\x50\x78\xa1\x85\x71\x7a\ +\x5a\xb9\x24\xfc\x21\x3f\x1e\x04\x1c\x64\x1c\x00\x9c\xc0\x1a\xc9\ +\x2a\xbc\xb0\x86\x09\xa6\xfb\x8b\x38\x56\x22\x6e\xcb\x00\xce\x33\ +\x95\xb7\x9e\x67\xc9\x48\x56\xe1\x85\x0c\x13\x59\x6f\x2a\xe1\x3c\ +\x89\xb8\x75\x9a\xb2\x4c\x73\xae\x45\x84\x99\x63\x8a\xcf\x32\x6d\ +\x10\xb6\xf5\x0c\xe0\x40\x8a\x4d\xe5\x3d\x24\x49\x85\x57\x1e\x33\ +\x76\x70\xda\xa3\x72\xab\x06\x70\x80\xd1\x00\xb4\x07\xab\xf0\xcb\ +\x2d\xa6\xbb\x8b\xf5\x29\x70\x6b\x06\xd0\x98\x83\x4c\xa5\xbd\xc5\ +\x32\x49\x2a\xbc\xb2\xc4\xb8\xd1\xfc\x01\x3a\x2a\x64\xcb\xb4\xe1\ +\x2b\xd3\x1b\x96\x66\x5b\x0b\xff\x9c\x6e\x1c\x05\xd0\xee\x40\x5b\ +\x64\x7f\x93\xb4\x6b\x25\xad\x08\x80\xb6\xac\x37\xc5\xe9\xbe\x92\ +\x70\x4b\xaf\x00\xb6\x1e\xfc\x15\xd6\x4a\x50\xe1\x9d\xb5\xbc\x62\ +\xba\xff\x24\x49\xb8\x25\x03\x18\x6a\x28\x29\xc3\x24\x2a\x24\xa8\ +\xf0\x4e\x39\x93\x4c\x73\x4d\xf4\xa2\xba\x05\x5a\x9b\x12\xab\x55\ +\xda\x74\x49\x04\xc4\x01\xac\x36\xc5\xaa\x56\x05\xd6\x99\x01\x9c\ +\x6c\x2a\x69\x0d\x33\x25\xa7\x08\x84\xb7\x59\xad\x97\x00\xf7\x06\ +\x70\x9c\xa9\xa4\x97\x34\x05\x58\x04\xc4\x46\xe3\x92\x73\x1d\x58\ +\x5f\x27\x4b\x4c\x69\xd5\x11\x12\x50\x04\xc6\xb1\xa6\x58\x5d\x22\ +\x01\x7f\x4c\x2f\x56\x1a\x24\xad\x96\x80\x22\xe0\x2c\x20\xfb\x68\ +\x5d\xc1\xee\x12\xf0\x87\xaf\x00\x7b\x98\x26\x01\x3f\x25\x29\x45\ +\xa0\x8c\x37\xdc\x5b\xcc\x1e\x12\xf0\x87\x06\xd0\xd3\x74\x80\xa2\ +\x16\x01\x89\x60\xb1\x44\x5c\x01\xbb\x4a\xc0\xef\x1b\x40\x11\x3d\ +\x0c\xa5\x54\xf0\x96\xa4\x14\x81\x32\x95\x2a\xc3\xdd\x3d\x28\x94\ +\x84\x9b\x1b\x40\x63\x93\x01\xbc\xcf\x06\x49\x29\x02\x65\x03\x1f\ +\x98\x0c\x40\xfb\x02\x7c\xcf\x00\x8a\x4d\x06\x30\x4b\x06\x20\x02\ +\x66\x3d\xb3\x64\x00\xee\x0c\xa0\x5b\x9d\x47\x84\xd4\x8f\x0c\xb3\ +\xa8\x91\x94\x22\x50\x6a\x4c\x06\x90\x47\x57\x49\xb8\xb9\x01\xf4\ +\x35\x25\x63\xb3\x24\xa4\x08\x9c\x0f\x4d\x79\x67\x1f\x09\xb8\xb9\ +\x01\x1c\x6c\x32\x80\x0f\x25\xa4\x08\xc1\x00\x2c\x1b\x84\x1e\x2c\ +\x01\xbf\xff\x46\x95\xfd\xb4\x0a\x35\x7f\x11\x0e\x1f\x19\xa2\x56\ +\x9b\xd7\x6f\x96\x01\x6c\x6f\x1a\x12\x79\x45\x32\x8a\x50\x78\xd5\ +\x70\x6f\x33\xad\x09\xfc\xce\x00\xf6\x37\x95\xf1\x7f\x92\x51\x84\ +\xc2\x0b\xa6\xbb\x7b\x4b\xc0\x4d\x06\x60\x5b\xc9\xff\xba\x64\x14\ +\xa1\x60\x8b\x3c\x6d\x0d\xe6\xc4\x00\xd6\x68\x23\x30\x11\x12\xab\ +\x4d\xc3\x80\x32\x80\x7f\x1b\x80\x65\x6d\xd4\x9b\x12\x51\x84\xc6\ +\x34\xc3\xbd\xbd\x24\xdf\x37\x06\xd0\xc2\xb4\x0e\x70\x9a\x44\x14\ +\xa1\x61\xe9\x7e\x9a\xd1\x4c\x02\x36\x02\x3a\x99\xce\x03\x9e\x2a\ +\x11\x45\x68\xbc\x61\xb8\x37\x9f\x8e\x12\xb0\x11\xd0\xde\x64\x00\ +\x1f\x4b\x44\x11\x1a\x96\x39\x28\x05\x74\x90\x80\x8d\x80\x8e\x06\ +\x03\x58\x44\xa5\x44\x14\xa1\x51\x69\xd8\xde\xab\x90\xf6\x12\xf0\ +\x9b\x0c\x20\xfb\xad\x40\x16\xca\x00\x44\x88\x54\xb0\x40\xaf\x00\ +\x36\x03\xc8\x31\xf9\x60\xa9\x69\x5b\x06\x21\x6c\x54\x51\x6a\xb8\ +\xbb\x44\x02\x36\x22\xdf\x74\xa2\x5f\x99\x0c\x40\x84\x6a\x00\x65\ +\x86\xbb\xdb\x98\xb6\xc1\x93\x01\x00\xa5\x3a\x0d\x40\x84\x48\xc6\ +\x94\x01\xb4\x95\x01\xd8\x0c\xa0\xc6\xe4\xbf\x42\xd8\x59\x66\xd8\ +\x8c\x46\x19\x80\xd1\x00\xaa\x64\x00\x22\x64\x2c\xa3\x50\x32\x00\ +\x1a\x91\x47\x2b\x19\x80\x88\x2d\x65\x86\x43\x69\x5a\xc9\x00\x1a\ +\x91\x47\x91\xc1\x00\x4a\x15\x81\x22\xb6\x19\x40\x13\xf2\x64\x00\ +\x96\x6d\x11\xaa\x59\xa6\x08\x14\x21\x8f\x01\x58\x8e\xa5\x6b\x26\ +\x03\xe8\x68\x32\x00\x21\xc2\x65\xa3\x29\x0a\xdb\xcb\x00\xda\x19\ +\xee\xfe\x4a\xf1\x27\x42\xe7\x6b\x19\x80\xc5\x00\x2c\xb3\x00\x96\ +\x2a\xfa\x44\x04\x46\x01\xb2\x67\x7b\x19\x40\x2b\xc3\xdd\xfa\x06\ +\x20\xc2\xc7\x12\x85\xad\x64\x00\x32\x00\x91\xde\x0c\x40\x06\x20\ +\x03\x10\x31\xc7\xf2\x25\xaa\xb5\x0c\xa0\xa5\x0c\x40\x28\x03\x48\ +\xaf\x01\x14\xcb\x00\x44\x6a\xc7\x00\x8a\x65\x00\x4d\x0d\x77\xaf\ +\x53\xf4\x89\xd0\x59\x2f\x03\x08\x2b\x03\xd0\x6e\x40\x22\x7c\x2a\ +\x64\x00\x61\x19\x40\xb9\xa2\x4f\xc4\xda\x00\x9a\xc8\x00\x1a\x87\ +\x24\xbd\x10\x6e\x28\x97\x01\x58\x0c\x20\x4f\x06\x20\x52\x9b\x01\ +\xe4\xca\x00\x2c\x06\xa0\x31\x00\x11\x6f\x03\xd0\x72\x60\x93\x07\ +\x6a\x35\xa0\x08\x9f\x2a\x19\x40\x58\x06\x20\x44\xbc\x49\xbd\x01\ +\xfc\x3f\xea\xdd\xb0\x4f\x65\x52\x37\x8b\x00\x00\x00\x00\x49\x45\ +\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x05\x1d\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x04\x06\x03\x37\x2c\x31\x73\ +\x78\xa6\x00\x00\x03\xeb\x49\x44\x41\x54\x78\xda\xed\x9d\x3b\x68\ +\x14\x51\x14\x86\xbf\x6c\x7c\x26\xea\x0a\xc6\xc2\x34\x16\x41\x54\ +\x34\x26\x36\x82\x88\x8f\x58\xd9\x88\x2f\x10\x1b\x0b\x0b\xb1\xb0\ +\x11\x45\x0b\x1b\x5f\xa5\x20\xa2\x88\xf8\xc2\x42\x44\x10\x04\x5f\ +\xa0\x20\x0a\x6a\x61\x23\x82\xa0\x82\x4f\x10\x51\x82\x8a\x92\x64\ +\x31\x2a\x31\x6b\xb1\x2c\x51\xf7\x4c\xb2\x21\x33\xf7\xbf\xac\xe7\ +\x3b\xcd\x32\xbb\xe1\xfe\xff\x3f\x73\x67\xf6\x0e\x9b\x33\xe0\x38\ +\x8e\xe3\x38\xce\xff\x4a\x5d\xe2\x3b\x0d\xb4\x90\x67\x5c\x8a\x63\ +\x75\xf2\x44\x6d\xb7\x3a\xda\xd8\xc3\x23\xfa\x29\xa6\x5c\xdd\x4c\ +\x57\x5b\x1b\x9a\x39\x5c\x4c\xdd\xf8\x40\xdd\x1e\xe4\x88\x8b\x80\ +\x31\x1c\xe3\x57\x86\xf6\x8b\x14\xd9\xa2\x36\x99\xcc\x14\xee\x64\ +\x6c\xbe\x48\x91\x02\x2d\x6a\xa3\x36\x93\x79\x16\xc0\x7e\x91\x22\ +\xb7\x62\x9c\x06\xf5\x5c\x0f\x64\x3f\xd2\x69\xb0\x37\xa0\xfd\x08\ +\xaf\x06\xcd\x14\x82\x06\x10\xdd\xd5\xe0\x64\x60\xfb\x51\x4d\x83\ +\x3a\x1a\xf9\xc4\x78\xf3\xbd\x6f\x3c\xa1\x7b\xc4\x23\xb4\xd3\x64\ +\x6c\xed\xa1\x95\xb7\x6a\xf3\x25\xd6\x9a\x7b\xa8\x97\x6d\x09\xb1\ +\x0c\x97\x15\x09\xdf\x29\xa3\xb9\x1a\x9c\x35\xc4\xfd\x60\x59\x8a\ +\x23\x9c\x4e\x98\x06\x3b\xd5\xd6\x4b\x3c\x36\xa4\x1d\x4a\x75\x84\ +\x3c\xef\xcc\x00\xfa\x39\xc1\x5c\x46\xab\x03\xf8\x6a\x48\x6b\x4d\ +\x79\x8c\x15\x19\x2c\xad\xaa\xaf\x02\xcf\xb9\xc7\x41\x96\x50\x6f\ +\x89\xeb\x33\xfe\x64\x6c\xea\x31\x9f\x11\x06\x30\x50\x1f\xd9\x55\ +\x79\x66\xb3\x3e\x98\x3e\x49\xd3\x20\x7c\xbd\x63\x9d\x22\x00\xf5\ +\x34\xf8\xb3\xfa\x39\x40\x2e\x7c\x00\x70\x48\x6e\x7d\xa0\x2e\x94\ +\x23\xc8\x8d\xcc\xd3\xb0\xe8\x09\x38\xd6\x50\x6c\x60\x5f\xf9\x65\ +\xa8\x23\x60\x69\xe6\x37\x5b\x86\x3b\x11\xd6\x85\x0c\x60\x02\xaf\ +\xe4\x96\xff\xad\xf7\x34\x84\x9b\x02\x47\x22\xbc\x13\xd4\xcc\x56\ +\x08\x73\x04\xac\x94\xef\x6d\xbb\x3e\x52\x1f\x22\x80\xa9\x74\x26\ +\x08\x38\x4f\x1b\xa3\x32\xdf\xd3\x8d\x74\x70\x35\x41\xc1\x92\x10\ +\x01\x5c\x4a\x18\xfc\x40\xe6\xd6\xff\x64\xb7\xa9\xe1\x60\xf6\x01\ +\x6c\x4c\xb0\x7f\x2f\xe8\x25\x18\xe0\x9a\xa1\xe2\x6e\x9d\x69\x77\ +\x22\x4b\x59\x44\x33\x4d\x29\xac\x0a\x16\x30\xc9\xd8\xda\x43\x3b\ +\x6f\x02\x07\xb0\x9c\xdb\x15\xdb\x5e\xd8\x47\xc0\xcf\xcc\x4f\x3f\ +\x9b\x03\x9b\x07\x98\x68\xe8\xe8\x26\x73\xab\x56\xdd\x14\xdd\x0d\ +\x32\xb4\xd8\x53\x20\x5b\x3e\xd3\x4a\xa7\x28\x80\x0a\x42\x9f\x88\ +\x00\xb6\x8a\xec\x27\x10\xfa\xf0\x3f\x17\x97\xd7\xd0\x53\xe0\x3d\ +\xf3\xf8\x22\x0c\xa0\x82\xb0\x53\xa0\xc8\x66\xa1\xfd\x44\x51\xe1\ +\xea\x68\x7c\x5e\x43\x4e\x81\xd7\xb4\x53\x10\x07\x50\x41\xf2\x52\ +\xe4\x03\xa7\x78\xc8\xf7\x14\x87\x7f\x29\xb6\x9f\x80\x7d\xb0\x5e\ +\xa1\x51\x2d\x2c\x90\x57\x73\xe3\xe3\x54\x7f\x1e\x17\x0f\x55\x07\ +\xb0\x5e\xad\x54\x1d\x40\xd3\xc8\xc7\x8a\x92\xaa\xaf\x02\x39\xc1\ +\x0a\x21\x4c\x00\x86\xd5\x2a\x3f\x58\xab\x28\x16\x43\x51\xe1\x01\ +\xa8\x05\xa8\xf1\x00\xd4\x02\xd4\x78\x00\x6a\x01\x6a\x3c\x00\xb5\ +\x00\x35\x1e\x80\x5a\x80\x1a\x0f\x40\x2d\x40\x8d\x07\xa0\x16\xa0\ +\xc6\x03\x50\x0b\x50\xe3\x01\xa8\x05\xa8\xf1\x00\xd4\x02\xd4\x78\ +\x00\x6a\x01\x6a\x3c\x00\xb5\x00\x35\x1e\x80\x5a\x80\x1a\x0f\x40\ +\x2d\x40\x8d\x07\xa0\x16\xa0\xc6\x03\x50\x0b\x50\xe3\x01\xa8\x05\ +\xa8\xf1\x00\xd4\x02\xd4\x78\x00\x6a\x01\x6a\x3c\x00\xb5\x00\x35\ +\x1e\x80\x5a\x80\x1a\x0f\x40\x2d\x40\x8d\x07\xa0\x16\xa0\xc6\x03\ +\x50\x0b\x50\xe3\x01\xa8\x05\xa8\xf1\x00\xf8\x65\x6c\x4d\xbf\xa1\ +\x62\x0c\x58\xae\xfa\x72\x66\x8f\xb7\x99\x6a\xad\x99\x30\xcb\xd8\ +\xd6\x9d\x33\xbb\xfb\x6e\x52\x6b\xcd\x04\xcb\xd5\x5b\xbb\xad\xee\ +\x4f\x3a\xd4\x6a\x53\x67\xb9\xd9\x1c\xe8\x0c\xac\x31\xff\x79\xb6\ +\x97\xed\x29\x35\x56\x8e\x81\x06\x76\xd0\x6b\xfa\x5c\x35\x78\x6b\ +\xed\xa7\x74\xa9\xb5\xa7\x40\x9e\x39\x34\x24\x38\x9c\x0a\x70\x22\ +\x78\x27\x99\x58\xea\x78\xe9\x31\x3b\xd3\x78\x59\x93\xed\x12\x86\ +\xa2\xc0\x0c\x3a\xeb\x81\x02\xa3\x6a\xf0\xa4\x37\x34\xfb\xb9\x51\ +\x7e\x99\x33\x5b\xcd\xd5\x76\xdd\x2c\x35\xda\x2e\x37\x35\xcb\xf3\ +\x80\xd9\xea\x5d\x12\x90\x67\x2c\x2c\x3d\x3b\xa1\xbc\x16\xe8\x62\ +\x31\x77\xd4\xaa\x82\x71\x9f\x0e\xeb\xd1\x11\xa3\x39\x12\x59\xef\ +\xdf\x2c\xaa\x8f\xc3\x83\xf5\x31\x9d\x9d\xe9\xa3\xb6\xf4\x75\x8b\ +\xb6\xbf\x0d\x5b\x8d\x0d\x5b\x59\xcd\x2a\xe6\xd7\xd4\x52\xb9\x9f\ +\x47\x5c\xe1\x72\xe5\xf3\xee\x92\x3b\x3b\x8e\xa7\x85\x7c\x4d\x7c\ +\x1d\xee\xa5\x8b\x57\xa9\xf6\xc4\x72\x1c\xc7\x71\x1c\xa7\x26\xf8\ +\x0d\xa1\x9b\xad\xee\x73\xd9\x4b\x70\x00\x00\x00\x25\x74\x45\x58\ +\x74\x64\x61\x74\x65\x3a\x63\x72\x65\x61\x74\x65\x00\x32\x30\x32\ +\x30\x2d\x30\x34\x2d\x30\x36\x54\x30\x33\x3a\x35\x35\x3a\x34\x34\ +\x2b\x30\x30\x3a\x30\x30\xec\xd4\x23\xd5\x00\x00\x00\x25\x74\x45\ +\x58\x74\x64\x61\x74\x65\x3a\x6d\x6f\x64\x69\x66\x79\x00\x32\x30\ +\x32\x30\x2d\x30\x34\x2d\x30\x36\x54\x30\x33\x3a\x35\x35\x3a\x34\ +\x34\x2b\x30\x30\x3a\x30\x30\x9d\x89\x9b\x69\x00\x00\x00\x19\x74\ +\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\ +\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\ +\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x27\xb3\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x02\x00\x00\x00\x02\x00\x08\x04\x00\x00\x00\x5e\x71\x1c\x71\ +\x00\x00\x02\xe7\x7a\x54\x58\x74\x52\x61\x77\x20\x70\x72\x6f\x66\ +\x69\x6c\x65\x20\x74\x79\x70\x65\x20\x65\x78\x69\x66\x00\x00\x78\ +\xda\xed\x97\x5b\x92\x1b\x21\x0c\x45\xff\x59\x45\x96\x80\x24\x84\ +\xc4\x72\x68\x1e\x55\xd9\x41\x96\x9f\x0b\xdd\xf6\x78\x1e\x49\x66\ +\x2a\xf9\x4a\xb9\xa9\x86\xb6\x00\x49\x7d\x0f\x60\x3b\x8c\x1f\xdf\ +\x67\xf8\x86\x8b\x4a\xe6\x90\xd4\x3c\x97\x9c\x23\xae\x54\x52\xe1\ +\x8a\x07\x8f\xe7\x55\x76\x4d\x31\xed\xfa\x76\xf1\x65\x7d\x65\x0f\ +\xf7\x0e\x86\x49\xd0\xca\xd9\x61\xf5\x6c\xa9\xc2\xae\x2f\x13\x6e\ +\x31\xe8\x78\x6d\x0f\x7e\xf5\xb0\x5f\x8e\x6e\x91\x2f\x87\xb2\x22\ +\xaf\x50\xfd\x31\x49\xd8\xf9\xb4\x53\xba\x1c\x95\x71\x3e\xe4\xe2\ +\xf6\x98\xea\x71\xe6\x19\xdb\x35\x70\xa7\x72\xdd\xc7\xe5\x74\x67\ +\x14\xcf\xcf\xe1\xd1\x90\x0c\x2a\x75\x45\x20\x61\x1e\x42\x12\x77\ +\xed\x67\x06\x72\xde\x15\xb7\xa0\x66\x31\x8c\x43\xc6\x78\x16\xc9\ +\x61\x9b\xfc\xca\x04\x82\xbc\x7a\xbd\x17\x81\x1f\x05\x7a\x2b\x3e\ +\x9d\x62\xd3\xc7\x1d\x6f\xc5\xe7\x7a\xd9\xe5\x8d\x96\xf9\x46\x2d\ +\x7f\xdc\x41\xfa\xb1\xf8\x5b\xe2\x87\xc0\x72\xcf\x88\x5f\x77\x98\ +\xdc\x5c\xbd\x17\x79\xce\xee\x73\x8e\xf3\xed\x6a\xca\x50\x34\x5f\ +\x2b\x6a\x8b\x4d\x37\x37\x18\x78\x40\x72\xd9\xd3\x32\x8a\xe1\x56\ +\x3c\xdb\x2e\x05\xc5\x63\x8d\x0d\xc8\x7b\x6c\xf1\x40\x69\x54\x88\ +\x41\x65\x06\x4a\xd4\xa9\xd2\xa4\xb1\xdb\x46\x0d\x29\x26\x1e\x0c\ +\x26\xcc\xdc\x58\xb6\xcd\xc1\xa8\x70\xdb\x94\xd2\x2a\x34\xd9\xa4\ +\x48\x17\x07\xac\xc6\x23\x00\x5d\x12\xbe\xe7\x42\x3b\x6e\xd9\xf1\ +\x1a\x39\x22\x77\xc2\x50\x26\x38\x23\x4c\xf9\x65\x09\xbf\xeb\xfc\ +\x4a\x09\x73\xb6\x25\x11\x45\xbf\x6b\x85\xbc\x78\xad\x6b\xa4\xb1\ +\xc8\xad\x1a\xa3\x00\x84\xe6\xc5\x4d\xb7\xc0\xb7\x72\xdf\xb4\xf1\ +\x01\xac\x80\xa0\x6e\x99\x1d\x2f\x58\xe3\x71\xba\x38\x94\x5e\xd6\ +\x96\x6c\xce\x82\x71\x8a\xf6\xdc\x42\x14\xac\x5f\x0e\x20\x11\x62\ +\x2b\x92\x21\x01\x81\x98\x49\x94\x32\x45\x63\x36\x22\xe8\xe8\x00\ +\x54\x91\x39\x4b\xe2\x03\x04\x48\x95\x3b\x92\xe4\x84\x1d\xc2\xc1\ +\xd8\x79\xc5\xc6\x1c\xa3\x3d\x96\x95\x33\x2f\x33\xce\x26\x80\x50\ +\xc9\x62\x60\x53\xa4\x02\x56\x4a\x8a\xf5\x63\xc9\xb1\x86\xaa\x8a\ +\x26\x55\xcd\x6a\xea\x41\x8b\xd6\x2c\x39\x65\xcd\x39\x5b\x5e\x87\ +\x5c\x35\xb1\x64\x6a\xd9\xcc\xdc\x8a\x55\x17\x4f\xae\x9e\xdd\xdc\ +\xbd\x78\x2d\x5c\x04\x67\xa0\x96\x5c\xac\x78\x29\xa5\x56\x0e\x15\ +\x81\x2a\x7c\x55\x8c\xaf\xb0\x1c\x7c\xc8\x91\x0e\x3d\xf2\x61\x87\ +\x1f\xe5\xa8\x0d\xcb\xa7\xa5\xa6\x2d\x37\x6b\xde\x4a\xab\x9d\xbb\ +\x74\x1c\x13\x3d\x77\xeb\xde\x4b\xaf\x83\xc2\xc0\x49\x31\xd2\xd0\ +\x91\x87\x0d\x1f\x65\xd4\x89\xb5\x36\x65\xa6\xa9\x33\x4f\x9b\x3e\ +\xcb\xac\x77\x6a\x17\xd5\x77\xe5\x0b\xd4\xe8\xa2\xc6\x9b\xd4\x1a\ +\x67\x77\x6a\xb0\x06\xb3\x9b\x0b\x5a\xc7\x89\x2e\x66\x20\xc6\x89\ +\x40\xdc\x16\x01\x2c\x68\x5e\xcc\xa2\x53\x4a\xbc\xc8\x2d\x66\xb1\ +\x30\x36\x85\x32\x92\xd4\xc5\x26\x74\x5a\xc4\x80\x30\x0d\x62\x9d\ +\x74\x67\xf7\x42\xee\x53\xdc\x82\xfa\xa7\xb8\xf1\x9f\xc8\x85\x85\ +\xee\x5f\x90\x0b\x40\xf7\x9e\xdb\x07\xd4\xfa\xfa\x9e\x6b\x9b\xd8\ +\xb9\x0b\x97\xa6\x51\xb0\xfb\xd0\x3f\xbc\x06\xf6\xba\xbe\xd4\xea\ +\xdf\xb6\x4f\x47\x4f\x47\x4f\x47\x4f\x47\x4f\x47\x4f\x47\x4f\x47\ +\xff\x81\xa3\x89\x1f\x0f\xf8\x13\x1b\x7e\x02\xb3\x1f\x9e\x33\x29\ +\xa8\x51\x96\x00\x00\x01\x23\x69\x43\x43\x50\x49\x43\x43\x20\x70\ +\x72\x6f\x66\x69\x6c\x65\x00\x00\x28\x91\x9d\x90\xbf\x4a\xc3\x50\ +\x14\xc6\x7f\x69\x8b\x16\xd1\x49\x71\xf0\x0f\x64\x70\x2d\xb8\xd8\ +\xc9\xa5\x2a\x04\x41\x21\xc6\x0a\x46\xa7\x34\x49\xb1\x98\xc4\x90\ +\xa4\x14\xdf\xc0\x37\xd1\x87\xe9\x20\x08\x3e\x81\x4f\xa0\xe0\xec\ +\x77\xa3\x83\x83\x59\xbc\x70\xf8\x7e\x1c\xce\xf9\xbe\x7b\x2f\xb4\ +\xec\x24\x4c\xcb\xce\x2e\xa4\x59\x55\x38\xde\xc0\xbf\xf4\xaf\xec\ +\xc5\x37\xda\x6c\xd2\x61\x9b\x6e\x10\x96\xf9\xc0\x75\x4f\x68\x3c\ +\x9f\xaf\x58\x46\x5f\x7a\xc6\xab\x79\xee\xcf\xb3\x10\xc5\x65\x28\ +\x9d\xab\xb2\x30\x2f\x2a\xb0\xf6\xc5\xfd\x59\x95\x1b\x56\xb1\x76\ +\x3b\xf4\x0e\xc5\x0f\x62\x3b\x4a\xb3\x48\xfc\x24\xde\x89\xd2\xc8\ +\xb0\xd9\xf5\xd2\x64\x1a\xfe\x78\x9a\xdb\x2c\xc7\xd9\xc5\xb9\xe9\ +\xab\xb6\x70\x38\xe6\x14\x17\x9b\x11\x53\x26\x24\x54\xf4\xa4\x99\ +\x3a\x47\xf4\xd9\x93\x3a\x14\x04\xdc\x53\x12\x4a\x13\x62\xf5\x66\ +\x9a\xa9\xb8\x11\x95\x72\x72\x38\x10\x0d\x45\xba\x4d\x43\xde\x46\ +\x9d\xe7\x2a\x65\x24\x8f\x89\xbc\x4c\xc2\x1d\xa9\x3c\x4d\x1e\xe6\ +\x7f\xbf\xd7\x3e\xce\xea\x4d\x6b\x7d\x9e\x07\x45\x50\xb7\xda\xaa\ +\xd6\x78\x0c\xef\x8f\xb0\xe2\xc3\xea\x33\x2c\x5d\x37\x64\x75\x7f\ +\xbf\xad\x61\xa6\x5f\xcf\xfc\xf3\x8d\x5f\xb4\xf4\x50\x4b\xa7\xc8\ +\x6e\xd4\x00\x00\x00\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\ +\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95\x2b\x0e\x1b\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\ +\x1b\x04\x24\x1e\x8d\x57\x2d\x63\x00\x00\x20\x00\x49\x44\x41\x54\ +\x78\xda\xed\xdd\x79\x60\x95\xd5\x9d\xff\xf1\x4f\xf6\x40\x08\xab\ +\x84\x24\xa0\x55\x16\x09\x8a\xa2\x88\xb8\x20\x2a\xe2\x0a\x2a\x43\ +\x5d\x68\x5d\x5b\xd7\x2a\xd6\xda\xce\xe8\x8c\xd5\xa9\x33\xfe\x66\ +\xaa\xe3\x82\x3a\xda\xd1\x8a\x5a\x6a\xad\x53\x6b\xeb\x82\x62\x1d\ +\xf7\x8a\xb8\xe0\x8e\xa8\xa8\xec\x08\x49\x40\x96\x00\x21\x7b\xee\ +\xef\x0f\xa1\x8d\x18\xc2\xcd\xfd\x9e\x67\x7f\xbf\x9e\xff\x5a\xee\ +\x35\xcf\xe7\x9c\xe7\x7b\x9f\xe5\x3c\xe7\x48\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x22\x28\x8b\x08\x90\ +\x78\x3d\xb5\xbf\x8e\xd6\x01\xaa\x50\x6f\x75\x55\xad\xd6\x6b\x81\ +\xe6\xe9\x45\xbd\xa5\x0d\x84\x03\xc4\xd9\x20\xcd\x50\xb5\x52\xed\ +\x6e\xd5\x9a\xa1\x0a\x22\x02\xe2\xa9\x9f\xa6\xef\xe0\xd0\x6f\xbb\ +\xdd\xaf\x52\xa2\x02\xe2\xe6\x64\xad\x49\xe3\xf0\x4f\x29\xa5\x35\ +\x9a\x4c\x5c\x40\x7c\x64\xe9\xda\x34\x0f\xfe\x6d\xdb\x35\xdc\x2f\ +\x03\xe2\xe2\xd6\x4e\x1e\xfe\x29\xa5\x34\x8d\x12\x00\xc4\xc1\x55\ +\x19\x1c\xfe\x29\xa5\x74\x15\xd1\x01\x51\x77\x9c\x9a\x33\x2c\x00\ +\xcd\x3a\x96\xf8\x80\x28\xeb\xa6\x65\x19\x1e\xfe\x29\xa5\xb4\x4c\ +\xdd\x88\x10\x88\xae\xff\x32\x1c\xfe\x29\xa5\x74\x23\x11\x02\x51\ +\x55\xa2\x7a\x63\x01\xa8\x57\x49\x9c\x02\xc9\xa6\x4f\x20\x41\x2e\ +\x56\x81\xf1\x1b\x0a\x74\x11\x31\x02\x51\x94\xab\x79\xc6\xdf\xff\ +\x94\x52\x9a\xa7\x5c\xa2\x04\xa2\x67\x57\x07\x87\x7f\x4a\x29\xed\ +\xca\x25\x00\x10\x3d\xe3\x1d\x7d\xcf\x51\x14\x00\x20\x7a\x46\x86\ +\xec\x7b\x28\x00\x80\x8f\x2a\x42\xf6\x3d\x14\x00\xc0\x47\xbd\x1c\ +\x7d\x4f\x6f\x0a\x00\x10\x3d\x45\x21\xfb\x1e\x0a\x00\xe0\xa3\xac\ +\x90\x7d\x0f\x05\x00\x00\x05\x00\x00\x05\x00\x00\x05\x00\x00\x05\ +\x00\x00\x05\x00\x00\x05\x00\x00\x05\x00\x00\x05\x00\x00\x05\x00\ +\x00\x05\x00\x00\x05\x00\x00\x05\x00\x00\x05\x00\x00\x05\x00\x00\ +\x05\x00\x00\x05\x00\x00\x05\x00\x00\x05\x00\xa0\x00\x00\xa0\x00\ +\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\ +\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\ +\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\ +\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\ +\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\ +\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\ +\x00\xa0\x00\x00\xa0\x00\x00\xa0\x00\x00\x14\x00\x00\x14\x00\x00\ +\x14\x00\x00\x14\x00\x00\x14\x00\x20\x46\x72\x42\xf6\x3d\x21\x90\ +\x45\xaf\x40\x42\xe4\xaa\x46\x5d\x9d\x7c\xd3\x16\xf5\x50\x33\x67\ +\x00\x40\x94\x5c\xec\xe8\xf0\x97\xba\xea\x22\xe2\x04\xa2\xf4\xeb\ +\x3f\x55\x75\x4a\x39\xdb\xea\x34\x55\xb9\x5c\x02\x00\x1d\x1f\x76\ +\x65\x1a\xaf\x91\xaa\x50\x2f\x15\x07\xd8\xd7\xb2\x55\xee\xec\xd7\ +\xbf\xed\x85\xc0\x2a\xb5\x06\xb6\x4f\x29\x6d\xd2\x7a\x2d\xd0\x7b\ +\x7a\x51\x95\x99\x5f\x90\x50\x00\xe0\x8d\x12\x5d\xa4\xd3\xb5\x0f\ +\x41\xf8\x60\x9e\xfe\xa4\x7b\x55\x4d\x10\x08\x87\x22\xdd\xa8\x7a\ +\x87\x27\xdc\x6c\x3b\xdf\xea\xf5\x5f\x2a\xa6\xeb\x21\x78\xc7\x69\ +\x29\x07\x64\x20\xdb\x72\x1d\xd7\xd9\xc6\xca\xa1\xbf\xc2\xa9\x2b\ +\xf5\x80\x7a\x11\x43\x20\x7a\xe8\xfb\x6a\xd0\x1c\x0a\x00\x82\x91\ +\xa5\x5b\x74\x1d\x8f\x96\x03\x94\xad\xa3\xd5\x5d\xcf\x53\x00\x10\ +\x84\x6b\x75\x0d\x21\x04\xee\x10\x35\x69\x76\xfa\x35\x1b\x70\xe3\ +\x1f\xf4\x38\x21\x84\xc4\x64\x3d\x41\x01\x80\x9f\xfa\x69\xbe\x76\ +\x21\x86\x90\xf8\x4a\xc3\xd3\x7b\x2c\xc8\x25\x00\xdc\xb8\x43\x87\ +\x11\x42\x68\x74\x55\x4f\x3d\xc5\x19\x00\xfc\x32\x44\x9f\x13\x42\ +\xe8\xda\x64\xe1\xce\xff\x11\x77\x6c\xe1\xc2\xcf\x89\x20\x74\xae\ +\xe5\x0c\x00\xfe\xe8\xa9\xcf\x54\x42\x0c\x21\xb3\x5a\x43\xb5\x81\ +\x33\x00\x78\x6f\x14\x87\x7f\x08\x95\xe8\x00\x2e\x01\xe0\x87\xa3\ +\x88\x20\xaa\xed\x42\x01\x80\xdd\x48\x22\x88\x6a\xbb\x50\x00\x60\ +\x37\x94\x08\xa2\xda\x2e\x14\x00\xd8\xf5\x21\x82\xa8\xb6\x0b\x4f\ +\x01\x60\xd7\xcc\x80\xb2\x50\x6a\xd9\xf9\xb4\x65\x9c\x01\xc0\x6e\ +\x0b\x11\x84\x52\x2d\x97\x00\xf0\xc3\x5a\x22\x88\x6a\xbb\x50\x00\ +\x60\xf7\x19\x11\x44\xb5\x5d\x28\x00\xb0\x7b\x8f\x08\xa2\xda\x2e\ +\x14\x00\xd8\xbd\x44\x04\x51\x6d\x17\x0a\x00\xec\xde\xd1\x6a\x42\ +\x08\x9d\x6a\xbd\x43\x01\x80\x1f\x36\xe8\x19\x42\x08\x9d\x67\x55\ +\xb3\xf3\x7f\xc4\x38\x00\xb8\x30\x58\x5f\x10\x42\xc8\x30\x1f\x00\ +\x7c\xb3\x50\xf7\x11\x42\xa8\x4c\x4f\xe7\xf0\xe7\x0c\x00\xae\x30\ +\x27\x60\x98\xa4\x3d\x27\x20\x67\x00\x70\xa3\x5a\x17\x10\x42\x68\ +\x5c\x98\xee\x4a\x81\x8c\xe1\x86\x2b\x9f\xa9\x49\xe3\x89\x21\x04\ +\xae\xd1\xf4\x74\xff\x29\x05\x00\xee\xbc\xa6\x22\x1d\x4a\x0c\x01\ +\x9b\xa6\x5f\xa4\xff\x8f\x29\x00\x70\xe9\x79\xd5\x6a\x3c\x17\x96\ +\x81\x69\xd1\xd5\xba\xae\x33\x1f\xe0\x26\xa0\x97\xb2\x55\xa0\x7c\ +\xe5\x2b\x4f\x3d\x34\x58\xfd\x55\xa2\x7e\xea\xa7\x52\xf5\x54\x37\ +\x65\xa9\xa7\xb2\xd4\x4d\xd2\x66\xa5\xb4\x41\x29\x6d\xd6\x7a\x55\ +\x69\xb5\xaa\xb4\x46\x2b\xb5\x50\x35\x6a\x52\xa3\x1a\xd5\xa0\xd6\ +\x48\xed\xf5\xb1\xba\x57\xdf\xa1\xf1\x03\xb0\x5c\x17\xea\xb9\xce\ +\x7d\x84\x02\xe0\x85\x2e\xda\x43\x83\xb5\x87\x86\x6a\x37\x95\xaa\ +\x54\xe5\x19\xe5\xdc\xaa\x4a\x55\xa9\x52\xcb\xb5\x44\x2b\xb4\x50\ +\x9f\x6b\x53\x44\xf6\xbf\x48\xd7\xea\x0a\x15\xd2\x11\x7c\x54\xaf\ +\xff\xd6\x7f\x74\xbe\x87\x50\x00\xdc\x3a\x48\x27\xea\x78\xf5\x57\ +\xa1\x0a\x55\xe8\x30\xdd\x46\xd5\xa9\x4e\x6b\xf5\xb2\x9e\xd6\x0b\ +\x6a\x89\x40\x12\xbb\xe8\x47\x3a\x4d\xfb\xd2\x25\x7c\x30\x4f\x8f\ +\x6a\x7a\xba\xf7\xfd\x29\x00\xae\xe5\xab\x9b\x76\xd3\x24\x4d\xd4\ +\x81\x3e\xfd\x17\xe7\x6a\x96\x9e\xd4\x2a\x6d\x52\x7d\xa8\x93\xc9\ +\x55\xa9\xc6\x6b\xa4\x2a\xd4\x5b\xc5\x01\xf6\xb5\x6c\x95\xa9\xc8\ +\xf9\xb7\xd6\xaa\x32\xc0\x8b\xb3\x94\x36\x69\x9d\x16\xe8\x3d\xbd\ +\xa8\x2a\x35\x73\x18\x06\x21\x47\x63\x74\x85\x1e\xd2\x22\xa5\x02\ +\xd8\x56\xe9\xcf\xfa\xa9\xc6\x70\xcb\x2d\xad\x52\x74\xa9\xea\x1c\ +\x66\x5f\xa7\x4b\x77\x3e\xdd\x16\xe2\xac\x4c\xd7\x6b\x89\x36\xaa\ +\x35\x90\x83\x7f\xdb\xd6\xaa\x8d\x5a\xa6\xbb\xb4\x07\x0d\xb2\x53\ +\x97\x3a\xcc\xfd\x52\xe2\x4c\xee\x09\x7f\xb9\xce\xd1\xdb\x81\x1e\ +\xf6\xed\x6d\x0b\x75\x89\x76\xe5\xc6\x5b\x87\x67\x01\xb5\x8e\xb2\ +\xde\xcc\xaf\x7f\x32\x15\xeb\x0c\x3d\xac\x0d\xa1\x3b\xf8\xb7\x6d\ +\x0d\x7a\x5c\x3f\x54\x6f\x1a\x6a\x07\x3e\x77\x94\x33\x2b\x21\x27\ +\xd0\x00\xdd\xa7\xd5\x6a\x08\xed\xc1\xbf\x6d\x6b\xd2\x57\x9a\xae\ +\xfe\x34\x58\x3b\x3e\x75\x94\xf1\xa7\x44\x99\x24\x05\x1a\xa8\x5f\ +\x87\xfe\xc0\xdf\x7e\xfb\x83\x86\x70\x41\x40\x01\x80\x4d\x9e\xce\ +\xd4\x93\x91\x3b\xf8\xb7\x6d\xcf\xe8\x0c\xae\x56\x29\x00\xc8\xd4\ +\x64\x2d\x8c\xc0\x49\x7f\xc7\x77\x05\x16\xe9\xfb\x34\x24\x05\x00\ +\x9d\x3d\xed\x1f\xa9\x57\x22\x7d\xe8\xb7\xdd\xde\xd3\x68\x15\xd0\ +\xa8\x14\x00\xa4\xe7\x18\x3d\x13\x9b\x83\x7f\xdb\x36\x4b\x27\x53\ +\x00\x28\x00\xd8\x99\x7e\x7a\xce\xd9\xf3\xe2\x70\x6d\x75\x7a\x5e\ +\x03\x28\x00\x14\x80\xb6\x18\x46\xda\x56\xb1\x2e\xd2\x0a\x1d\xa3\ +\xae\xb1\xdc\xbb\x42\x1d\xad\xa5\xba\x4c\xc5\x34\x34\xd0\xde\x2d\ +\xbf\x0f\x63\xf9\xcb\xbf\xfd\xf6\x81\x4e\xe5\x0c\x80\x33\x00\x7c\ +\xf3\xa6\xdf\x4c\x35\x25\xe2\xf0\xff\x7a\xb0\xd0\x5f\x62\x7a\x96\ +\x43\x01\xe0\x12\xa0\xd3\xf2\x34\x51\x6b\x75\x52\x82\x9e\x98\xe7\ +\xea\x78\xad\xd5\xa9\x8c\x11\x00\x05\xa0\x4c\xf7\xeb\x69\x0f\xde\ +\x16\x0f\xff\x1d\x81\x47\xf5\x80\x06\x72\x08\x20\xc9\x26\x68\x45\ +\x62\x4e\xfc\xdb\x9f\x53\x60\x12\x97\x00\x5c\x02\x24\x53\x17\x5d\ +\xa5\x59\x09\x7f\x30\x56\xa6\x27\xf4\x73\xde\x19\x40\xf2\xec\x19\ +\xa3\x91\x7e\xd6\xed\x25\x0d\xe3\x0c\x80\x33\x80\x24\x39\x42\xaf\ +\xeb\x08\xaa\xe0\x56\xe3\x34\x5b\x63\x89\x21\x89\xb2\x13\xb9\xcf\ +\x97\xea\x15\xf5\xa1\xf1\xdb\xe8\xa3\x57\x75\x35\x53\xc4\x52\x00\ +\xe2\xaf\x50\x33\xf4\x2b\x1a\xbe\x1d\xbf\xd4\x6f\x13\x39\x3a\x80\ +\x02\x90\x20\x05\x7a\x53\x67\xd3\xec\x3b\x70\xb6\xe6\x26\xf0\x81\ +\x28\x05\x20\x31\x06\x6b\xb1\x46\xd0\xe8\x1d\xd8\x5b\x8b\x34\x94\ +\x18\x28\x00\x71\x93\x25\xe9\x18\xbd\xac\x72\x9a\x7c\x27\xfa\xe9\ +\x25\x1d\x2d\x96\x8c\xa1\x00\xc4\x4a\x4a\xa7\x25\xfe\x99\x7f\xba\ +\xca\x35\x4b\x67\x2a\x45\x10\x49\x90\x94\xd1\xe0\xdf\xd3\xef\x03\ +\x2b\x76\x4d\xaa\x57\xbd\x1a\xb4\x56\x9f\xea\x33\x2d\x57\x95\xaa\ +\x55\xa9\x3a\xad\x6f\xf3\x6f\x7a\xa9\x8b\xca\x54\xaa\x52\xed\xaa\ +\x0a\x55\xa8\x8f\x0a\x54\xa8\x42\xe5\x05\xf2\x17\xe7\xeb\xb7\x6a\ +\xd6\x23\x1c\x1e\x14\x80\x78\xb8\x40\x77\x06\x70\xf8\x37\xeb\x63\ +\xcd\xd7\x42\x2d\xd2\x62\x2d\x56\x65\x87\xff\x76\xbd\xd6\x6b\xd5\ +\x37\xfe\x97\x32\x0d\xd4\x20\x0d\xd4\x10\x0d\xd7\xde\xca\xf1\xf9\ +\x6f\xcf\xd1\x0c\x15\xe9\x01\x0e\x10\x44\xdf\xbf\xaa\xc5\xe7\x91\ +\x75\xeb\x75\xb7\x8e\x53\x7f\xf5\x54\xbe\x83\x5f\xe3\x9e\x1a\xa0\ +\xe3\x75\x8f\x6a\x7c\xde\x8b\x16\x5d\x13\xb3\x9e\xc0\x48\xc0\x04\ +\xba\xc0\xc7\xc3\x7f\x93\x96\xe8\x66\xed\xe3\xd9\xbe\x8c\xd0\x2d\ +\x5a\xec\x63\x21\x68\xd1\xf9\x14\x00\x0a\x40\x54\x65\x49\xfa\x9e\ +\xb6\xf8\x72\xa8\x34\xeb\x79\x5d\xe3\xd3\x70\xda\x91\xfa\x17\xbd\ +\xe0\x53\x59\xab\xd7\xe9\x8a\xcf\x13\x01\x0a\x40\xc2\x7c\xdf\x97\ +\xc3\xa4\x45\xd3\x34\xd8\xe7\x01\x34\x45\x1a\xa8\xeb\x7c\x59\xb3\ +\xa0\x59\xa7\x71\x06\x40\x01\x88\xa2\x63\xd5\xe8\xf9\xef\xe3\x22\ +\x4d\x0d\x74\x1f\x2f\xd3\x22\xa7\xeb\xde\xb7\xbf\xb8\xc8\x78\x0a\ +\x00\x05\x20\x6a\x86\xea\x4b\x8f\x0f\x8c\x47\x75\xba\xef\x77\xe7\ +\xbf\x2d\x57\x93\xf5\x27\x8f\xf7\x74\xa5\x86\x50\x00\x28\x00\x51\ +\x92\xaf\x55\x9e\x1e\x12\xcf\xaa\x22\xa0\x67\xf4\xed\xef\xed\x10\ +\x8f\x57\x30\x5c\xa5\x2e\x14\x00\x0a\x40\x54\x74\xf1\x70\x82\xef\ +\x06\x7d\xa4\x23\x43\xb9\xd7\x07\xea\x7d\x0f\x2f\x7a\xe6\xc7\xa0\ +\x04\x50\x00\x12\x21\x4b\xbf\xf3\x70\x95\xbd\xb3\x42\xbd\xef\x67\ +\x6b\xae\x67\xfb\xfe\x60\xe4\x07\x8e\x53\x00\x12\xe1\x27\x9e\x1d\ +\x02\x17\x47\xe0\x7d\xf9\x2e\xba\xc4\xb3\xfd\x9f\x4a\x01\xa0\x00\ +\x84\xdd\x58\x8f\x1e\x86\xcd\x51\x49\x64\x32\xe8\xa5\x17\xd4\xec\ +\x49\x0e\x47\x52\x00\x28\x00\x61\x36\x4c\x5f\x79\xd0\xed\x17\xeb\ +\x82\xc8\x5d\x06\x5d\xa8\x25\x1e\x24\xb1\x56\x15\x14\x00\x0a\x40\ +\x78\x4f\x7f\xbd\x98\xe9\xf7\x09\x95\x45\x32\x8d\x72\xcd\xf4\x20\ +\x8d\x17\x23\x3c\x89\x38\x05\x20\xe6\xfe\xd9\x79\x77\xaf\xd5\x0d\ +\x91\x4e\xe4\x7a\x0f\x86\x42\x5f\x4d\x01\xe0\x50\x0b\xa3\x89\xce\ +\xbb\xfa\x47\x1a\x13\x83\x7b\x22\xee\x1f\x89\x1e\x4f\x01\x40\xd8\ +\xec\xae\x95\xce\x97\xcb\xe8\x15\x8b\x64\x7a\xe8\x39\xc7\xc9\x7c\ +\xa9\x5d\x29\x00\x08\x93\x3c\xe7\xcf\xfe\x6f\x89\x55\x3e\xb7\x39\ +\x4e\xe7\xb7\x91\x9c\x4a\x86\x02\x10\x5b\x93\x1c\x3f\xf4\xbb\x28\ +\x76\x09\x5d\xec\x78\x94\xe0\x49\x14\x00\x84\x45\x17\xc7\xb7\xba\ +\x0e\x8d\x65\x4a\xa3\x9d\xbe\x1c\x5d\xab\x02\x0a\x40\xf4\xc5\x63\ +\x56\xe0\x47\x1d\x8e\x53\x5f\xa3\x3d\xf5\x7a\x2c\x0b\xc0\x5c\xed\ +\xa5\x35\xce\xbe\xad\xab\x1e\xe5\x97\x07\x61\x70\xaa\x9a\x1c\xde\ +\xde\x3a\x20\xd6\x59\xed\xa7\xa5\xce\xb2\x6a\xd2\x64\xce\x00\x10\ +\xb4\x62\xcd\x73\xd6\xa5\xab\x54\x1a\xfb\xbc\x4a\x55\xed\x2c\xaf\ +\xf7\xd5\x8d\x02\xc0\x25\x40\xb0\xce\x72\x36\x09\xe7\x2a\xed\xaf\ +\xaa\xd8\x17\x80\x2a\xed\xab\x2f\x9d\x9d\x4f\x9c\xcb\x2f\x10\x82\ +\x54\xee\xec\xf4\x7f\xb5\x46\x25\x26\xb5\x03\xb4\xc6\xd9\xf3\x92\ +\x28\x9d\x33\x71\x06\x10\x3b\x2f\x38\x6a\xd2\xd6\x98\x4c\x7a\x95\ +\xae\x0a\x67\x97\x01\xff\x47\x01\xe0\x12\x20\x28\x13\x1d\x0d\xd5\ +\x6d\xd6\xa1\xfa\x22\x51\x05\x60\x81\xc6\xa8\xc5\xc9\x37\x8d\x8d\ +\xec\xc0\x60\x44\x5c\x81\x9e\x71\x36\xd1\x47\x12\x5d\xac\x56\x27\ +\xe9\x3d\x1d\x99\x11\x01\x9c\x01\xc4\xca\x68\x06\xfd\x1a\xdd\xee\ +\x28\xc1\x51\x14\x00\xf8\xef\x1d\x27\x8d\xf9\x7c\xa2\x33\x7c\xd1\ +\x49\x86\x6f\x50\x00\xe0\xb7\x53\x9d\x34\xe5\xc7\xea\x9d\xe8\x14\ +\x7b\x69\xbe\x93\x1c\x27\x53\x00\xe0\xa7\x3c\x2d\x72\x32\x9e\x7d\ +\x4c\xe2\x93\x3c\xdc\xc9\x7b\x14\x5f\x44\xe2\xed\x40\x0a\xc0\xb7\ +\x44\xf5\x29\xc0\x14\x0d\x70\xf0\x2d\x77\x68\x4e\xe2\x0b\xc0\xab\ +\x9a\xe6\xe0\x5b\x76\x8b\xd1\x0a\x82\x08\xbd\x02\x27\xb3\xdd\x3d\ +\x45\x90\x5b\xb9\x58\x55\xe8\xc9\x08\x3c\x0b\xe0\x0c\x20\x26\x86\ +\x38\x99\xe9\xb7\x9c\x20\xb7\x2a\x77\xf2\x8a\xd0\x20\x0a\x00\x97\ +\x00\xfe\xb8\xce\xfc\x0d\x2d\xba\x41\xab\x38\xf2\xb7\x5a\xa5\xff\ +\xe7\x60\x58\xd0\x2f\x08\x12\x7e\x18\xe0\xa0\x86\xbf\xa2\x2c\x82\ +\x6c\x23\x4b\xb3\x1d\xa4\x1a\xf6\x73\x2a\xce\x00\x62\x71\x06\xf0\ +\xef\x0e\xbe\xe3\x34\xa5\x38\xea\xdb\x48\xe9\xbb\x0e\xbe\xe5\xdf\ +\x08\x92\x02\xe0\xb5\x1e\x3a\xd9\xfc\x1d\x17\x3b\x9c\x19\x27\x2e\ +\xd6\xe8\x32\xf3\x77\x4c\x56\x8f\x90\x97\xb9\x70\x7d\x0f\x05\x20\ +\x03\x93\xcc\x9d\xec\x7d\x3d\xc4\xf1\xde\x8e\x07\xf4\x9e\xb9\x38\ +\x87\x7b\xaa\xd0\xda\x90\x7d\x0f\x05\xa0\xd3\x0a\x34\x51\x79\xa6\ +\x6f\x68\xd4\xad\xda\xc2\xd1\xde\x8e\x3a\xdd\xae\x26\xd3\x37\xe4\ +\x69\x62\xa8\x1f\x06\xae\x77\xf4\x3d\xeb\xe8\x2c\x41\xe9\xaf\x4d\ +\xc6\x1b\x38\x1f\x10\x62\x07\xac\xd3\xab\xd5\x84\xfa\x46\xe0\x1d\ +\x8e\x6e\x02\xde\xc1\x19\x40\x50\x4e\x30\xcf\x42\x77\x29\x47\x79\ +\x07\xa6\x1a\x3f\xdf\x5d\xc7\x86\x78\xef\xde\x0b\xd9\xf7\xa0\xd3\ +\x3e\x36\x8f\x57\x43\xc7\x66\xc5\xf8\x0c\x6b\x57\x47\x67\x00\x03\ +\xe8\x26\xc1\xd8\xdd\xdc\x74\x43\x09\x71\x27\xec\xa3\x2c\xc3\xbb\ +\x6e\x60\xae\x93\x85\x52\x3f\x8c\xe4\xb2\x68\xb1\xb8\x04\xb8\xc2\ +\xf8\xf9\x3f\x69\x09\x47\xf8\x4e\x2c\xd3\x63\xc6\x6f\xf8\x49\x68\ +\xf7\xad\xd9\xc9\x62\x26\x7f\x54\x33\xdd\x24\x08\x59\xc6\x11\xeb\ +\x75\x3a\x95\x10\xd3\x70\xba\xea\x4d\x39\x2f\x0d\xf1\xcf\x4a\x89\ +\xea\x8c\xbf\xff\x75\xea\x4b\x17\x09\xc6\x61\xda\x68\x7c\xfd\x27\ +\x97\x10\xd3\x90\x63\x9c\x6b\xa1\x46\x87\x84\x78\xef\x6e\x34\x16\ +\x80\x1b\xe8\x20\x41\xf9\xa9\x71\x12\x4b\xee\xff\xa7\xeb\xc7\xc6\ +\x29\xd6\xaf\x08\xf1\xbe\x75\x33\x9d\x47\x2e\x8d\xd8\x5a\x48\x31\ +\x52\xa0\x3f\x18\x57\xb2\x43\x67\xae\x96\x2d\x59\x3f\xac\xfc\x10\ +\xef\xdb\xb1\x19\xef\x5d\x73\xa8\x1f\x72\xc6\x5c\x1f\x2d\xe1\xd4\ +\xcd\x37\x37\x9b\xb2\x5e\x18\xf2\x99\x16\xaf\xcc\x70\xbf\xae\xa4\ +\x63\x04\x67\x3f\xe3\x12\x56\x83\x88\xb0\x13\x06\xab\xc5\x94\xf7\ +\xbe\x21\xdf\xbf\x5b\x33\xd8\xa7\x5b\xe9\x16\x41\xfa\x85\x71\xf2\ +\xef\x22\x22\xec\x84\x22\xe3\x84\xe1\xd7\x84\x7c\xff\xb2\x74\x75\ +\x27\xf7\xe8\xe7\xcc\x20\x11\xac\xb9\xb1\xee\x90\xe1\xf3\x73\x53\ +\xde\xaf\x47\x60\x0f\x4f\xd4\xea\x34\xf7\x66\x8d\xfe\x81\x0e\x11\ +\x34\x4b\x77\xdc\xa8\x83\x08\xb0\x93\x0e\x35\x3e\x74\x8d\x82\x7e\ +\xba\x37\x8d\x3d\xb9\x4f\xfd\xe8\x0e\x41\x3b\xd2\xd4\x19\x17\x11\ +\x60\x06\x6c\x37\x5d\xc7\x46\x64\x2f\x07\x69\x86\xaa\x77\xb0\x0f\ +\x55\xfa\x8d\x2a\xe2\xdd\xc8\x51\x19\x1a\x33\xd1\xf4\xe9\xc7\x38\ +\x9a\x33\xf0\x84\xe9\x79\xfe\x04\xcd\x8e\xc4\x5e\x2e\xd2\x0f\xd4\ +\x53\xfb\xeb\x68\x8d\x54\x85\x7a\xab\x48\xb5\x5a\xa7\x05\xfa\x40\ +\xaf\xe8\x2d\x6d\xa0\x1b\x84\xc3\xfb\xa6\x5f\xa3\x7d\x08\x30\x03\ +\xfb\x9b\x32\xe7\x95\x59\x38\x53\xac\x4a\x43\x57\x5c\x4f\x80\x19\ +\xb2\xdc\x05\xa8\xe4\xb9\x4b\x14\x44\xe3\x6d\xc0\x81\x2a\x34\x7c\ +\xfa\x7f\x69\xe6\x0c\x3d\x62\xf8\x6c\xa1\x06\x12\x20\x05\x20\x0c\ +\x05\x80\x49\x40\x32\xf5\x38\x05\x80\x02\x10\xf5\x02\xd0\xac\xf9\ +\x34\x73\x86\xe6\xa9\x95\x02\x40\x01\x08\xfe\x6f\xb4\x0c\xe3\xfd\ +\x38\x4e\x93\x38\xfb\x6c\xb3\xa9\x78\x0e\x8a\xec\xda\xd3\x14\x80\ +\x50\x29\xd0\x6e\xa6\x02\xc0\x24\xe0\x99\xda\xa2\x8f\x0d\x9f\xde\ +\x2d\x02\xeb\x05\x53\x00\x22\xf0\x37\xe6\xab\xd4\xf0\xe9\xcf\xd5\ +\x48\x33\x67\xa8\x51\x9f\x1b\x3e\x5d\x1a\xea\x97\x82\x11\xa1\x02\ +\x50\x96\xf1\x67\x9b\x18\x05\x68\xb2\xc4\x30\x8f\x42\xa9\x71\x09\ +\x17\x50\x00\x24\x49\x79\x86\x02\x50\xaf\xc5\x34\xb2\xc1\x62\xd5\ +\x67\xfc\xd9\x72\x0a\x00\x05\xc0\x85\x9e\x86\x17\x31\x29\x00\xc1\ +\x15\x80\x9c\x90\x2f\x15\x8a\x88\x14\x80\x3d\x0d\x9f\x6d\x50\x15\ +\x8d\x6c\xb0\xd2\x74\x07\x65\x4f\x02\xa4\x00\xd8\x59\x16\x9a\x58\ +\x4b\x13\x1b\x59\x06\x52\xb3\x82\x0e\x05\xc0\x81\x32\xc3\x67\x3f\ +\xa5\x89\x8d\x3e\x0d\xa8\xe5\x40\x01\xd8\xca\xf2\x10\x70\x01\x4d\ +\x6c\xb4\x20\xa0\x96\x03\x05\xc0\x41\x37\x5a\x41\x13\x1b\xad\xa0\ +\x00\x50\x00\xa2\x7b\x09\xc0\x2d\x40\xab\x4a\x2e\x01\x28\x00\xc1\ +\xea\x63\xf8\x6c\x35\x4d\x6c\x64\x29\xa1\xac\xa2\x47\x01\x70\xa0\ +\x4b\x40\xbf\x5f\xb0\x16\x80\x42\xe2\xa3\x00\xd8\x59\x5e\x29\xd9\ +\x44\x13\x1b\x6d\xa4\x00\x50\x00\xa2\x7b\x06\x50\x47\x13\x1b\xd5\ +\x53\x00\xe2\x2c\x0a\xab\x9d\xa4\x62\xbe\x7f\xe4\x0f\xce\x00\x00\ +\x50\x00\x00\x50\x00\x90\xf8\x8b\x07\x50\x00\xb6\xb2\xdc\xc8\xcb\ +\xa5\x89\x8d\x2c\xcf\x60\x98\x8c\x8d\x02\xe0\x80\xa5\x1b\x75\xa1\ +\x89\x29\x00\x48\x6e\x01\x28\xa6\x89\x8d\x7a\x50\x00\x28\x00\xd1\ +\xbd\x04\x60\x34\xba\x55\x29\x05\x80\x02\x10\xac\xda\x80\xba\x2f\ +\xac\x25\x94\x02\x40\x01\x70\x60\x1d\x05\x20\x40\xfd\x0c\x9f\x65\ +\x3e\x26\x0a\x80\x03\x4b\x0d\x9f\x65\x52\x2a\x2b\xcb\xa2\x2c\xcb\ +\x88\x8f\x02\x60\x67\x99\x92\x62\x18\x4d\x6c\x34\xd4\xf0\xd9\xe5\ +\xc4\x47\x01\xb0\xb3\x74\xa3\x0a\x9a\xd8\x68\x18\x05\x80\x02\x10\ +\x2c\xcb\x89\x64\x1f\x9a\xd8\x24\x4b\xbd\xb8\x04\xa0\x00\x44\xf7\ +\x12\xa0\x80\x07\x81\x26\xe5\xa6\xf5\xfd\x56\x12\x20\x05\xc0\xce\ +\xf2\x30\x89\x55\xea\x6d\x06\x9a\xde\xe9\x67\x36\x06\x0a\x80\x03\ +\x4d\x86\x99\xfd\x28\x00\x36\x83\x0c\x05\xa0\x92\x75\x99\x29\x00\ +\x2e\xd4\x1b\x6e\x26\xe5\x69\x10\x8d\x6c\xb0\x87\x61\x81\xcf\xe5\ +\x6a\x20\x40\x0a\x80\x5d\x83\xe9\x66\xd2\x10\x56\xa9\xcf\x58\xbe\ +\x86\x18\x3e\x4d\x01\xa0\x00\x84\xa0\x00\x0c\x57\x57\x9a\x39\x43\ +\x5d\x35\x9c\x02\x40\x01\x08\xde\x0a\xc3\xd4\x12\x7b\xab\x1b\xcd\ +\x9c\xa1\x6e\x86\x02\x90\xe2\x21\x20\x05\xc0\x15\xcb\x6f\x49\x8e\ +\xe9\x57\x2c\xd9\x46\x18\x26\xf5\xac\x67\x18\x10\x05\xc0\x95\x65\ +\xa6\x93\xc9\xc9\x34\x73\x00\xc9\x35\x70\x06\x00\x77\xa7\xa2\xab\ +\x94\xca\x78\xdb\x40\x80\x19\xda\x6c\x48\x7d\x15\xf7\x5e\x38\x03\ +\x70\xd7\x11\x97\x1a\x3e\xdd\x43\x23\x68\xe8\x0c\x8c\x54\x91\xe1\ +\xd3\x8b\x99\x0d\x80\x02\xe0\xce\x73\xa6\x4f\x9f\x4d\x43\x67\xe0\ +\x1c\xd3\xa7\x9f\x25\x40\xb8\x73\xb0\xe1\x64\x34\xa5\xc5\x04\x98\ +\x81\x25\xa6\xcc\x47\x13\x20\x5c\xb2\x74\xc6\x8d\x3a\x84\x00\x3b\ +\x69\x8c\x36\x99\x32\x07\x97\x00\x4e\xbd\x69\xf8\x6c\xb1\x8e\xa2\ +\xa9\x3b\xe9\x28\xd3\xf8\x89\x39\x04\x48\x01\x70\xeb\x19\xd3\xa7\ +\xc7\x99\x6e\x68\x25\x4f\x91\xc6\x99\x3e\xff\x17\x22\x84\x5b\xfb\ +\x98\x4e\x48\x5b\x34\x98\x08\x3b\x61\xb0\x5a\x4d\x79\x33\xf8\x8a\ +\x33\x00\xc7\x56\x6a\xa1\x69\x3f\x2f\xa4\xb1\x3b\xe1\x47\xa6\x85\ +\xbd\x3f\xd7\x2a\x22\x84\x5b\xf9\x7a\xc8\xf4\x9b\xd4\x4c\x84\x9d\ +\xd0\x62\xca\xfa\x41\xc3\x4b\xc4\xe0\x0c\xa0\x5d\x8d\x7a\x4b\xad\ +\x86\xcf\xe7\x68\x2a\xcd\x9d\xa6\xcb\x4d\xfd\xa2\x55\x6f\xa9\x89\ +\x10\xe1\xda\x68\xd5\x18\x47\x03\xf0\xbb\x94\x8e\x5c\x2d\x36\xe5\ +\xbc\x41\x07\x10\x22\xdc\xcb\xd2\x32\x53\xc7\xac\xd7\x69\x84\x98\ +\x86\xef\xa9\xde\x94\xf3\x52\x22\xe4\x12\xc0\x0b\x29\xcd\x34\x7d\ +\xbe\x40\x53\x98\x1d\x28\x8d\x7b\x2d\x53\x4c\x4b\x82\x4b\x8f\x11\ +\x22\xbc\xb1\xab\xe9\x97\x29\xa5\x94\x69\x9d\x9b\x64\xd8\xd3\x9c\ +\x71\x39\x21\xc2\x2b\xef\x18\x3b\xe7\x53\x44\xb8\x13\xcf\x1a\x13\ +\x7e\x83\x08\xe1\x9d\x29\xe6\xdf\xa7\xb1\x84\xd8\x81\x23\xcc\xf9\ +\x9e\x42\x88\xf0\x4e\xa9\xbe\x32\x76\xd0\x79\x84\xd8\x81\x8f\x8c\ +\xe9\xae\x36\x2d\x27\x0e\xdf\x65\x47\xec\xef\x5d\xaf\x17\x8d\xdf\ +\x30\x94\xd9\x01\x76\xe8\x07\xe6\x7b\x24\x2f\x68\x3d\x31\xc2\x4b\ +\x3f\x54\x93\xf1\x57\xea\x7d\x5e\x0c\x6a\x57\x91\x3e\x34\x26\xdb\ +\xa8\x73\x89\x11\xde\xea\xae\xd5\xe6\xeb\xd4\x4b\x88\xb1\x1d\x3f\ +\x36\xe7\xba\x5a\xc5\xc4\x08\xaf\xdd\x66\xee\xa8\x29\x95\x10\xe3\ +\x76\xfa\x3a\x48\xf5\x66\x62\x84\xf7\x7a\x3a\xe8\xaa\x7f\x8d\xdc\ +\xdd\x0f\x6f\x65\xe9\x35\x07\xa9\x76\x27\x48\xf8\xe1\x5e\x73\x57\ +\x6d\xd6\x45\xc4\xd8\xc6\x45\x6a\x36\x67\x7a\x37\x31\xc2\x1f\x83\ +\x1d\xfc\x5a\x2d\x61\xc4\xda\xdf\xf4\x37\xbe\x65\xf1\xf5\xc6\x3a\ +\xcc\xf0\x49\xa1\x9e\x76\xd0\x61\x19\x15\xb8\xcd\x2c\x27\x69\x16\ +\x12\x24\xfc\x72\xba\xf1\x8d\xb5\xaf\xb7\x1b\x08\x52\xd2\x7f\x3a\ +\x48\xb2\x5e\xa7\x12\x24\xfc\xf4\xa9\x83\x6e\xbb\x45\x87\x27\x3e\ +\xc7\x71\xaa\x73\x90\xe4\xc7\x74\x48\xf8\xeb\x04\x07\xdd\x36\xa5\ +\x4f\xd4\x27\xd1\x29\xf6\xd6\xc7\x4e\x72\x3c\x9e\x0e\x09\xbf\xcd\ +\x71\xd2\x75\x5f\x4d\x74\x86\x2f\x3b\xc9\x70\x36\x9d\x11\xfe\x1b\ +\xe5\xa4\xf3\xa6\x74\x5b\x62\x13\xbc\xcb\x51\x82\x4c\x01\x86\x00\ +\x14\xe8\x29\x47\x1d\x38\x99\x43\x83\x2f\x75\x94\xde\xd3\xc6\x19\ +\x84\x80\x0c\x1d\xa3\x5a\x27\x5d\xb8\x45\x47\x24\x2e\xbb\xc3\x1c\ +\x0c\xfd\x49\x29\xa5\x5a\x1d\x4d\x47\x44\x50\x66\x39\xfa\x15\x4b\ +\xda\x64\x61\xc3\x9c\xe5\xc6\x68\x0a\x04\x68\x17\x27\x0f\xb1\x52\ +\x4a\x69\x9d\x0e\x4c\x4c\x6a\x07\x98\xa7\x55\xd9\xb6\x35\x24\xfc\ +\x29\x0a\x02\x77\xae\xb3\xdf\xb2\x95\x09\x19\x1c\xdc\x4f\x2b\x9c\ +\x65\x76\x1e\x1d\x10\xc1\xea\xa6\xb7\x9d\x75\xe7\x2a\x95\xc5\x3e\ +\xaf\x32\x55\x3b\xcb\x6b\xae\x69\x09\x71\xc0\x89\x53\xcc\x73\x04\ +\xfd\x7d\xfb\x52\xa3\x62\x9d\xd5\x48\xad\x74\x96\x55\x93\x26\xd3\ +\xf9\x10\x06\x8f\x3b\xeb\xd4\x29\xad\x51\x45\x6c\x73\xda\xcb\xd9\ +\xb5\x7f\x4a\x29\xfd\x99\x8e\x87\x70\xc8\xd2\x7a\x87\x1d\x3b\xa5\ +\xc3\x62\x99\xd2\xe1\x4e\x33\xda\xa0\x1c\x3a\x1e\xc2\xe2\x68\xb5\ +\x3a\xec\xdc\xcd\x31\x1c\x1a\x34\xd5\xb8\xe4\xf7\xf6\xdb\x51\x74\ +\x3a\x84\x47\x8e\xa6\x3b\xed\xde\x29\xdd\x1e\xab\x7c\xee\x72\x9c\ +\xce\xbd\xfc\xfe\x23\x5c\x76\xd7\x97\x8e\x3b\xf9\xcb\x31\x79\xc6\ +\xdd\x47\xaf\x3a\x4e\x66\xa5\xbe\x43\x87\x43\xd8\x9c\xe0\xb8\x9b\ +\xa7\xb4\x20\x06\x43\x84\xc7\xe9\x13\xe7\xb9\x4c\xa0\xb3\x21\x8c\ +\x2e\x71\xde\xd5\xb7\xe8\xa6\x48\x27\xf2\x9f\xce\x46\x4a\xfe\x7d\ +\xbb\x8c\x8e\x86\x70\x2a\xd0\x4c\xe7\xdd\x3d\xa5\x59\xea\x1f\xc9\ +\x34\xca\xf5\x84\x07\x69\x3c\xa1\x7c\x3a\x1a\xc2\x6a\x90\xaa\x3c\ +\xe8\xf4\x4b\xf5\xa3\x88\xdd\xf4\xca\xd2\x45\x4e\x66\xfa\xdd\x7e\ +\xab\xd6\x40\x3a\x19\xc2\x6c\x8c\x07\xdd\x3e\xa5\x16\xcd\x89\xd0\ +\xba\xb7\xfd\xf4\x8a\xa3\x97\x7d\xb7\xdf\x98\x43\x11\xa1\xf7\x13\ +\x4f\xba\xfe\xd7\x57\xbf\xe1\x1f\xfb\x5e\xe4\x60\x8d\xbf\x1d\x6d\ +\x53\xe9\x5c\x88\x82\x7b\x3c\x3b\x04\xe6\xeb\x9c\x50\xef\xf9\xb9\ +\xfa\xc0\xb3\x7d\xff\x1f\x3a\x16\xa2\x21\x57\x6f\x79\x76\x18\x34\ +\x6a\xbe\xc6\x85\x72\xaf\x47\xeb\x03\x35\x7a\xb6\xdf\xef\x28\x8f\ +\x8e\x85\xe8\x94\x80\x15\x9e\x1d\x0a\x29\xa5\xf4\x8a\x86\x85\xe8\ +\x6e\x78\x81\xf6\xd4\x93\x9e\xee\xef\x0a\x46\xfe\x21\x5a\x06\x3b\ +\x7c\xf1\xb5\xfd\xed\x31\x9d\xae\xdc\xc0\xf7\x33\x4f\x53\x9c\xbe\ +\x0d\xd9\xfe\x4b\xd2\xac\xfb\x87\xc8\x39\x5c\x0d\x1e\x1f\x18\xf5\ +\x5a\xac\x1f\x07\xba\x8f\x97\x6b\x89\x93\x45\xd2\x3a\xde\xcb\xb1\ +\x74\x26\x44\xd1\xb1\x1e\x3d\x0c\xfb\xe6\xd6\xaa\x69\x1a\xa2\x22\ +\x5f\xf7\xac\x48\x83\x74\x9d\x87\x57\xfc\x6d\xa7\xfd\x38\x91\x8e\ +\x84\xa8\x9a\xa4\x4d\x3e\x1c\x24\x29\xb5\xe8\x05\x5d\xeb\xd3\x2f\ +\xe5\x48\x5d\xad\x97\x9c\xbe\xfe\xdc\xd1\xa4\xdf\x27\xd1\x89\x10\ +\x65\x67\xf9\x72\x16\xf0\xf5\xb6\x49\x4b\x34\x4d\xfb\x79\x78\xe8\ +\xdf\xae\x25\xda\xe0\xdb\xfe\x34\xeb\x0c\x3a\x10\xa2\xee\x4a\xc7\ +\x53\x61\xec\x7c\xdb\xa8\xe9\x9a\xa0\x01\xea\xe9\xe0\x49\x41\xbe\ +\x7a\x6a\x80\x26\xea\x7e\x9f\xce\x65\xda\x9e\xd5\xfc\x8c\xce\x13\ +\x77\x59\x89\xd8\xcb\xf3\x74\x97\xba\xf8\xfe\x5f\x6d\xd1\x27\x9a\ +\xaf\x2f\xb4\x48\x4b\xb4\x48\xab\x3a\xd9\x2e\xe5\x1a\xa8\x81\x1a\ +\xa4\xc1\x1a\xae\xbd\x95\xed\xfb\x5f\xbf\x45\x97\xea\xb7\x1c\x20\ +\x14\x80\xb8\xdc\x0b\xf8\x73\x60\x4f\xb2\x9b\x54\xaf\x7a\x35\x6a\ +\x9d\x16\x68\x81\x96\xab\x4a\xd5\xaa\xd4\x16\xd5\xa8\x65\xeb\xbf\ +\xc8\x51\x0f\x75\x55\x99\xfa\xa9\x54\xbb\xa9\x42\xc3\xd4\x4b\xf9\ +\x2a\x54\x61\x60\x83\x6f\x5a\xf4\x5d\xcd\xe4\xf0\x40\x7c\x8c\xd7\ +\x16\x9f\x4f\xa1\xa3\xbb\xd5\xe9\x04\x3a\x0c\xe2\x66\xb4\x27\x2f\ +\xc8\xc6\x6f\x5b\xa1\x43\xe9\x2c\x5c\x02\xc4\xd1\x50\xbd\xa2\x52\ +\x1a\xbd\x43\x95\x1a\xa7\xcf\x88\x81\x02\x10\x4f\xf9\x9a\x13\xf3\ +\xb5\x7f\x6c\x3e\xd0\x18\x6d\x21\x86\xe4\xc8\x4e\xd8\xfe\x36\xea\ +\x60\xdd\x45\xb3\xef\xc0\x3d\x1a\xcd\xe1\x8f\xf8\x3b\x87\x2b\xfd\ +\x76\xb6\x0b\x12\x77\x3e\x88\xc4\x3a\xc2\xe9\x2a\x79\xd1\xdf\xbe\ +\x62\xb2\x2f\x24\xcb\x60\x3d\xc6\x81\xbf\x75\x7b\x56\x43\xe9\x10\ +\x48\x9a\x7c\x5d\xc4\xc1\xaf\x94\x2e\x53\x21\x9d\x01\xc9\x74\x64\ +\xc2\xc7\x06\x2c\xd3\xb1\x74\x82\x24\x4b\xfa\x44\x4f\x2b\xd4\xaa\ +\xe3\x13\xbc\xff\x37\xe9\x11\x35\x73\x18\x20\x89\xf6\xd1\xdd\xaa\ +\x4a\xf8\x00\xe1\x06\x55\xeb\x61\x8d\xa1\x33\x20\x39\x72\xd5\x47\ +\xe7\xea\x0b\xae\xfe\xdb\x6c\x9f\xeb\x1c\xf5\x65\xd1\xaf\xe4\x49\ +\xde\x48\xc0\x09\x3a\x49\x93\x62\xb2\xf0\xb7\x5b\x35\x7a\x5a\xb3\ +\xf4\x8c\x6a\x88\x02\x71\x54\xa0\x5f\x6a\xb9\x07\xab\xe5\xc6\xeb\ +\x92\xe0\x4b\x4d\xe3\xa9\x00\xe2\xa5\x50\x43\x34\x8d\xc3\xbb\x13\ +\xdb\xfd\x1a\x4a\x19\xe0\x12\x20\x1e\x07\xff\xa9\x9a\xa2\x09\x89\ +\x7b\xeb\xc1\xaa\x45\xcf\xe8\x51\x3d\xae\xcd\x44\x81\xe8\x9a\xaa\ +\x25\xbe\x4c\x9e\x1d\xcf\xad\x49\xcb\x75\x0d\x9d\x08\x51\xd4\x47\ +\x17\xf8\x3e\x8d\x66\x3c\xb7\x8d\xba\x40\x7d\xe9\x50\x5c\x02\x44\ +\x47\x99\xce\xd3\x59\xaa\xa0\x79\x9d\x59\xa6\xdf\x68\x86\x96\x11\ +\x04\xc2\x2e\x57\x37\xf3\xa6\x9f\x27\xdb\x3a\xdd\xab\x02\x3a\x18\ +\xc2\xab\xa7\xce\xf7\x7c\x3d\xc0\x64\x6f\x2d\xba\x9c\x31\x14\x5c\ +\x02\x84\x51\x57\x9d\xa7\xf3\x3d\x5c\x95\x07\xdb\x7c\xa6\x7b\x34\ +\x43\x1b\x08\x02\xe1\x71\x96\x96\xfa\xbe\xfe\x4f\x72\xb7\x56\x2d\ +\xd3\x99\x74\x3a\x84\x41\xb6\xf6\xd6\x1c\x0e\xca\x00\xb6\xb7\x35\ +\x22\xf1\x6f\x93\x22\x60\x23\x74\x8f\xea\x39\x18\x03\x3b\x13\xb8\ +\x4f\xfb\xd2\x09\x11\x94\xeb\xb5\x8e\xc3\x30\xf0\x67\x03\xb7\xd2\ +\x11\xb9\x09\xe8\xb7\x1c\x8d\xd0\x23\x1a\x4c\x03\x86\xc2\x72\x9d\ +\xae\x77\x99\x58\x24\x9a\x57\xd0\x51\x34\x54\xd3\xf5\x2e\x87\x7f\ +\x68\xec\xa6\x37\x35\x83\x89\x45\xa3\xf9\x4b\x1a\x3d\xff\xa4\xdf\ +\xeb\x20\x9a\x2e\x64\xf6\xd5\xb9\xca\xd6\x6c\x82\xe0\x12\xc0\x4b\ +\x7b\xe8\x7e\x8d\x8b\xc0\xdf\xd9\xac\x46\x35\xaa\x49\x8d\x6a\x56\ +\xb5\xaa\x54\xa5\x2a\x55\xaa\x52\xd5\xda\xa2\xcd\x6a\xd5\x26\xa5\ +\xb4\xae\xcd\xbf\xef\xad\x2c\x15\x2b\x5b\xdd\xd4\x55\xfd\x54\xae\ +\x52\x95\xaa\x54\x65\x2a\x51\xae\xf2\x95\xa7\x7c\xe5\x2b\x37\x02\ +\xfb\xfd\xb2\xce\xd3\x52\x0e\x2b\x0a\x80\x17\x8a\x74\x85\xae\x52\ +\xf7\x10\xff\x85\x2b\xb4\x54\x95\xaa\x52\xe5\xd6\x43\xbe\x4a\xd5\ +\x4a\x99\x5b\xa8\xdf\xd6\x62\xf0\x75\x51\x28\xd7\xee\x1a\x10\xe2\ +\x0c\x6a\x74\x93\x6e\x67\x81\x31\x0a\x80\x6b\xbb\xeb\xc9\x90\x3e\ +\x72\x6a\xd1\x6c\xbd\xa3\xd7\xf4\xae\xea\xd5\xa0\x46\x35\x9a\x0f\ +\xfa\x1d\xb7\x56\xbe\xf2\x55\xa0\x02\x0d\xd2\x58\x8d\xd2\x11\xea\ +\x15\xca\x44\x3e\xd4\x24\x5e\x1c\x82\x3b\xb9\xa1\x5b\xc2\xa3\x56\ +\x5f\x69\xbe\x6e\xd7\x04\x15\x07\x9a\x4c\x77\x4d\xd0\x1d\x9a\xaf\ +\xd5\xda\x18\xb2\x91\x90\xe7\x2b\x8f\x8e\xcb\x19\x80\x0b\x7b\xe9\ +\x26\x4d\x0c\xc9\xdf\x52\xaf\x79\xfa\x48\x9f\x6a\xbe\x16\x84\xea\ +\x37\xae\x44\x43\xb5\x9f\xf6\xd3\x70\x8d\x08\xcd\x1b\x7b\xcf\xea\ +\x67\xfa\x94\x43\x0c\x36\xe7\x6b\x73\x48\x7e\xd3\x9e\xd2\x29\x2a\ +\x55\xf7\x10\xdf\x8c\xcb\x55\x77\x95\xea\x18\xfd\x2e\x24\xef\x44\ +\x6e\xd2\x3f\xd2\x81\x91\xb9\x5e\xba\x2b\xf0\xd7\x5f\x6b\xb4\x42\ +\x0f\xe9\x98\xc8\x65\x37\x56\xf7\x69\x99\x6a\xd4\x1a\x70\x82\xbf\ +\x0e\xe9\x7d\x0a\x84\xde\xe1\x9a\x1b\x68\xd7\xad\xd3\x73\xba\x42\ +\x07\x46\xfa\x02\xef\x40\x5d\xa1\x27\xb4\x31\xd0\x1c\xdf\xd4\x51\ +\x74\x66\x74\xd6\x15\xaa\x09\xb0\xd3\xbe\xaf\xf3\x55\xa2\xae\xb1\ +\x48\xb2\x50\x25\xfa\xae\x66\x07\x7a\x29\x70\x25\x1d\x1a\xe9\xeb\ +\xaa\x5b\x03\xea\xaa\x9b\xf5\x85\x6e\xd0\x6e\xb1\x4c\xb5\xa7\xfe\ +\x45\x0b\x02\xbb\xa3\x72\x97\xba\xd0\xb1\x91\x8e\x7d\x03\x3a\xf5\ +\x5f\xaa\xdb\x22\x31\xca\xd0\xe6\x28\xdd\xa2\x05\x01\x5d\x0a\x30\ +\x5f\x13\x76\xea\xa4\x40\x4e\xfd\x5f\xd1\xb1\xea\x99\x98\x95\x12\ +\xbb\xeb\x38\xfd\x35\x80\x94\x6b\x42\xf3\x38\x17\x21\xbd\x69\xf5\ +\x33\xdf\xa7\xb4\x58\xab\x87\x34\x2c\x91\x69\x0f\xd4\x7d\xfa\x4a\ +\xcd\x3e\x27\x7e\x75\xe2\x16\xa4\x45\x9a\x8a\xf4\xa0\xcf\x9d\xf1\ +\x03\x5d\xa9\xf2\x44\x67\xde\x57\x97\xeb\x0d\x9f\x53\x7f\x38\xe0\ +\xd1\x93\x08\xa5\xde\x3e\x5f\x99\xbe\xab\xb1\xea\x41\xec\x92\x8a\ +\x75\x90\x5e\xf5\x35\xfb\x2f\x54\x42\xec\x68\x6b\xb4\x8f\xcb\x78\ +\xd5\x6a\x8e\x0e\x27\xf2\xed\x1c\xa1\x37\xb4\xc5\xc7\xbb\x01\xa3\ +\x88\x1c\xdb\x4c\xf1\x6d\x6e\xbf\x16\xfd\x4e\x63\x09\x7c\x07\x0e\ +\xd7\xbd\x6a\xf2\xa9\x25\xd6\xea\x0c\x02\x87\x24\x5d\xef\x5b\xa7\ +\x7b\x4a\x43\x95\x4f\xe0\x1d\xc8\xd5\x60\x3d\xee\xdb\xda\xc3\x37\ +\x10\x78\xd2\xe5\xeb\x26\x5f\x3a\x5b\x83\xde\xd5\xc1\xc4\x9d\xa6\ +\x43\xf4\xae\x4f\x93\xad\xdf\x48\x41\x4e\xf6\xef\xcd\x74\x5f\xba\ +\xd9\x2c\x4d\x22\xec\x4e\x3a\x5e\xb3\x7c\x69\x9b\xfb\x58\x5c\x24\ +\xa9\xba\xf8\x32\x3e\xbd\x4a\x47\x33\x08\x35\xc3\xf6\x39\x5c\x4b\ +\x7c\x68\xa1\xd7\x55\x44\xd8\xc9\xb3\x8b\x9e\xf7\xe1\x5e\xf3\xaf\ +\x99\x97\xc6\x24\x47\x77\xfa\xf0\x2e\xe1\x4b\xda\x85\xa8\x93\x65\ +\x57\xbd\xe9\x79\xb7\x7a\x40\x7b\x13\xb4\x03\xfb\xe8\x3e\xcf\xdb\ +\xea\x0d\xed\x4e\xd0\xc9\xb1\x87\x16\x7a\xdc\xa1\x3e\xd4\x5e\x91\ +\x98\x46\x3b\x2a\xe7\x01\x7b\xe9\x23\x8f\x5b\x6c\x21\x25\x20\x29\ +\x06\x78\xdc\x99\x36\xea\x46\x42\xf6\xc0\xf5\x5a\xef\x69\xbb\x7d\ +\xa2\xef\x10\x72\xfc\x0d\xf3\xf8\xf0\x7f\x5a\x87\x11\xb2\x47\x46\ +\xe9\x49\x8f\x87\x08\x0f\x27\xe4\x78\xdb\x4b\xcb\x3d\x1d\xe7\xf7\ +\x43\xee\xf8\x7b\xaa\x50\x53\xd4\xe8\x61\x0b\x7e\xc9\x72\xe3\xf1\ +\xbe\xf5\xe7\xdd\x0b\x3f\xad\x7a\x95\xe9\x27\x7d\xd1\x5b\xaf\x7a\ +\xb8\x06\xc1\xa7\x31\x9d\x8f\x09\xda\x43\xf3\x3c\x9c\x75\xee\x0a\ +\x02\xf6\xd1\x25\xda\xe0\xe1\xbd\x80\x21\x04\x1c\xc7\xc3\x7f\xb1\ +\x67\x5d\x66\x6e\xa8\x57\xcb\x8b\xa7\xfe\x7a\xc7\xb3\xf6\x5c\xcc\ +\xed\xc0\xb8\xd9\x45\xaf\x7b\xd4\x59\xea\x75\x27\xf1\x06\xe4\x56\ +\xd5\x79\x36\x7f\x60\x5f\xe2\x8d\x8f\x22\xbd\xe0\x51\x47\x99\xaf\ +\xc9\xc4\x1b\xa0\x93\x34\xdf\xb3\xd1\x81\x5d\x89\x37\x1e\xf2\x35\ +\xc7\xa3\x4e\xf2\x38\x33\xcb\x04\xae\x44\x7f\xf4\x6c\x74\x60\x21\ +\xf1\xc6\xe1\xf0\x9f\xe1\x49\xf7\x68\xd4\x5d\x84\x1b\x12\xbf\xf2\ +\xe8\xe5\xe1\x07\x78\x59\x38\xfa\x7e\xe9\x49\xd7\xa8\xd4\x14\xa2\ +\x0d\x91\x93\xb5\xd2\xa3\xf9\x02\xc0\xe1\xdf\xce\x5d\xe2\x41\x44\ +\x1b\x32\xbb\xeb\x73\x4f\xda\xfa\x3f\x88\x36\xba\xce\xf2\x64\xd6\ +\xf9\x67\x08\x36\xa4\xbc\x18\x28\xdc\xa4\x33\x09\x36\x9a\x0e\xf4\ +\xe4\xe5\x91\x6b\x09\x36\xc4\x7e\xee\x41\x8b\xaf\xd7\x68\x82\x8d\ +\x9e\xde\x9e\x8c\x15\xe3\xa1\x5f\xd8\x4d\xf4\xe4\xfd\x4e\x9e\xf6\ +\x44\x4c\x37\x7d\xe6\xbc\x1b\xac\xd3\x41\x04\x1b\x01\x23\xb5\xd6\ +\x83\xd9\x02\x58\x4d\x28\x42\xb2\xf5\x07\xe7\x5d\xe0\x33\x8d\x20\ +\xd8\x88\xd8\x4f\x1f\x3b\x6f\xff\x47\x94\x4d\xb0\x51\x71\x8d\xf3\ +\xe6\x7f\x4d\xdd\x89\x35\x52\x67\x80\x2f\x3b\xef\x03\x57\x11\x6b\ +\x34\x9c\xe8\xbc\xe9\x9f\xa4\xfa\x47\x4e\x8e\x66\x3a\xef\x07\xc7\ +\x13\x6b\xf8\x8d\x72\xbe\xca\xdf\xef\x99\x36\x3a\x92\xba\xe8\x61\ +\xe7\x37\x03\xf7\x27\xd6\x70\xeb\xea\xfc\x25\xd1\xdb\x59\x38\x22\ +\xb2\x72\x75\xb3\xe3\xde\xf0\x1e\xaf\x08\x85\xdb\x3d\x8e\x1b\xfc\ +\x57\xca\x22\xd4\x48\xbb\xc3\x71\x8f\xe0\xe5\xef\x10\xfb\x89\xe3\ +\xc6\xbe\x85\x5f\xff\xc8\xcb\xd6\x8d\x8e\x7b\x05\x73\x3f\x85\xd4\ +\x78\xc7\x57\xff\x97\xf3\xeb\x1f\x13\x97\x3b\xbe\x13\x30\x8e\x48\ +\xc3\xa7\xb7\xde\x75\xda\xcc\xd3\x38\xfc\x63\xc4\xed\x1a\xd0\x73\ +\xd5\x9b\x48\xc3\xe6\x6e\xa7\x4d\x7c\x37\x0f\xfe\x62\x25\x4b\x77\ +\x3a\xed\x1f\xf7\x12\x69\xb8\x4c\x75\x7c\xa3\x87\x5f\xff\xf8\x99\ +\xe6\xb4\x8f\x5c\x4c\xa0\xe1\x31\x5c\x9b\x9d\x3e\xf7\xe7\xd6\x5f\ +\x1c\xe5\x38\x1d\x17\xb0\x59\x7b\x11\x69\x38\xe4\xe9\x59\x87\x0d\ +\x3b\x53\xdd\x88\x34\xa6\xba\xea\x29\x87\x3d\xe5\x2f\x2c\xff\x1a\ +\xbf\xd3\xff\xd9\x5c\xfb\xc7\xfc\x2c\xe0\x65\x2e\x03\xe2\x65\x90\ +\xd3\x65\xa1\x78\xe9\x33\xee\x7a\x38\x5d\x1e\x8e\x35\x84\x02\x56\ +\xe4\x70\xb9\xaf\xb5\x8c\xf4\x4e\x84\xfd\xb4\xce\xe1\xba\x10\xbc\ +\x27\x12\x28\x97\x2f\xfe\x1e\x48\x9c\x09\x31\xda\x61\xaf\xf9\x57\ +\xe2\x0c\xf2\xf4\xbf\xc6\x59\x43\x9e\x42\x9c\x09\x32\xc9\xe1\xb2\ +\xb0\xcc\x10\x1d\x98\xd7\x98\xea\x13\x81\x9f\x3b\xfe\x95\x30\x83\ +\xf1\x6f\x0e\x27\xfc\x40\xf2\xcc\xe2\x32\x20\xca\x86\x38\x3b\xfd\ +\x5f\x48\x98\x09\xf5\xb9\xb3\xd7\x83\x86\x12\xa6\xbf\x72\xf5\xa0\ +\xa3\xc6\x5b\xa5\x81\xc4\x99\x50\xbb\x3b\x5b\x50\xec\x41\x46\x8f\ +\xfa\xeb\x60\x67\x4b\x7c\x9e\x46\x98\x09\x36\x45\x8d\x8e\x7a\xd2\ +\xa1\x84\xe9\x27\x57\x95\x9b\x15\x7e\x93\xce\xd5\x5b\xa4\xcb\x89\ +\xd2\x3f\xb7\x38\x6a\xb4\x47\x89\x12\x7a\x8c\x95\x84\xa3\xc5\xd5\ +\x48\xae\x8f\xd4\x97\x30\xa1\x7e\xfa\xc4\xd1\x48\xd2\x7d\x08\xd3\ +\x7b\x39\x7a\xc0\x49\x73\xd5\x69\x12\x61\x42\x92\x34\x59\x75\x4e\ +\xfa\xd4\x6f\x78\x3f\xd0\x7b\xfb\xab\xc5\x49\x63\xdd\x41\x94\xf8\ +\x9b\xbb\x9c\xf4\xa9\x66\x8d\x24\x4a\xaf\xb9\x99\xf9\x6f\x2e\x41\ +\xe2\x1b\xdc\xac\x27\xf1\x3e\x41\x7a\xeb\x6c\x47\xe3\xb7\x77\x23\ +\x4a\x7c\xc3\xae\xda\xe8\xa4\x6f\x7d\x8f\x28\xbd\xd3\x53\xcb\x1c\ +\x34\x51\x8b\x2e\x23\x4a\x7c\xcb\x65\x4e\x2e\x2e\x97\xb2\x8c\xac\ +\x77\xfe\x49\xad\xbc\xbc\x01\xcf\xcc\x76\xd0\xbb\x5a\xf5\x33\x82\ +\xf4\x46\x1f\x27\x73\xb9\x34\xab\x0f\x51\x62\x07\x67\x98\xcd\x0e\ +\x7a\xd8\x27\xf4\x30\xaf\x4e\xd1\x5c\x5c\xa3\x9d\x41\x90\xf0\xf8\ +\x1e\xd3\x8f\x08\xd2\xbd\x3c\x27\xd5\xf9\x69\x15\x12\x25\x76\xa8\ +\x50\x33\x9d\xbc\x61\xc2\x78\x00\xe7\x5c\x8c\xd9\xde\xa0\x83\x09\ +\x12\x1d\x3a\xd4\xc9\x6b\xe6\xac\x21\xec\xd8\x40\x6d\x70\xd0\x2c\ +\xbf\x24\x48\xec\x94\x8b\xd5\x84\xd7\x6b\x77\x82\x74\xe9\xdf\x1d\ +\x34\xca\x07\xc4\x88\xb4\x7c\xe4\xa0\xb7\x5d\x47\x8c\xee\xf4\xd5\ +\x62\x07\x4d\xc2\x52\x4e\x48\xcf\x70\x07\xbd\xed\x0b\xed\x42\x90\ +\xae\x5c\xe2\xa0\x41\xee\x67\xce\x16\xa4\x29\x57\xbf\x61\xdd\xa0\ +\x30\xb1\xbf\xab\x55\xa3\xbd\x89\x11\x69\xdb\xd7\xc1\xc0\xe0\x2d\ +\xc4\xe8\xc6\x55\x0e\xaa\xf1\xaf\x88\x11\x9d\x72\xa7\x83\x5e\xf7\ +\x53\x62\xb4\x2b\x72\x30\xfe\xff\x4b\x4e\xff\xd1\x49\xd9\x5a\xe5\ +\xe0\xbd\x80\x2e\x04\x69\x75\x8e\x9a\xcc\x0d\x71\x34\x31\xa2\xd3\ +\xc6\x9b\xfb\x5d\x93\xce\x25\x46\x9b\x2e\x7a\x86\xd1\x7f\x88\x6c\ +\xdf\x7b\x8a\xbe\x67\x53\x61\x7e\xff\xaf\x5e\x13\x89\x11\x19\x39\ +\x59\x0d\xe6\x57\xcf\x59\x32\xc4\x64\xba\xb9\x06\xbf\x43\x88\xc8\ +\x98\x7d\xfe\xa9\xbb\x09\x31\x73\x45\x0e\xee\xc4\x32\xfa\x1f\x99\ +\x1b\xe3\xa0\x07\x76\x25\xc6\x4c\xdd\x66\x0e\xff\x09\x42\x84\x89\ +\xfd\xed\xc0\x5b\x09\x31\x33\xdd\xf5\xa5\xf9\x0a\x6c\x08\x31\xc2\ +\x64\x88\xf9\x35\xf4\x15\xea\x46\x8c\x3b\x92\xdd\xc1\xff\x37\xc1\ +\x3c\x9a\xfa\x77\x5a\x4a\xc4\x30\x59\xa6\xff\x35\x7e\x43\x5f\x4d\ +\x20\xc6\xce\xcb\xd3\x43\xc6\xca\x5b\xab\xb1\xc4\x08\xb3\x23\xb4\ +\xc5\xd8\x13\x1f\x52\x1e\x31\x76\x56\x89\x36\x19\x63\x9f\x43\x88\ +\x70\xe2\x4d\xf3\x9b\x28\x2c\x42\xd7\x69\x17\x9a\x6f\xbe\xf0\xfb\ +\x0f\x37\xc6\x99\xfb\xe2\x0f\x08\xb1\xb3\x16\x19\x23\x7f\x9b\x08\ +\xe1\xcc\x3b\xe6\xd9\x01\xd0\x29\x07\x9b\x6b\xee\x61\x84\x08\x67\ +\xec\xe3\x01\x46\x11\x62\x67\xfc\xc9\x18\xf7\x9b\x2a\x26\x44\x38\ +\x53\xac\xb9\xc6\x1e\xf9\x07\x42\x4c\x5f\x89\xbe\x32\x3e\xff\xe7\ +\x4d\x6c\xb8\xf5\x8f\xc6\x85\xc3\xd6\xb0\x5c\x48\xfa\xce\x30\xbe\ +\x84\xb1\x56\x65\x84\x08\xa7\xca\xb5\xd6\xd4\x27\x1b\x74\x3a\x21\ +\x7e\x5b\x7b\x03\x81\x72\x74\xa4\xf2\x4d\xdf\x3a\x4b\x95\x44\x0b\ +\xa7\x56\xe9\x2f\xa6\xcf\xe7\x6b\x3c\xd3\xd2\xa4\xa7\x97\x96\x18\ +\xaf\xb7\x06\x11\x22\x9c\x1b\x62\x7e\x12\xd0\x93\x10\xd3\x61\xbd\ +\xe3\xfa\x0a\x11\xc2\x13\xd6\xd5\x83\x0f\x22\xc2\x74\xcc\x30\xc6\ +\x7c\x0c\x11\xc2\x13\x27\x18\x7b\xe6\xaf\x89\x30\x1d\x0d\xc6\x13\ +\xad\x1e\x44\x08\x4f\xf4\x30\x0e\x4f\xab\x23\xc2\xed\x7d\xfb\x26\ +\xe0\x44\xe3\x0d\xc0\x99\xda\x48\xac\xf0\xc4\x46\xcd\x34\x7d\xbe\ +\x50\xc7\x11\xe2\xce\x0a\xc0\x05\xa6\xef\xab\xd5\xd3\x4a\x11\x2b\ +\x3c\x91\xd2\x2c\xd5\x9a\xbe\xe1\x02\x42\xec\x58\x37\x55\x33\xe6\ +\x1a\x21\xf6\x85\xa9\x7f\x56\x31\x41\x58\xc7\x67\x00\x07\xab\xc8\ +\xf4\x7d\xbf\x27\x52\x78\xea\x8f\xc6\x1f\x38\xe6\xa8\xec\xb0\x00\ +\x1c\x64\x2c\x00\xcc\xc1\x0a\x6f\xfd\x8f\xe9\xd3\x45\x3c\x0a\xec\ +\xa8\x00\x74\xd1\x21\xa6\x6f\x7b\x4b\xd5\x44\x0a\x4f\xad\x34\x4e\ +\x34\x7f\x10\x4b\x85\xec\x58\x5f\xad\x31\x5d\x61\x31\xda\x1a\xde\ +\x3b\xd3\x78\x17\x80\xd9\x81\x76\xe8\x40\x53\xb4\x9b\x88\x16\x3e\ +\xbc\xa3\x07\x6c\x00\x00\x03\x16\x49\x44\x41\x54\x28\x51\xad\xa9\ +\x9f\x1e\x40\x84\x3b\xba\x04\xb0\xfd\x82\xbf\xa8\x4d\x04\x0a\xcf\ +\x6d\xd2\x8b\xa6\xcf\x7f\x97\x08\x77\x54\x00\x4e\x35\x7c\x53\x4a\ +\x2f\xab\x9e\x40\xe1\xb9\x3a\xbd\x6c\x1a\x6b\xc2\x85\xea\x0e\xf4\ +\x31\x9d\x58\x6d\x60\xd2\x25\xf8\xe4\x20\xd5\x98\xfa\x2a\x6f\x05\ +\xb6\x7b\x06\x70\x8a\xe9\x9b\x36\xea\x5d\xe2\x84\x2f\xde\x56\x0d\ +\x17\x01\xee\x0b\xc0\x89\xa6\x6f\x7a\x9e\x21\xc0\xf0\x49\xab\xf1\ +\x95\x73\x16\xac\x6f\xd7\x4a\xd3\x69\xd5\x51\x04\x08\xdf\x1c\x67\ +\xea\xab\x2b\x09\xf0\xdb\x46\x68\xbd\x21\xd2\x26\x02\x84\xcf\x67\ +\x01\x99\xf7\xd6\x75\x1a\x4e\x80\xdb\x5f\x02\xec\x63\x1a\x04\xfc\ +\x38\x51\xc2\x57\x4f\x1a\x3e\x5b\xa4\x7d\x08\x70\xfb\x02\x30\xcc\ +\xb4\x80\x22\x2f\x01\xc1\x5f\x96\x1e\x97\xaf\xbd\x08\xf0\x9b\x05\ +\xa0\x50\x15\x86\x6f\xa9\xd7\x5b\x44\x09\x5f\xcd\x51\xa3\xe1\xd3\ +\x15\x2a\x20\xc2\xb6\x05\xa0\x8b\xa9\x00\x7c\xa8\x2d\x44\x09\x5f\ +\x6d\xd1\x3c\x53\x01\x60\x5e\x80\x6f\x14\x80\x22\x53\x01\x98\x4f\ +\x01\x80\xcf\x6a\x35\x9f\x02\xe0\xae\x00\x0c\x69\x77\x89\x90\xf4\ +\xa4\x34\x5f\xcd\x44\x09\x5f\x35\x9b\x0a\x40\xae\x06\x13\x61\xdb\ +\x02\x70\xb8\xe9\x64\x6c\x3e\x41\xc2\x77\x1f\x99\xce\x3b\xc7\x12\ +\x60\xdb\x02\x70\xa8\xa9\x00\x7c\x44\x90\x08\xa0\x00\x58\x26\x08\ +\x3d\x94\x00\xbf\x79\x45\x95\xf9\xb0\x0a\x0e\x7f\x04\xe3\x63\x43\ +\xaf\x65\xf2\xfa\x36\x67\x00\xbb\x98\x6e\x89\xbc\x48\x8c\x08\xc4\ +\x4b\x86\xcf\x16\xf3\x4e\xe0\xdf\x0b\xc0\x81\xa6\xef\xf8\x3f\x62\ +\x44\x20\x9e\x35\x7d\x7a\x34\x01\x6e\x2b\x00\xb6\x37\xf9\x5f\x23\ +\x46\x04\xc2\xd6\xf3\x98\x1a\xcc\x49\x01\xd8\xc8\x44\x60\x08\x48\ +\x8d\xe9\x36\x20\x05\xe0\x6f\x05\xc0\xf2\x6e\xd4\x1b\x84\x88\xc0\ +\xbc\x69\xf8\xec\x08\xe2\xfb\xba\x00\xf4\x30\xbd\x07\xf8\x26\x21\ +\x22\x30\x96\x9f\x9f\x62\x15\x13\x60\xb6\xa4\x01\xa6\xf5\x80\xe7\ +\x10\x22\x02\xf3\xba\xe1\xb3\x79\xea\x4f\x80\xd9\x92\xca\x4c\x05\ +\xe0\x13\x42\x44\x60\x2c\x63\x50\xf2\x55\x4e\x80\xd9\x92\xfa\x1b\ +\x0a\xc0\x72\x35\x10\x22\x02\xd3\x60\x98\xde\xab\x40\x65\x04\xf8\ +\xf5\x19\x40\xe6\x53\x81\x2c\xa3\x00\x20\x40\xf5\x5a\xca\x25\x80\ +\xad\x00\x64\x99\xea\x60\xa5\x69\x5a\x06\xc0\xa6\x51\x95\x86\x4f\ +\x97\x12\x60\xb6\xf2\x4c\x2b\xfa\x55\x51\x00\x10\x68\x01\xa8\x32\ +\x7c\xba\xaf\x69\x1a\x3c\x0a\x80\xa4\x4a\x56\x03\x40\x80\x52\xa6\ +\x33\x80\x12\x0a\x80\xad\x00\x34\x9b\xea\x2f\x60\x57\x6d\x98\x8c\ +\x86\x33\x00\x63\x01\x68\xa4\x00\x20\x60\x96\xbb\x50\x14\x00\x65\ +\x2b\x57\xbd\x29\x00\x88\xac\x2a\xc3\xa2\x34\xbd\x29\x00\xd9\xca\ +\x55\xa1\xa1\x00\x54\xd2\x03\x11\xd9\x33\x80\xae\xca\xa5\x00\x58\ +\xa6\x45\x68\x52\x35\x3d\x10\x01\xdf\x03\xb0\x2c\x4b\x57\x4c\x01\ +\xe8\x6f\x2a\x00\x40\xb0\x5a\x4d\xbd\xb0\x8c\x02\xd0\xcf\xf0\xe9\ +\x35\xf4\x3f\x04\xee\x2b\x0a\x80\xa5\x00\x58\x46\x01\xac\xa2\xf7\ +\x21\x04\x77\x01\x32\xb7\x0b\x05\xa0\xb7\xe1\xd3\x3c\x03\x40\xf0\ +\x2c\xbd\xb0\x37\x05\x80\x02\x80\xe4\x9e\x01\x50\x00\x28\x00\x88\ +\x38\xcb\x93\xa8\x3e\x14\x80\x9e\x14\x00\x70\x06\x90\xdc\x02\x50\ +\x44\x01\x40\x62\xef\x01\x14\x51\x00\xba\x19\x3e\xbd\x99\xde\x87\ +\xc0\xd5\x52\x00\x82\x3a\x03\x60\x36\x20\x04\xaf\x9e\x02\x10\x54\ +\x01\xa8\xa3\xf7\x21\xd2\x05\xa0\x2b\x05\xa0\x4b\x40\xd1\x03\x6e\ +\xd4\x51\x00\x2c\x05\x20\x97\x02\x80\xc4\x9e\x01\xe4\x50\x00\x2c\ +\x05\x80\x7b\x00\x88\x76\x01\xe0\x75\x60\x53\x0d\xe4\x6d\x40\x04\ +\xaf\x91\x02\x10\x54\x01\x00\xa2\x2d\xf1\x05\xe0\xff\x03\xb3\xeb\ +\x81\x95\x88\x1d\xd8\x7b\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\ +\x60\x82\ +\x00\x00\x06\x61\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1b\x05\x03\x33\x13\x8f\ +\xa9\x44\x00\x00\x05\x2f\x49\x44\x41\x54\x78\xda\xed\x9c\x4d\x6c\ +\x15\x55\x14\xc7\x7f\xcf\x8f\x1a\x1a\x90\xb6\x31\xc5\xf8\xb1\xa9\ +\xd1\x2a\xc1\x94\xa6\xb8\x57\x63\x62\x28\xd4\x46\x13\x01\x61\xcf\ +\xc2\x8d\x14\x35\xba\xd0\x80\x44\x63\x35\x12\x57\x6e\xfd\x58\x1a\ +\x2d\x56\xc1\x58\xab\x68\xd5\xb5\x0b\x29\x1a\x5f\xb1\x7c\x98\x58\ +\x2c\x2d\xf4\xbd\xb6\x26\x52\xe5\x3d\x17\x2f\x35\x85\x9e\x79\x73\ +\xef\xcc\xbc\x7b\xef\xdc\x99\xdf\xdd\x4d\xfb\xce\x9c\xf3\x3f\x67\ +\xee\x9d\xb9\x73\xef\x40\x4e\x4e\x4e\x4e\x4e\x4e\x92\xf4\x72\x9c\ +\x32\x97\xf8\x92\xad\xb6\x5d\x31\x4f\x3b\x1f\x53\x5d\xd1\x5e\xb1\ +\xed\x90\x59\x76\x32\x73\x55\xf8\x55\xaa\xf4\xda\x76\xca\x14\xd7\ +\xe6\x7e\xb9\x8d\xda\x76\xcc\x0c\x4f\x72\x41\x0c\xbf\xca\x45\xdb\ +\xae\x35\x9e\x76\x3e\x0a\x08\xbe\xd6\x3c\x27\x38\xf7\x19\x10\x20\ +\x2c\xf7\x9e\x0b\x10\x9e\x7b\x8f\x05\x50\xcb\xbd\xb7\x02\xa8\xe6\ +\xde\x4b\x01\x74\x72\xef\xa1\x00\x7a\xb9\xf7\x4c\x00\xfd\xdc\x7b\ +\x25\x80\x74\x9f\x7f\x6d\x1b\xf2\x55\x00\x95\xdc\x5f\x62\x2f\xf8\ +\x29\x80\xca\x75\x7f\x94\xdb\xc0\x47\x01\xd4\x73\x8f\x8f\x02\xe8\ +\xe4\xde\x3b\x01\x74\x73\xef\x99\x00\xfa\xb9\xf7\x48\x80\x68\xb9\ +\xf7\x46\x80\xa8\xb9\xf7\x42\x80\x38\xb9\xf7\x40\x80\x78\xb9\x4f\ +\xb9\x00\xf1\x73\x9f\x52\x01\x0a\x00\xec\xe0\x1d\x6e\x09\xf9\xcf\ +\x23\x3c\xcd\xb4\x82\x00\x8d\x61\x89\xbf\x98\x63\x91\x45\x7e\xa3\ +\x48\x91\x09\x7e\xe5\x9f\xa4\x8c\xbf\x1a\x9a\xfb\x19\x76\x29\xda\ +\x0a\xb3\x94\x5c\x5b\xe4\x0b\x9e\x67\x0b\xd7\xc7\x0d\x7f\x5b\xe8\ +\xa9\x86\xd8\xa0\x6c\xcd\x9c\x00\xcb\x6d\x9a\xb7\xe9\x8e\x23\xc0\ +\xf1\x84\x72\x6f\x4b\x80\x5a\x1b\x67\x80\xb5\xd1\x04\x28\xd5\x31\ +\x1b\xd6\xe7\xbb\x23\x40\x95\x2a\x65\x06\x69\xd3\x0d\xff\xba\xba\ +\x7f\x5d\xe2\x4a\x34\x55\xad\x70\x33\x2f\x70\x86\x43\x34\xeb\xfd\ +\xec\x6b\x2f\x2e\x81\x95\xed\x0c\x8f\xe9\xb8\xdc\x1b\x6a\xd0\xed\ +\x4e\x50\x6e\x9f\x71\xa7\xba\x04\x87\x42\xcd\xb9\x38\x0c\x86\xb5\ +\x59\xb6\xa9\x4b\xb0\x43\x69\xc2\x53\xa5\x0e\x6c\x87\xbd\xb2\x55\ +\x78\x93\x1b\x54\x25\x08\x5a\xde\xa0\x5b\x07\xd2\xef\x92\xa0\x89\ +\x56\x3a\xb8\x9f\x27\x78\x91\xf7\x18\xa7\xa2\x28\xc2\xf7\xb4\xaa\ +\x9f\x44\x6d\xe2\xbb\x7e\x1d\x34\x4a\x80\xd5\x09\xdb\xc9\xbb\x75\ +\x87\xf0\xe5\x76\x92\x3b\x74\xcc\xc6\xad\x03\x53\x02\xd4\x58\xc3\ +\x2e\x46\x42\xab\xe1\x1c\xf7\xea\x18\x8d\x57\x07\x66\x05\xa8\xd1\ +\xc5\x87\x5c\x09\x49\x9a\x96\x04\x71\xea\xc0\x86\x00\x00\xf7\xf1\ +\x55\x48\x15\x68\x5c\x08\xa0\x36\x39\x72\xcc\xb1\x49\xd1\x3e\xce\ +\xd5\xf1\xf6\x67\xdd\xdb\xe4\x68\x75\x60\x53\x00\x68\xe5\x93\xba\ +\x23\xc2\x8d\xba\x06\xf5\xeb\xc0\xae\x00\x00\x7b\xb9\x1c\xe8\xeb\ +\x1b\xfa\xe6\x54\xa6\xca\xe6\x1c\x7b\x35\xf6\x60\xe0\xf0\x58\xa1\ +\x3f\x8a\x41\x9d\x71\xc1\x05\x01\xa0\x9b\xf3\x81\x17\xad\x66\x67\ +\x58\x43\xbd\x3f\x70\x43\x00\xb8\x9b\x3f\x03\xfc\xfc\x34\xaa\xc9\ +\xb4\x2d\x90\xe8\xa6\x1c\xe0\x65\x5f\x54\x93\x2a\x75\xe0\x8e\x00\ +\xf0\x50\x40\x77\x78\x9a\x35\xd1\x8d\xaa\xd4\x81\x2b\x02\xc0\x40\ +\x80\x47\xb1\x76\x31\xa4\x69\x99\x5c\x81\x23\xa2\x47\x65\x9d\x67\ +\x44\x09\xbd\x3a\xb0\x49\x2b\x7f\x88\x3e\xbd\x1c\xd7\x70\x7a\x96\ +\xca\x3e\x25\xfa\x74\x91\x75\xf1\x4d\xab\xd6\x81\x6d\xe4\x69\xdf\ +\x81\x24\x4c\xab\x8d\x0b\xb6\xd9\x28\x3e\x2c\x9f\x48\xca\x7c\x1a\ +\x36\x4c\xc8\x97\xeb\xe6\xa4\xcc\xbb\xbf\x65\xa6\x4b\x9c\x35\x3a\ +\x9c\xe4\x29\x82\xfb\x03\x37\x36\x4d\x8d\x08\x9e\x9d\x0f\x79\x2f\ +\xa6\x89\xdb\xdb\xe6\xe4\xb1\x20\xd6\x5b\x65\x89\xd5\x75\x50\x71\ +\x64\xfb\x6c\xb3\xf8\x64\xf0\x6c\xf2\x27\xba\xba\x0e\x2a\x1c\xb4\ +\x1d\xf9\xff\xbc\x2f\x08\xf0\x79\x63\x4e\xb5\x95\x51\x4a\xcc\x30\ +\xe2\x48\xf6\x6b\x48\x17\xc1\xbc\xfe\x24\x59\x7a\x69\x17\x47\x82\ +\x8d\x89\xf6\x83\x4e\x73\x81\x5f\x84\xa3\x9d\xd9\x11\x00\x7e\xcc\ +\xba\x00\xc5\x5c\x80\xd5\x74\x64\x49\x80\x49\xe1\x58\x4b\x96\x04\ +\x28\x0b\xc7\xd6\x65\x49\x80\x05\x49\x80\x82\x6d\xaf\x0c\x72\x13\ +\x7f\xaf\x3a\x76\x39\x4b\x15\x20\x92\x25\x01\xa4\x59\xc0\x85\x5c\ +\x00\xdb\x5e\x19\x64\x7d\xd6\x05\xb8\x4b\x38\x56\xca\x92\x00\xd2\ +\x42\xa9\xd3\x59\x12\xa0\x53\x38\x56\xcc\x92\x00\x3d\x92\x00\xb6\ +\x9d\x32\xc7\x06\x79\x42\xc4\xb6\x5b\xe6\xd8\x2d\x4f\x89\x65\xe7\ +\x12\x78\x54\x38\xf6\x43\x72\x5b\xef\x5c\xa7\x99\x79\x33\xd3\xe2\ +\xae\xb2\xa7\xb1\x6f\x07\xdd\x67\x54\x08\x7f\x2a\x3b\x4f\x42\x9b\ +\xc5\x11\xe0\x2d\xdb\x6e\x99\x43\x5e\xbe\xd7\x65\xdb\x2d\x53\x6c\ +\x12\x17\x48\xfc\x64\xdb\x2d\x53\x14\xf8\x46\xcc\xff\x3e\xdb\x8e\ +\x99\x42\xee\xff\x67\x93\x58\x24\x95\x06\xda\x98\x12\x05\x78\xc9\ +\xb6\x63\x66\x28\x30\x2c\x86\x5f\xa6\xc5\xb6\x6b\x66\x78\x2e\x60\ +\xd9\xce\x01\xdb\x8e\x99\xe1\x11\x96\xc4\xf0\x27\xe3\x2c\x96\x4e\ +\x0f\x3d\xe2\xdd\x7f\x95\x2a\xdb\x6d\xbb\x66\x82\x7b\x98\x0e\x08\ +\x7f\xd8\xb6\x6b\x26\xe8\x09\x0c\x3f\xe2\x96\x99\x74\xf1\x70\xe0\ +\x5e\x91\x8a\xde\xa7\x15\xd2\x48\x81\x67\xea\x6c\x9b\x7b\xdd\xb6\ +\x7b\x8d\xa6\x8d\xa3\x81\xc1\x57\x19\x53\xff\x9a\x40\x3a\xe9\xe3\ +\xf7\x3a\xe1\x9f\xd4\xff\xc2\x4c\x9a\xd8\x14\xf0\xc8\xb3\xdc\xce\ +\x72\xbb\x6d\x17\x1b\x47\x37\x43\xc9\x6e\x9f\x4f\x0f\xcd\xec\x61\ +\x34\xf4\x03\x0a\x67\x7d\x0c\xff\x56\x76\xf3\x41\xe0\xbd\xde\xca\ +\x36\x5e\xbf\xf8\xd3\xb2\x44\xa6\x89\xb5\xb4\xb0\x9e\x0e\x3a\xe9\ +\x64\x8b\xf2\x1b\x9d\xef\x78\x9c\x39\xdb\xce\xc7\xa3\x9f\x31\x16\ +\x15\x32\x2d\xdd\xf6\x0c\xa6\x7f\xe0\x1b\x8c\x14\x7a\xad\xe3\x73\ +\x69\xad\x7a\x44\xfa\x23\x87\x3f\xec\xc7\x3d\xff\x58\xa4\xe0\x27\ +\xfd\x79\xe0\x5d\xd0\x0e\xbe\xc4\x01\x9f\xa6\x3b\xf4\x04\x98\xe5\ +\x60\xdc\x4d\xd1\xae\xf1\xad\x72\xf0\x27\xd8\x17\xf5\x93\x9a\x2e\ +\xd3\xa7\x10\xfa\x14\x87\x7d\x7e\xc9\xf5\x5a\x60\xe0\xf3\x1c\x63\ +\x3f\xdd\x71\xdf\xf0\xba\x7f\x27\xb8\x9d\xfd\x3c\x40\x13\x8b\x94\ +\x58\x60\x81\x53\x4c\x30\x41\x91\x22\xff\xda\x76\x2d\x27\x27\x27\ +\x27\x27\xed\xfc\x07\xdf\x4c\xa7\x34\x46\xdd\x51\x80\x00\x00\x00\ +\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x63\x72\x65\x61\x74\x65\ +\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x37\x54\x30\x35\x3a\x30\ +\x33\x3a\x35\x31\x2b\x30\x30\x3a\x30\x30\x9e\x80\x85\x29\x00\x00\ +\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x6d\x6f\x64\x69\x66\ +\x79\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x37\x54\x30\x35\x3a\ +\x30\x33\x3a\x35\x31\x2b\x30\x30\x3a\x30\x30\xef\xdd\x3d\x95\x00\ +\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\ +\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\ +\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\ +\x00\x00\x24\x36\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x02\x00\x00\x00\x02\x00\x08\x04\x00\x00\x00\x5e\x71\x1c\x71\ +\x00\x00\x02\xe7\x7a\x54\x58\x74\x52\x61\x77\x20\x70\x72\x6f\x66\ +\x69\x6c\x65\x20\x74\x79\x70\x65\x20\x65\x78\x69\x66\x00\x00\x78\ +\xda\xed\x97\x5b\x92\x1b\x21\x0c\x45\xff\x59\x45\x96\x80\x24\x84\ +\xc4\x72\x68\x1e\x55\xd9\x41\x96\x9f\x0b\xdd\xf6\x78\x1e\x49\x66\ +\x2a\xf9\x4a\xb9\xa9\x86\xb6\x00\x49\x7d\x0f\x60\x3b\x8c\x1f\xdf\ +\x67\xf8\x86\x8b\x4a\xe6\x90\xd4\x3c\x97\x9c\x23\xae\x54\x52\xe1\ +\x8a\x07\x8f\xe7\x55\x76\x4d\x31\xed\xfa\x76\xf1\x65\x7d\x65\x0f\ +\xf7\x0e\x86\x49\xd0\xca\xd9\x61\xf5\x6c\xa9\xc2\xae\x2f\x13\x6e\ +\x31\xe8\x78\x6d\x0f\x7e\xf5\xb0\x5f\x8e\x6e\x91\x2f\x87\xb2\x22\ +\xaf\x50\xfd\x31\x49\xd8\xf9\xb4\x53\xba\x1c\x95\x71\x3e\xe4\xe2\ +\xf6\x98\xea\x71\xe6\x19\xdb\x35\x70\xa7\x72\xdd\xc7\xe5\x74\x67\ +\x14\xcf\xcf\xe1\xd1\x90\x0c\x2a\x75\x45\x20\x61\x1e\x42\x12\x77\ +\xed\x67\x06\x72\xde\x15\xb7\xa0\x66\x31\x8c\x43\xc6\x78\x16\xc9\ +\x01\x8d\x4a\xbc\x32\x81\x20\xaf\x5e\xef\x45\xe0\x47\x81\xde\x8a\ +\x4f\xa7\xd8\xf4\x71\xc7\x5b\xf1\xb9\x5e\x76\x79\xa3\x65\xbe\x51\ +\xcb\x1f\x77\x90\x7e\x2c\xfe\x96\xf8\x21\xb0\xdc\x33\xe2\xd7\x1d\ +\x26\x37\x57\xef\x45\x9e\xb3\xfb\x9c\xe3\x7c\xbb\x9a\x32\x14\xcd\ +\xd7\x8a\xda\x62\xd3\xcd\x0d\x06\x1e\x90\x5c\xf6\xb4\x8c\x62\xb8\ +\x15\xcf\xb6\x4b\x41\xf1\x58\x63\x03\xf2\x1e\x5b\x3c\x50\x1a\x15\ +\x62\x50\x99\x81\x12\x75\xaa\x34\x69\xec\xb6\x51\x43\x8a\x89\x07\ +\x83\x09\x33\x37\x96\x6d\x73\x30\x2a\xdc\x36\xa5\xb4\x0a\x4d\x36\ +\x29\xd2\xc5\xc1\xaf\xf1\x08\x40\x97\x84\xef\xb9\xd0\x8e\x5b\x76\ +\xbc\x46\x8e\xc8\x9d\x30\x94\x09\xce\x08\x53\x7e\x59\xc2\xef\x3a\ +\xbf\x52\xc2\x9c\x6d\x49\x44\xd1\xef\x5a\x21\x2f\x5e\xeb\x1a\x69\ +\x2c\x72\xab\xc6\x28\x00\xa1\x79\x71\xd3\x2d\xf0\xad\xdc\x37\x6d\ +\x7c\x00\x2b\x20\xa8\x5b\x66\xc7\x0b\xd6\x78\x9c\x2e\x0e\xa5\x97\ +\xb5\x25\x9b\xb3\x60\x9c\xa2\x3d\xb7\x10\x05\xeb\x97\x03\x48\x84\ +\xd8\x8a\x64\x48\x40\x20\x66\x12\xa5\x4c\xd1\x98\x8d\x08\x3a\x3a\ +\x00\x55\x64\xce\x92\xf8\x00\x01\x52\xe5\x8e\x24\x39\x61\x87\x70\ +\x30\x76\x5e\xb1\x31\xc7\x68\x8f\x65\xe5\xcc\xcb\x8c\xb3\x09\x20\ +\x54\xb2\x18\xd8\x14\xa9\x80\x95\x92\x62\xfd\x58\x72\xac\xa1\xaa\ +\xa2\x49\x55\xb3\x9a\x7a\xd0\xa2\x35\x4b\x4e\x59\x73\xce\x96\xd7\ +\x21\x57\x4d\x2c\x99\x5a\x36\x33\xb7\x62\xd5\xc5\x93\xab\x67\x37\ +\x77\x2f\x5e\x0b\x17\xc1\x19\xa8\x25\x17\x2b\x5e\x4a\xa9\x95\x43\ +\x45\xa0\x0a\x5f\x15\xe3\x2b\x2c\x07\x1f\x72\xa4\x43\x8f\x7c\xd8\ +\xe1\x47\x39\x6a\xc3\xf2\x69\xa9\x69\xcb\xcd\x9a\xb7\xd2\x6a\xe7\ +\x2e\x1d\xc7\x44\xcf\xdd\xba\xf7\xd2\xeb\xa0\x30\x70\x52\x8c\x34\ +\x74\xe4\x61\xc3\x47\x19\x75\x62\xad\x4d\x99\x69\xea\xcc\xd3\xa6\ +\xcf\x32\xeb\x9d\xda\x45\xf5\x5d\xf9\x02\x35\xba\xa8\xf1\x26\xb5\ +\xc6\xd9\x9d\x1a\xac\xc1\xec\xe6\x82\xd6\x71\xa2\x8b\x19\x88\x71\ +\x22\x10\xb7\x45\x00\x0b\x9a\x17\xb3\xe8\x94\x12\x2f\x72\x8b\x59\ +\x2c\x8c\x4d\xa1\x8c\x24\x75\xb1\x09\x9d\x16\x31\x20\x4c\x83\x58\ +\x27\xdd\xd9\xbd\x90\xfb\x14\xb7\xa0\xfe\x29\x6e\xfc\x27\x72\x61\ +\xa1\xfb\x17\xe4\x02\xd0\xbd\xe7\xf6\x01\xb5\xbe\xbe\xe7\xda\x26\ +\x76\xee\xc2\xa5\x69\x14\xec\x3e\xf4\x0f\xaf\x81\xbd\xae\x2f\xb5\ +\xfa\xb7\xed\xd3\xd1\xd3\xd1\xd3\xd1\xd3\xd1\xd3\xd1\xd3\xd1\xd3\ +\xd1\x7f\xe0\x68\xe2\xc7\x03\xfe\xc4\x86\x9f\x6c\xa8\x9e\x2e\x5e\ +\x1e\x3b\x33\x00\x00\x01\x23\x69\x43\x43\x50\x49\x43\x43\x20\x70\ +\x72\x6f\x66\x69\x6c\x65\x00\x00\x28\x91\x9d\x90\xbf\x4a\xc3\x50\ +\x14\xc6\x7f\x69\x8b\x16\xd1\x49\x71\xf0\x0f\x64\x70\x2d\xb8\xd8\ +\xc9\xa5\x2a\x04\x41\x21\xc6\x0a\x46\xa7\x34\x49\xb1\x98\xc4\x90\ +\xa4\x14\xdf\xc0\x37\xd1\x87\xe9\x20\x08\x3e\x81\x4f\xa0\xe0\xec\ +\x77\xa3\x83\x83\x59\xbc\x70\xf8\x7e\x1c\xce\xf9\xbe\x7b\x2f\xb4\ +\xec\x24\x4c\xcb\xce\x2e\xa4\x59\x55\x38\xde\xc0\xbf\xf4\xaf\xec\ +\xc5\x37\xda\x6c\xd2\x61\x9b\x6e\x10\x96\xf9\xc0\x75\x4f\x68\x3c\ +\x9f\xaf\x58\x46\x5f\x7a\xc6\xab\x79\xee\xcf\xb3\x10\xc5\x65\x28\ +\x9d\xab\xb2\x30\x2f\x2a\xb0\xf6\xc5\xfd\x59\x95\x1b\x56\xb1\x76\ +\x3b\xf4\x0e\xc5\x0f\x62\x3b\x4a\xb3\x48\xfc\x24\xde\x89\xd2\xc8\ +\xb0\xd9\xf5\xd2\x64\x1a\xfe\x78\x9a\xdb\x2c\xc7\xd9\xc5\xb9\xe9\ +\xab\xb6\x70\x38\xe6\x14\x17\x9b\x11\x53\x26\x24\x54\xf4\xa4\x99\ +\x3a\x47\xf4\xd9\x93\x3a\x14\x04\xdc\x53\x12\x4a\x13\x62\xf5\x66\ +\x9a\xa9\xb8\x11\x95\x72\x72\x38\x10\x0d\x45\xba\x4d\x43\xde\x46\ +\x9d\xe7\x2a\x65\x24\x8f\x89\xbc\x4c\xc2\x1d\xa9\x3c\x4d\x1e\xe6\ +\x7f\xbf\xd7\x3e\xce\xea\x4d\x6b\x7d\x9e\x07\x45\x50\xb7\xda\xaa\ +\xd6\x78\x0c\xef\x8f\xb0\xe2\xc3\xea\x33\x2c\x5d\x37\x64\x75\x7f\ +\xbf\xad\x61\xa6\x5f\xcf\xfc\xf3\x8d\x5f\xb4\xf4\x50\x4b\xa7\xc8\ +\x6e\xd4\x00\x00\x00\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\ +\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95\x2b\x0e\x1b\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\ +\x1b\x04\x24\x35\x21\xeb\xd4\x23\x00\x00\x1f\xa5\x49\x44\x41\x54\ +\x78\xda\xed\xdd\x69\x74\x55\xe5\xd9\xc6\xf1\x2b\x21\x23\x21\x8c\ +\x02\x21\xa0\x62\x00\x01\x05\xb1\xc8\xe4\x00\x8a\xb3\xa0\xb2\xac\ +\x03\xd6\xb1\x8a\xa2\x16\xab\xbe\xb6\xb5\xb5\xb6\xab\xef\xeb\x6a\ +\xd5\xa5\x38\x74\x69\xeb\x6c\xad\x53\xb5\x5a\x07\x04\xb4\x4e\x50\ +\x95\xaa\x28\x75\x20\x28\x2a\x10\x06\x21\x01\x99\x02\x84\xcc\x39\ +\xef\x07\xa4\x22\x32\x24\xe7\x7e\xf6\xfc\xff\xed\x8f\x75\xa7\x9c\ +\xeb\xb9\xf7\x7d\xf6\xde\x67\xef\xe7\x91\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\xe0\xa3\x0c\x22\x80\xa7\x32\x95\x4b\ +\x08\x1e\xab\x55\x13\x0d\x00\x61\x92\xa5\xc1\x3a\x55\x47\xa8\xbf\ +\xf2\x08\xc3\x07\x35\xfa\x4c\x33\xf4\x0f\x7d\xa8\x06\xc2\x40\xd0\ +\xdf\xf9\x13\x34\x4f\x29\xb6\x00\xb6\x52\x4d\x54\x26\x67\x00\x08\ +\x4e\x89\x9e\xd5\x20\x62\x08\xd0\x47\x3a\x43\x5f\x36\xbf\x5b\x03\ +\xee\x1c\xa7\x8f\x39\xfc\x03\x76\xa0\xe6\xe8\x04\xce\x00\xe0\xbf\ +\x13\x34\x95\xaf\x94\x50\x68\xd2\x49\x9a\x4e\x03\x80\x9f\xfa\x69\ +\xb6\x0a\x89\x21\x24\x36\x6a\x98\xe6\xd3\x00\xe0\x9f\x39\x1a\x4c\ +\x08\xa1\x1a\x8f\x21\xdc\x03\x80\x5f\x2e\xe5\xf0\x0f\x99\x83\x34\ +\x91\x33\x00\xf8\x23\x47\x9f\xa8\x2f\x31\x84\xcc\x17\x1a\xa8\x3a\ +\xce\x00\xe0\xbd\xa1\x1c\xfe\x21\xb4\xaf\x86\x72\x09\x00\x3f\x8c\ +\x21\x82\x50\x3a\x81\x06\x00\x3f\x1c\x4a\x04\xd1\x1c\x17\x1a\x00\ +\x5c\xe0\x02\x20\xa2\xe3\xc2\x4d\x40\xb8\x50\xab\x1c\x42\x08\xe5\ +\xb8\xe4\xd1\x00\xe0\xbd\x14\x11\x84\x54\x06\x97\x00\x00\x68\x00\ +\x00\x68\x00\x00\x68\x00\x00\x68\x00\x00\x0d\x00\x00\x0d\x00\x00\ +\x0d\x00\x00\x0d\x00\x68\xbe\x46\x22\x88\xe6\xb8\xd0\x00\xe0\xc2\ +\x06\x22\x88\xe6\xb8\xd0\x00\xe0\x42\x19\x11\x84\xd2\x22\x1a\x00\ +\xfc\xf0\x01\x11\x44\x73\x5c\x68\x00\x70\xe1\x65\x22\x88\xe6\xb8\ +\xf0\x36\x20\x5c\xe8\xa8\xf9\xea\x4c\x0c\x21\xf3\xb5\xfa\x6a\x1d\ +\x67\x00\xf0\xde\x5a\x3d\x4e\x08\xa1\xf3\xf8\xee\x0e\x7f\xce\x00\ +\xe0\x4a\x5b\x2d\x51\x7b\x62\x08\x91\x75\xda\x5b\x1b\xb9\x07\x00\ +\x7f\x6c\xd0\x85\x4c\x0b\x12\x22\x29\x4d\xd8\xfd\xe1\xcf\x19\x80\ +\xb7\x32\x95\xab\x1c\xe5\x28\x5b\xed\xd4\x5b\xdd\xd5\x45\x5d\xd5\ +\x55\x45\x6a\xaf\x36\xca\x50\x7b\x65\xa8\x8d\xa4\x4d\x4a\x69\xbd\ +\x52\xda\xa4\x75\xaa\xd0\x2a\x55\xe8\x6b\x2d\xd7\x02\x55\xaa\x5e\ +\x75\xaa\x53\xad\x9a\x22\xf2\x79\x6f\xd2\x2f\x19\xf4\x90\xb8\xb9\ +\x79\x63\x41\x03\xf0\x42\xbe\xf6\x51\x6f\xed\xa3\xbe\xda\x4b\x45\ +\x2a\x52\x71\x5a\x39\x37\xa9\x5c\x15\x2a\xd7\x52\x95\x69\x99\x16\ +\xe8\x8b\xe6\x74\xf4\x80\x4d\xd6\xcf\x18\xfe\x10\xb8\xad\xb9\xe3\ +\x40\x03\x70\x6b\xb8\x4e\xd4\xf1\xea\xae\x3c\xe5\x29\xcf\x61\xba\ +\x75\xaa\x56\xb5\xd6\x68\x86\xa6\xea\xb5\x10\x3f\x78\x9b\xa1\x0b\ +\x74\x37\x13\x84\x06\xaa\x4e\x97\xeb\xfe\xe6\x0f\x18\xac\x72\xd4\ +\x46\x7b\x69\x9c\xc6\xee\x7e\x1d\x16\x47\x66\x6b\x9a\x5e\xd0\x0a\ +\x6d\x54\x4d\x08\xf3\xd8\x47\x37\xe9\x74\x2a\x2b\x20\xcf\xe8\x5a\ +\x2d\x68\x49\xc7\x46\xfa\x5a\x69\x84\x86\x6a\x88\x0e\x56\x49\x00\ +\xff\xef\xe5\x7a\x47\x6f\x6b\xb6\xde\x09\xe1\x3d\x82\x12\x5d\xac\ +\xd3\xb5\xb7\x32\x95\x41\x8d\x79\x2e\xa5\x94\x9a\xb4\x44\x4f\xeb\ +\x7e\x95\xb5\xec\x56\x2c\x83\x93\xae\x6e\xba\x4c\xe7\xaa\x93\xda\ +\x04\x9a\xe1\x96\x5b\x87\x2f\xea\xd6\x50\x3e\x8d\xbf\x87\x4a\xd4\ +\x7e\x77\x33\xd3\xc3\xac\x46\xeb\xb5\x48\xab\x09\xc2\x9f\x13\xfe\ +\x62\x9d\xa7\xf7\x95\x0a\xd9\xb6\x40\x97\x69\x4f\x0e\x36\xc0\x3b\ +\x85\x3a\x4b\x4f\x68\x7d\xe8\x0e\xfe\xad\x5b\xad\x9e\xd3\x05\xea\ +\xc8\x40\x01\xae\xf5\xd0\x03\x5a\xa5\xda\xd0\x1e\xfc\x5b\xb7\x7a\ +\xad\xd6\xfd\xea\xce\x80\x01\x6e\xe4\xaa\x44\xf7\x86\xfe\xc0\xdf\ +\x7e\x7b\x52\x7d\xb8\x20\x00\x6c\xb2\x75\xb6\x5e\x88\xdc\xc1\xbf\ +\x75\x9b\xae\xb3\x94\xc5\x20\x02\xe9\x39\x45\x0b\x22\x70\xd2\xbf\ +\xeb\xbb\x02\x0b\xf5\x23\x06\x12\x68\xe9\x69\xff\x60\xcd\x8c\xf4\ +\xa1\xbf\xed\xf6\x1f\x0d\x53\x2e\x83\x0a\x34\xcf\x31\x9a\x1e\x9b\ +\x83\x7f\xeb\x36\x4d\x27\x33\xb0\xc0\xee\x74\xd5\x2b\xaa\x8a\xdd\ +\xe1\x9f\x52\x4a\xd5\x7a\x55\x3d\x18\x60\x60\x67\x0a\x35\x51\x75\ +\xb1\x3c\xf8\xb7\x6e\x0d\xba\x5c\x85\x0c\x34\xb0\xa3\x5b\x7e\x1f\ +\xc7\xfa\xe0\xdf\xba\x7d\xa4\xd3\x18\x6c\xe0\xbb\x37\xfd\xa6\xa8\ +\x3e\x11\x87\xff\x96\x87\x85\x5e\x52\x6b\x06\x1d\x90\xa4\x6c\x8d\ +\xd5\xa6\xc4\x1c\xfc\xdf\xde\x11\x38\x8d\x67\x04\x80\x6e\x7a\x24\ +\x71\x07\xff\xd6\xed\x91\x40\x5e\x63\x06\x42\x63\x8c\x96\x25\xf6\ +\xf0\x4f\x29\xa5\x15\x1a\x47\x11\x20\x99\xf2\x75\x4d\xa2\x0f\xfe\ +\xad\xdb\xaf\x79\x67\x00\xc9\xb3\x6f\x8c\x9e\xf4\xb3\x6e\x6f\xa8\ +\x3f\x05\x81\x24\x39\x5c\xab\x39\xf0\xb7\xd9\x56\x6b\x24\x45\x81\ +\x64\xc8\xd4\x4f\x38\xe4\x77\xb0\x5d\xcb\x04\x71\x88\xbf\xbc\x04\ +\xdf\xf5\xdf\xfd\xaf\x02\x3c\x1d\x90\x30\x49\xeb\xf9\xb9\x7a\x4f\ +\x83\x18\xf6\x9d\x9a\xa7\xe1\xaa\x22\x86\x24\x9d\x0e\x27\x49\x6f\ +\x2d\xe2\xf0\xdf\xa5\xfd\xb5\x50\x7d\x89\x81\x06\x10\xc7\x33\x9d\ +\x63\x34\x43\xc5\x0c\xf9\x6e\x74\xd5\x1b\x3a\x5a\x4c\x18\x8f\x98\ +\x39\x3d\xe6\xef\xf9\xb9\x9d\x47\xe8\x6c\x0a\x86\x7b\x00\x71\x72\ +\xa6\x1e\x0f\xec\x6c\xa7\x5e\x35\xaa\x51\xad\xd6\xe8\x33\x7d\xae\ +\xa5\xaa\xd0\x4a\x95\xab\x5a\xeb\xb6\xf9\x6f\x3a\x28\x5f\xdd\x54\ +\xa4\x22\xed\xa9\x7e\xea\xa7\x4e\xca\x55\x9e\xf2\x94\x1d\xd0\xbf\ +\xb9\x51\x67\xeb\x29\x0e\x0f\x1a\x40\x3c\x5c\xa4\x3b\x03\x78\xda\ +\xad\x41\xf3\x54\xaa\x05\x5a\xa8\x45\x5a\xa4\xf2\x16\xee\xdd\x4d\ +\x25\xea\xa5\x12\xf5\xd1\x00\xed\xaf\x56\xbe\xff\xeb\x6b\x34\x49\ +\x0f\x71\x80\x20\xfa\x7e\xab\x46\x9f\x4f\xa1\xd7\xe9\x6e\x1d\xa7\ +\xee\x6a\xef\x60\x9d\xdc\x1c\xb5\x57\x0f\x1d\xaf\x7b\x54\xe9\xf3\ +\xa7\x68\xd4\x75\x14\x0f\xa2\xff\xed\xef\xdf\xe1\xbf\x51\x65\xba\ +\x45\x03\x3d\xfb\x2c\x83\x34\x59\x8b\x7c\x6c\x04\x8d\x9a\x40\x01\ +\x21\xca\x97\x37\x67\x6a\xb3\x4f\x53\x6d\xbd\xaa\xeb\x7c\x7a\x9c\ +\x76\xb0\x7e\xa5\xd7\x7c\x6a\x6b\x35\x3a\x43\xfc\x22\x80\x88\xfa\ +\x91\x2f\x87\x49\xa3\x6e\x53\x6f\x15\xf8\xfa\xc9\x0a\x54\xa2\xdf\ +\xf9\xb2\x66\x41\x83\x4e\xa7\x90\x10\x45\xc7\x7a\xfe\xc3\x5f\x8d\ +\x16\x6a\x52\xa0\x9f\xf1\x72\x2d\x54\xb5\xe7\x3f\x0a\x1e\x45\x31\ +\x21\x6a\xfa\xea\x2b\x8f\x0f\x8c\xa7\x75\x46\x00\x77\xe7\xb7\x97\ +\xa5\x53\xf4\x8c\xc7\x9f\x74\xb9\xfa\x50\x50\x88\x92\x1c\xad\xf0\ +\xf4\x90\x78\x59\xfd\x02\xfb\x8d\x7e\x47\x9f\xb6\x8f\xc7\x2b\x18\ +\xae\x50\x3e\x45\x85\xa8\xc8\xf7\x70\x82\xef\x5a\xcd\xd5\x11\xa1\ +\xfc\xd4\x43\xf5\xa1\x87\x17\x3d\xa5\xb4\x00\x44\x43\x86\x1e\xf5\ +\x70\x95\xbd\x73\x42\xfd\xd9\xcf\xd5\x6c\x0f\x5f\x16\xce\xa4\xb8\ +\x10\x7e\x57\x7a\x76\x08\x5c\x12\x81\xf7\xe5\xf3\x75\x99\x67\x9f\ +\x7f\x12\xc5\x85\xb0\x1b\xe9\xd1\x8f\x61\xb3\xd4\x25\x32\x19\x74\ +\xd0\x6b\x6a\xf0\x24\x87\x23\x28\x30\x84\x59\x7f\x4f\xe6\xfa\x5b\ +\xa4\x8b\x22\x77\x19\x74\xb1\xca\x3c\x48\x62\x8d\xfa\x51\x64\x08\ +\xef\xe9\xaf\x17\x33\xfd\x3e\xaf\x6e\x91\x4c\xa3\x58\x53\x3c\x48\ +\xe3\x75\x26\x11\x47\x58\xfd\xd2\x79\xb9\x57\xe9\xc6\x48\x27\x72\ +\xbd\x07\x8f\x42\x5f\x4b\xa1\x21\x8c\xc6\x3a\x2f\xf5\xb9\x3a\x34\ +\x06\xf7\x44\xdc\xff\x24\x7a\x3c\xc5\x86\xb0\xe9\xa9\xe5\xce\x97\ +\xcb\xe8\x10\x8b\x64\xda\xe9\x15\xc7\xc9\x7c\xa5\x3d\x29\x38\x84\ +\x49\xb6\xf3\xdf\xfe\x27\xc7\x2a\x9f\xdb\x1d\xa7\xf3\x57\x56\x16\ +\x46\x98\x8c\x73\xfc\xa3\xdf\xc4\xd8\x25\x74\x89\xe3\xa7\x04\x4f\ +\xa2\xe8\x10\x16\xf9\x8e\x6f\x75\x1d\x12\xcb\x94\x86\x39\x7d\x39\ +\xba\x4a\xb9\x14\x1e\xc2\x61\xaa\xc3\xc2\x5e\x15\xe3\x37\xdf\xfa\ +\x6a\x95\xc3\xa4\xa6\x50\x78\x08\x83\xd3\x54\xef\xf0\xf6\xd6\x41\ +\xb1\xce\xea\x40\x2d\x76\x96\x55\xbd\x4e\xa1\xf8\x10\xb4\x42\x7d\ +\xe2\xac\xa4\x2b\x54\x14\xfb\xbc\x8a\xb4\xd2\x59\x5e\x1f\xaa\x0d\ +\x05\x88\x60\xb9\x7b\xf5\x65\x79\x44\x9f\xf8\x6b\xa9\xae\x5a\xc6\ +\xeb\x41\x88\x87\x62\x67\xa7\xff\xab\x34\x24\x31\xa9\x1d\xa4\xaf\ +\x9d\xfd\x5e\x52\x44\x11\x22\x38\xaf\x39\x2a\xe4\xa6\x84\x4d\x7a\ +\xd5\xcf\xd9\x39\xc0\x3f\x29\x42\x04\x65\xac\xa3\x09\x31\xeb\x35\ +\x22\x71\xd9\x1d\xe2\xe8\x85\xe1\xcd\x3c\x18\x8c\x60\xe4\x6a\xba\ +\xb3\x89\x3e\x92\xe8\x12\x35\x39\x49\x6f\x2a\x4f\x04\x20\x08\xc3\ +\x78\xe8\xd7\xe8\x0e\x47\x09\x0e\xa1\x18\xe1\xbf\x0f\x9c\x14\xef\ +\xab\x89\xce\xf0\x75\x27\x19\xbe\x43\x31\xc2\x6f\xa7\x39\x29\xdd\ +\x79\xea\x98\xe8\x14\x3b\xa8\xd4\x49\x8e\x3c\x12\x04\x5f\x65\x6b\ +\xa1\x93\xe7\xd9\x0f\x4d\x7c\x92\xa3\x9c\xbc\x47\xf1\x25\x6f\x07\ +\xc2\x4f\xe7\x38\x59\x17\xef\x06\x82\x94\xf4\x7b\x27\xab\x25\xfc\ +\x88\x20\xe1\x97\x5c\x27\xb3\xdd\xbd\x48\x90\xdf\x70\xb1\xaa\xd0\ +\x0b\xfc\x16\x00\xbf\xf4\x71\x32\xd3\x6f\x31\x41\x7e\xa3\xd8\xc9\ +\x2b\x42\xbd\x08\x12\xfe\x78\xcc\xc1\x23\xac\x17\x13\xe3\x36\x26\ +\x38\x78\x2c\xe8\xaf\xc4\x08\x3f\xf4\x70\xf0\x6d\x35\x53\x19\x04\ +\xb9\x8d\x0c\xbd\xe5\x20\x55\xce\xa9\xe0\x83\x07\x1d\x94\x6a\x67\ +\x62\xdc\x4e\x67\x07\xa9\xde\x47\x8c\xf0\x5a\x3b\x07\x6f\xb2\x4d\ +\x24\xc6\x1d\x98\x64\xce\xf5\x6b\xb5\x23\x46\x78\xeb\x3c\xf3\xe4\ +\x96\xff\x89\xc0\x12\x9f\x41\xc8\xd7\x1c\x63\xb2\x75\x21\x5f\x3b\ +\x19\xdf\x13\xb5\x05\x9f\x73\x35\x56\xd9\xa6\xbf\x50\xa7\x5b\xb5\ +\x99\x81\xdf\x81\x6a\xdd\xa1\x7a\xd3\x5f\xc8\xd6\x58\x7e\x0c\x84\ +\x97\xba\x6b\xa3\xf1\x5b\xea\x23\x42\xdc\x05\xeb\xf4\x6a\x95\xdc\ +\x08\x84\x97\x2e\x62\xca\x6f\x4f\xd9\x17\x57\xff\x31\x21\xc2\x3b\ +\xf3\xcc\xcf\xab\x61\xd7\xa6\x71\x86\x85\xb0\xea\x69\xfe\x7e\xea\ +\x4b\x88\xbb\x61\x7f\xca\x92\x75\x03\x23\x24\x5a\x37\x01\xaf\x32\ +\xee\xff\x8c\xca\x18\xf2\xdd\x58\xa2\x67\x8d\x7f\xe1\x4a\x42\x84\ +\x17\x32\x8c\x4f\xac\x57\xeb\x34\x42\x6c\x86\x33\x54\x63\xca\x79\ +\x71\xe4\x7e\x5b\x42\x24\x1c\xa6\x0d\xc6\xd7\x7f\x78\x67\xbd\x39\ +\x5a\x19\xe7\x5a\xa8\xd4\xc1\x84\xc8\x25\x80\x7b\x43\x8d\xeb\xd0\ +\x4c\x56\x03\x03\xde\x0c\x8d\xba\xc3\xb4\x7f\xa1\x86\x13\x22\x5c\ +\xcb\xd5\x93\xc6\xa9\xbf\xd1\x7c\xb6\x77\x03\x9f\x50\x0e\x11\x72\ +\x06\xe0\x56\x1b\xe3\xf7\xca\x64\x06\xbb\x05\x6e\x37\xed\x3d\x8c\ +\x35\x03\xe1\xda\x81\xc6\xf7\xff\x99\xae\xa2\x25\x7a\xab\xd1\x94\ +\xf7\x01\x44\xc8\x19\x80\x5b\x27\x9b\xf6\x9e\xa1\x0a\x06\xbb\x05\ +\xca\x35\xd3\xb4\xff\x49\x44\x48\x03\x70\xeb\x44\xd3\xde\x33\x55\ +\xc5\x60\xb7\x40\x95\x5e\x37\xed\x3f\x96\x08\xe1\x96\xe5\x84\x74\ +\x03\xf7\xa5\x5b\xec\x10\xe3\x8f\xae\x80\x43\x47\x98\x8a\x71\x21\ +\x01\xa6\xa1\xcc\x94\xf9\x48\x02\xe4\x12\xc0\x1d\xdb\x29\xe5\xb3\ +\x0c\x74\x1a\x9e\x37\xed\x3d\x86\x00\xe1\xce\x87\xa6\x6f\xa3\x81\ +\x04\x98\x86\x1f\x18\xe7\x5d\x02\x1c\x29\x54\xb9\xa1\x14\xd7\x11\ +\x60\x9a\x2c\x77\x01\xca\x55\x40\x80\x5c\x02\xb8\x51\xa2\x3c\xc3\ +\xde\x7f\x63\x98\xd3\xf4\x94\x61\xdf\x3c\x95\x10\x20\x0d\x20\x0c\ +\x0d\x80\x49\x40\xd2\xf5\x1c\x0d\x80\x06\x10\xf5\x06\xd0\xa0\x52\ +\x86\x39\x4d\x9f\xa8\x89\x06\x40\x03\x08\xfe\xdf\x68\x79\x8c\x77\ +\x1e\x8f\x00\xa5\x6d\x93\xa9\x79\xf6\x62\x5e\x00\x1a\x80\x0b\xb9\ +\xda\xcb\xd4\x00\x98\x04\x3c\x5d\x9b\x35\xcf\xb0\xf7\x5e\x4c\x11\ +\x4e\x03\x70\x21\x47\x45\x86\xbd\xbf\x50\x1d\xc3\x9c\xa6\x3a\x7d\ +\x61\xd8\xbb\x88\x97\x82\x69\x00\x6e\x1a\x40\xb7\xb4\xf7\xad\xe7\ +\x29\x40\x93\x32\xc3\x3c\x0a\x45\xc6\x25\x5c\x40\x03\x90\x24\x65\ +\x1b\x1a\x40\x8d\x16\x31\xc8\x06\x8b\x54\x93\xf6\xbe\xc5\x34\x00\ +\x1a\x80\x0b\xed\x0d\x4b\x79\xd3\x00\x82\x6b\x00\xad\x58\x2a\x94\ +\x06\xe0\xc2\xbe\x86\x7d\x6b\x99\x07\xc0\x64\xb9\xe9\x0e\xca\xbe\ +\x04\x48\x03\xb0\xb3\x2c\x34\xb1\x86\x21\x36\xb2\x3c\x48\xdd\x83\ +\xf8\x68\x00\x76\xdd\x0c\xfb\x7e\xc6\x10\x1b\x7d\x16\xd0\xc8\x81\ +\x06\xf0\x0d\xcb\x8f\x80\xf3\x19\x62\xa3\xf9\x01\x8d\x1c\x68\x00\ +\x0e\xca\x68\x19\x43\x6c\xb4\x8c\x06\x40\x03\x88\xee\x25\x00\xb7\ +\x00\xad\xca\xb9\x04\xa0\x01\x04\xab\x93\x61\xdf\x95\x0c\xb1\x91\ +\xa5\x85\x76\x26\x3e\x1a\x80\x5d\x7e\x40\xdf\x5f\xb0\x36\x80\x3c\ +\xe2\xa3\x01\xd8\x59\x5e\x29\xd9\xc8\x10\x1b\x6d\xa0\x01\xd0\x00\ +\xa2\x7b\x06\x50\xcd\x10\x1b\xd5\xd0\x00\xe2\x2c\x23\x02\xff\xc6\ +\x54\xcc\x3f\x1f\xf9\x83\x33\x00\x00\x34\x00\x00\x34\x00\x24\xfe\ +\xe2\x01\x34\x80\x6f\x58\x6e\xe4\x65\x31\xc4\x46\x96\xdf\x60\x98\ +\x8c\x8d\x06\xe0\x80\xa5\x8c\xf2\x19\x62\x1a\x00\x92\xdb\x00\x0a\ +\x19\x62\xa3\x76\x34\x00\x1a\x40\x74\x2f\x01\x78\x1a\xdd\xaa\x88\ +\x06\x40\x03\x08\x56\x55\x40\xe5\x0b\x6b\x0b\xa5\x01\xd0\x00\x1c\ +\x58\x4b\x03\x08\x50\x57\xc3\xbe\xcc\xc7\x44\x03\x70\x60\xb1\x61\ +\x5f\x26\xa5\xb2\xb2\x2c\xca\xb2\x84\xf8\x68\x00\x76\x96\x29\x29\ +\xfa\x33\xc4\x46\x7d\x0d\xfb\x2e\x25\x3e\x1a\x80\x9d\xa5\x8c\xfa\ +\x31\xc4\x46\xfd\x69\x00\x34\x80\x60\x59\x4e\x24\x3b\x31\xc4\x26\ +\x19\xea\xc0\x25\x00\x0d\x20\xba\x97\x00\xb9\xfc\x10\x68\x52\x6c\ +\x5a\xdf\x6f\x39\x01\xd2\x00\xec\x2c\x3f\x26\xb1\x4a\xbd\x4d\x89\ +\xe9\x9d\x7e\x66\x63\xa0\x01\x38\x50\x6f\x98\xd9\x8f\x06\x60\xd3\ +\xcb\xd0\x00\xca\x59\x97\x99\x06\xe0\x42\x8d\xe1\x66\x52\xb6\x7a\ +\x31\xc8\x06\xfb\x18\x16\xf8\x5c\xaa\x5a\x02\xa4\x01\xd8\xd5\x9a\ +\x6e\x26\xf5\x61\x95\xfa\xb4\xe5\xa8\x8f\x61\x6f\x1a\x00\x0d\x20\ +\x04\x0d\x60\x80\x5a\x33\xcc\x69\x6a\xad\x01\x34\x00\x1a\x40\xf0\ +\x96\x19\xa6\x96\xd8\x5f\x6d\x18\xe6\x34\xb5\x31\x34\x80\x14\x3f\ +\x02\xd2\x00\x5c\xb1\x7c\x97\xb4\x32\x7d\x8b\x25\xdb\x20\xc3\xa4\ +\x9e\x35\x3c\x06\x44\x03\x70\x65\x89\xe9\x64\xf2\x14\x86\x39\x80\ +\xe4\x6a\x39\x03\x80\xbb\x53\xd1\x15\x4a\xa5\xbd\xad\x27\xc0\x34\ +\x6d\x32\xa4\xbe\x82\x7b\x2f\x9c\x01\xb8\x2b\xc4\xc5\x86\xbd\xdb\ +\x69\x10\x03\x9d\x86\xc1\x2a\x30\xec\xbd\x88\xd9\x00\x68\x00\xee\ +\xbc\x62\xda\xfb\x5c\x06\x3a\x0d\xe7\x99\xf6\x7e\x99\x00\xe1\xce\ +\x08\xc3\xc9\x68\x4a\x8b\x08\x30\x0d\x65\xa6\xcc\x87\x11\x20\x5c\ +\xb2\x14\xe3\x06\x1d\x4c\x80\x2d\x74\xa8\x36\x9a\x32\x07\x97\x00\ +\x4e\xbd\x6b\xd8\xb7\x50\x47\x32\xd4\x2d\x74\xa4\xe9\xf9\x89\x59\ +\x04\x48\x03\x70\x6b\xba\x69\xef\xd1\xa6\x1b\x5a\xc9\x53\xa0\xd1\ +\xa6\xfd\x5f\x22\x42\xb8\x35\xd0\x74\x42\xda\xa8\xde\x44\xd8\x02\ +\xbd\xd5\x64\xca\x9b\x87\xaf\x38\x03\x70\x6c\xb9\x16\x98\x3e\xe7\ +\xc5\x0c\x76\x0b\x5c\x6a\x5a\xd8\xfb\x0b\xad\x20\x42\xb8\x95\xa3\ +\xc7\x4c\xdf\x49\x0d\x44\xd8\x02\x8d\xa6\xac\x1f\x31\xbc\x44\x0c\ +\xce\x00\x76\xa8\x4e\xef\xa9\xc9\xb0\x7f\x2b\x4d\x62\xb8\x9b\xe9\ +\x0a\x53\x5d\x34\xe9\x3d\xd5\x13\x22\x5c\x1b\xa6\x4a\xe3\xd3\x00\ +\x7c\x2f\x35\x47\x96\x16\x99\x72\x5e\xaf\x83\x08\x11\xee\x65\x68\ +\x89\xa9\x30\x6b\x74\x3a\x21\x36\xc3\x99\xaa\x31\xe5\xbc\x98\x08\ +\xb9\x04\xf0\x42\x4a\x53\x4c\xfb\xe7\x6a\x3c\xb3\x03\x35\xe3\x5e\ +\xcb\x78\xd3\x92\xe0\xd2\xb3\x84\x08\x6f\xec\x69\xfa\x66\x4a\x29\ +\x65\x5a\xe7\x26\x19\xf6\x35\x67\x5c\x4c\x88\xf0\xca\x07\xc6\xe2\ +\x7c\x91\x08\x77\xe3\x65\x63\xc2\xef\x10\x21\xbc\x33\xde\xfc\xfd\ +\x34\x92\x10\x77\xe1\x70\x73\xbe\xa7\x12\x22\xbc\x53\xa4\xd5\xc6\ +\x02\xfd\x84\x10\x77\x61\xae\x31\xdd\x55\xa6\xe5\xc4\xe1\xbb\xcc\ +\x88\xfd\x7b\xd7\xe9\x75\xe3\x5f\xe8\xcb\xec\x00\x3b\xf5\x63\xf3\ +\x3d\x92\xd7\xb4\x8e\x18\xe1\xa5\x0b\x54\x6f\xfc\x96\xfa\x90\x17\ +\x83\x76\xa8\x40\x1f\x1b\x93\xad\xd3\xf9\xc4\x08\x6f\xb5\xd5\x2a\ +\xf3\x75\xea\x65\xc4\xb8\x03\x3f\x35\xe7\xba\x4a\x85\xc4\x08\xaf\ +\xdd\x6e\x2e\xd4\x94\xba\x10\xe3\x76\x3a\x3b\x48\xf5\x16\x62\x84\ +\xf7\xda\x3b\x28\xd5\x7f\x45\xee\xee\x87\xb7\x32\xf4\xb6\x83\x54\ +\xdb\x12\x24\xfc\x70\x9f\xb9\x54\x1b\x34\x91\x18\xb7\x31\x51\x0d\ +\xe6\x4c\xef\x26\x46\xf8\xa3\xb7\x83\x6f\xab\x32\x9e\x58\xfb\xaf\ +\xee\xc6\xb7\x2c\xb6\x6c\xac\xc3\x0c\x9f\xe4\x69\xaa\x83\x82\xe5\ +\xa9\xc0\xad\xa6\x39\x49\x33\x8f\x20\xe1\x97\x33\x8c\x6f\xac\x6d\ +\xd9\x6e\x24\x48\x49\x7f\x70\x90\x64\x8d\x4e\x23\x48\xf8\xe9\x33\ +\x07\x65\xbb\x59\xa3\x12\x9f\xe3\x68\x55\x3b\x48\x72\x1e\x05\x09\ +\x7f\x9d\xe0\xa0\x6c\x53\xfa\x54\x9d\x12\x9d\x62\x47\xcd\x73\x92\ +\xe3\xf1\x14\x24\xfc\x36\xcb\x49\xe9\xbe\x99\xe8\x0c\x67\x38\xc9\ +\xf0\x2d\x8a\x11\xfe\x1b\xe2\xa4\x78\x53\xba\x3d\xb1\x09\xde\xe5\ +\x28\x41\xa6\x00\x43\x00\x72\xf5\xa2\xa3\x02\x4e\xe6\xa3\xc1\x3f\ +\x71\x94\xde\x54\xe3\x0c\x42\x40\x9a\x8e\x51\x95\x93\x12\x6e\xd4\ +\xe1\x89\xcb\xee\x30\x07\x8f\xfe\xa4\x94\x52\x95\x8e\xa6\x10\x11\ +\x94\x69\x8e\xbe\xc5\x92\x36\x59\x58\x7f\x67\xb9\xf1\x34\x05\x02\ +\xb4\x87\x93\x1f\xb1\x52\x4a\x69\xad\x86\x26\x26\xb5\x83\xcc\xd3\ +\xaa\x6c\xdd\x6a\x13\xfe\x2b\x0a\x02\x77\xbe\xb3\xef\xb2\xe5\x09\ +\x79\x38\xb8\xab\x96\x39\xcb\xec\x42\x0a\x10\xc1\x6a\xa3\xf7\x9d\ +\x95\x73\x85\xba\xc5\x3e\xaf\x6e\x5a\xe9\x2c\xaf\xd9\xa6\x25\xc4\ +\x01\x27\x4e\x35\xcf\x11\xf4\xed\xf6\x95\x86\xc4\x3a\xab\xc1\x5a\ +\xee\x2c\xab\x7a\x9d\x42\xf1\x21\x0c\x9e\x73\x56\xd4\x29\x7d\xad\ +\x7e\xb1\xcd\x69\x3f\x67\xd7\xfe\x29\xa5\xf4\x0f\x0a\x0f\xe1\x90\ +\xa1\x75\x0e\x0b\x3b\xa5\xc3\x62\x99\xd2\x28\xa7\x19\xad\x57\x2b\ +\x0a\x0f\x61\x71\xb4\x9a\x1c\x16\x77\x43\x0c\x1f\x0d\x9a\x64\x5c\ +\xf2\x7b\xfb\xed\x48\x8a\x0e\xe1\xd1\x4a\xf7\x3b\x2d\xef\x94\xee\ +\x88\x55\x3e\x77\x39\x4e\xe7\x3e\xbe\xff\x11\x2e\x3d\xf5\x95\xe3\ +\x22\x9f\x11\x93\xdf\xb8\x3b\xe9\x4d\xc7\xc9\x2c\xd7\xde\x14\x1c\ +\xc2\xe6\x04\xc7\x65\x9e\xd2\xfc\x18\x3c\x22\x3c\x5a\x9f\x3a\xcf\ +\x65\x0c\xc5\x86\x30\xba\xcc\x79\xa9\x6f\xd6\xcd\x91\x4e\xe4\x0f\ +\xce\x9e\x94\xfc\x76\xbb\x9c\x42\x43\x38\xe5\x6a\x8a\xf3\x72\x4f\ +\x69\x9a\xba\x47\x32\x8d\x62\x3d\xef\x41\x1a\xcf\x2b\x87\x42\x43\ +\x58\xf5\x52\x85\x07\x45\xbf\x58\x97\x46\xec\xa6\x57\x86\x26\x3a\ +\x99\xe9\x77\xfb\x6d\xa5\x4a\x28\x32\x84\xd9\xa1\x1e\x94\x7d\x4a\ +\x8d\x9a\x15\xa1\x75\x6f\xbb\x6a\xa6\xa3\x97\x7d\xb7\xdf\x98\x43\ +\x11\xa1\x77\xa5\x27\xa5\xbf\xe5\xea\x37\xfc\xcf\xbe\x17\x38\x58\ +\xe3\x6f\x67\xdb\x24\x8a\x0b\x51\x70\x8f\x67\x87\x40\xa9\xce\x0b\ +\xf5\x27\x3f\x5f\x1f\x79\xf6\xd9\xff\x4c\x61\x21\x1a\xb2\xf4\x9e\ +\x67\x87\x41\x9d\x4a\x35\x3a\x94\x9f\x7a\x98\x3e\x52\x9d\x67\x9f\ +\xfb\x03\x65\x53\x58\x88\x4e\x0b\x58\xe6\xd9\xa1\x90\x52\x4a\x33\ +\xd5\x3f\x44\x77\xc3\x73\xb5\xaf\x5e\xf0\xf4\xf3\x2e\xe3\xc9\x3f\ +\x44\x4b\x6f\x87\x2f\xbe\xee\x78\x7b\x56\x67\x28\x2b\xf0\xcf\x99\ +\xad\xf1\x4e\xdf\x86\xdc\xf1\x4b\xd2\xac\xfb\x87\xc8\x19\xa5\x5a\ +\x8f\x0f\x8c\x1a\x2d\xd2\x4f\x03\xfd\x8c\x57\xa8\xcc\xc9\x22\x69\ +\xbb\xfe\x94\x23\x29\x26\x44\xd1\xb1\x1e\xfd\x18\xf6\xdd\xad\x49\ +\xb7\xa9\x8f\x0a\x7c\xfd\x64\x05\xea\xa5\xdf\x79\x78\xc5\xbf\xed\ +\xb4\x1f\x27\x52\x48\x88\xaa\x71\xda\xe8\xc3\x41\x92\x52\xa3\x5e\ +\xd3\x6f\x7c\xfa\xa6\x1c\xac\x6b\xf5\x86\xd3\xd7\x9f\x77\x35\xe9\ +\xf7\x49\x14\x11\xa2\xec\x1c\x5f\xce\x02\xb6\x6c\x1b\x55\xa6\xdb\ +\x74\xa0\x87\x87\xfe\x1d\x2a\xd3\x7a\xdf\x3e\x4f\x83\xce\xa2\x80\ +\x10\x75\xbf\x70\x3c\x15\xc6\xee\xb7\x0d\xba\x5f\x63\xd4\x43\xed\ +\x1d\xfc\x52\x90\xa3\xf6\xea\xa1\xb1\x7a\xd0\xa7\x73\x99\x6d\xcf\ +\x6a\xae\xa6\x78\xe2\x2e\x23\x11\x9f\xf2\x42\xdd\xa5\x7c\xdf\xff\ +\x5f\x1b\xf5\xa9\x4a\xf5\xa5\x16\xaa\x4c\x0b\xb5\xa2\x85\xe3\x52\ +\xac\x12\x95\xa8\x97\x7a\x6b\x80\xf6\x57\xa6\xef\xff\xfa\xcd\xfa\ +\x89\xfe\xca\x01\x42\x03\x88\xcb\xbd\x80\x7f\x04\xf6\x4b\x76\xbd\ +\x6a\x54\xa3\x3a\xad\xd5\x7c\xcd\xd7\x52\x55\x68\xa5\xca\xb5\x59\ +\x95\x6a\xfc\xe6\xbf\x68\xa5\x76\x6a\xad\x6e\xea\xaa\x22\xed\xa5\ +\x7e\xea\xaf\x0e\xca\x51\x9e\xf2\x02\x7b\xf8\xa6\x51\x3f\xd4\x14\ +\x0e\x0f\xc4\xc7\x51\xda\xec\xf3\x29\x74\x74\xb7\x6a\x9d\x40\xc1\ +\x20\x6e\x86\x79\xf2\x82\x6c\xfc\xb6\x65\x3a\x84\x62\xe1\x12\x20\ +\x8e\xfa\x6a\xa6\x8a\x18\xf4\x5d\x2a\xd7\x68\x7d\x4e\x0c\x34\x80\ +\x78\xca\xd1\xac\x98\xaf\xfd\x63\xf3\x91\x0e\xd5\x66\x62\x48\x8e\ +\xcc\x84\x7d\xde\x3a\x8d\xd0\x5d\x0c\xfb\x4e\xdc\xa3\x61\x1c\xfe\ +\x88\xbf\xf3\xb8\xd2\xdf\xc1\x76\x51\xe2\xce\x07\x91\x58\x87\x3b\ +\x5d\x25\x2f\xfa\xdb\x6a\x26\xfb\x42\xb2\xf4\xd6\xb3\x1c\xf8\xdf\ +\x6c\x2f\xab\x2f\x05\x81\xa4\xc9\xd1\x44\x0e\x7e\xa5\x74\xb9\xf2\ +\x28\x06\x24\xd3\x11\x09\x7f\x36\x60\x89\x8e\xa5\x08\x92\x2c\xe9\ +\x13\x3d\x2d\x53\x93\x8e\x4f\xf0\xe7\xbf\x59\x4f\xa9\x81\xc3\x00\ +\x49\x34\x50\x77\xab\x22\xe1\x0f\x08\xd7\x6a\xa5\x9e\xd0\xa1\x14\ +\x03\x92\x23\x4b\x9d\x74\xbe\xbe\xe4\xea\x7f\x9b\xed\x0b\x9d\xa7\ +\xce\x2c\xfa\x95\x3c\xc9\x7b\x12\x70\x8c\x4e\xd2\xb8\x98\x2c\xfc\ +\xed\x56\xa5\xa6\x6a\x9a\xa6\xab\x92\x28\x10\x47\xb9\xba\x41\x4b\ +\x3d\x58\x2d\x37\x5e\x97\x04\x5f\xe9\x36\x7e\x15\x40\xbc\xe4\xa9\ +\x8f\x6e\xe3\xf0\x6e\xc1\xf6\xa0\xfa\xd2\x06\xb8\x04\x88\xc7\xc1\ +\x7f\x9a\xc6\x6b\x4c\xe2\xde\x7a\xb0\x6a\xd4\x74\x3d\xad\xe7\xb4\ +\x89\x28\x10\x5d\x93\x54\xe6\xcb\xe4\xd9\xf1\xdc\xea\xb5\x54\xd7\ +\x51\x44\x88\xa2\x4e\xba\xc8\xf7\x69\x34\xe3\xb9\x6d\xd0\x45\xea\ +\x4c\x41\x71\x09\x10\x1d\xdd\x74\xa1\xce\x51\x3f\x86\xd7\x99\x25\ +\xfa\x8b\x1e\xd6\x12\x82\x40\xd8\x65\xe9\x16\xde\xf4\xf3\x64\x5b\ +\xab\xfb\x94\x4b\x81\x21\xbc\xda\x6b\x82\xe7\xeb\x01\x26\x7b\x6b\ +\xd4\x15\x3c\x43\xc1\x25\x40\x18\xb5\xd6\x85\x9a\xe0\xe1\xaa\x3c\ +\xd8\xea\x73\xdd\xa3\x87\xb5\x9e\x20\x10\x1e\xe7\x68\xb1\xef\xeb\ +\xff\x24\x77\x6b\xd2\x12\x9d\x4d\xd1\x21\x0c\x32\xb5\xbf\x66\x71\ +\x50\x06\xb0\xbd\xaf\x41\x89\x7f\x9b\x14\x01\x1b\xa4\x7b\x54\xc3\ +\xc1\x18\xd8\x99\xc0\x03\x3a\x80\x22\x44\x50\xae\xd7\x5a\x0e\xc3\ +\xc0\x7f\x1b\xb8\x95\x42\xe4\x26\xa0\xdf\x5a\x69\x90\x9e\x52\x6f\ +\x06\x30\x14\x96\xea\x0c\xcd\x61\x62\x91\x68\x5e\x41\x47\x51\x5f\ +\xdd\xaf\x39\x1c\xfe\xa1\xb1\x97\xde\xd5\xc3\x4c\x2c\x1a\xcd\x6f\ +\xd2\xe8\xf9\xb9\x1e\xd7\x70\x86\x2e\x64\x0e\xd0\xf9\xca\xd4\x5b\ +\x04\xc1\x25\x80\x97\xf6\xd1\x83\x1a\x1d\x81\x7f\x67\x83\xea\x54\ +\xa7\x7a\xd5\xa9\x41\x2b\x55\xa1\x0a\x55\xa8\x5c\xe5\x5a\xa9\xcd\ +\xda\xa4\x26\x6d\x54\x4a\x6b\xb7\xf9\xef\x3b\x2a\x43\x85\xca\x54\ +\x1b\xb5\x56\x57\x15\xab\x48\x45\x2a\x52\x37\x75\x51\x96\x72\x94\ +\xad\x1c\xe5\x28\x2b\x02\x9f\x7b\x86\x2e\xd4\x62\x0e\x2b\x1a\x80\ +\x17\x0a\x74\x95\xae\x51\xdb\x10\xff\x0b\x97\x69\xb1\xca\x55\xa1\ +\xf2\x6f\x0e\xf9\x0a\xad\x54\xca\x3c\x42\x5d\xbf\x69\x06\x5b\x9a\ +\x42\xb1\x7a\xaa\x47\x88\x33\xa8\xd4\xcd\xba\x83\x05\xc6\x68\x00\ +\xae\xf5\xd4\x0b\x21\xfd\xc9\xa9\x51\x6f\xe9\x03\xbd\xad\x39\xaa\ +\x51\xad\xea\x54\x67\x3e\xe8\x77\x3e\x5a\x39\xca\x51\xae\x72\xd5\ +\x4b\x23\x35\x44\x87\xab\x43\x28\x13\xf9\x58\xe3\x78\x71\x08\xee\ +\x64\x85\x6e\x09\x8f\x2a\xad\x56\xa9\xee\xd0\x18\x15\x06\x9a\x4c\ +\x5b\x8d\xd1\x1f\x55\xaa\x55\xda\x10\xb2\x27\x21\x27\x28\x9b\xc2\ +\xe5\x0c\xc0\x85\xfd\x74\xb3\xc6\x86\xe4\xdf\x52\xa3\x4f\x34\x57\ +\x9f\xa9\x54\xf3\x43\xf5\x1d\xd7\x45\x7d\x75\xa0\x0e\xd4\x00\x0d\ +\x0a\xcd\x1b\x7b\x2f\xeb\x6a\x7d\xc6\x21\x06\x9b\x09\xda\x14\x92\ +\xef\xb4\x17\x75\xaa\x8a\xd4\x36\xc4\x37\xe3\xb2\xd4\x56\x45\x3a\ +\x46\x8f\x86\xe4\x9d\xc8\x8d\xfa\x19\x05\x8c\xf4\x75\xd0\x5d\x81\ +\xbf\xfe\x5a\xa9\x65\x7a\x4c\xc7\x44\x2e\xbb\x91\x7a\x40\x4b\x54\ +\xa9\xa6\x80\x13\xbc\x37\xa4\xf7\x29\x10\x7a\xa3\x34\x3b\xd0\xd2\ +\xad\xd6\x2b\xba\x4a\x43\x23\x7d\x81\x37\x54\x57\xe9\x79\x6d\x08\ +\x34\xc7\x77\x75\x24\xc5\x8c\x96\xba\x4a\x95\x01\x16\xed\x87\x9a\ +\xa0\x2e\x6a\x1d\x8b\x24\xf3\xd4\x45\x3f\xd4\x5b\x81\x5e\x0a\xfc\ +\x82\x82\x46\xf3\xb5\xd6\xad\x01\x95\xea\x26\x7d\xa9\x1b\xb5\x57\ +\x2c\x53\x6d\xaf\x5f\x69\x7e\x60\x77\x54\xee\x52\x3e\x85\x8d\xe6\ +\x38\x20\xa0\x53\xff\xc5\xba\x3d\x12\x4f\x19\xda\x1c\xa9\xc9\x9a\ +\x1f\xd0\xa5\x00\xf3\x35\x61\xb7\x4e\x0a\xe4\xd4\x7f\xa6\x8e\x55\ +\xfb\xc4\xac\x94\xd8\x56\xc7\xe9\x5f\x01\xa4\x5c\x19\x9a\x9f\x73\ +\x11\xd2\x9b\x56\x57\xfb\x3e\xa5\xc5\x1a\x3d\xa6\xfe\x89\x4c\xbb\ +\x44\x0f\x68\xb5\x1a\x7c\x4e\xfc\xda\xc4\x2d\x48\x8b\x66\x2a\xd0\ +\x23\x3e\x17\xe3\x47\xfa\x85\x8a\x13\x9d\x79\x67\x5d\xa1\x77\x7c\ +\x4e\xfd\x89\x80\x9f\x9e\x44\x28\x75\xf4\xf9\xca\x74\x8e\x46\xaa\ +\x1d\xb1\x4b\x2a\xd4\x70\xbd\xe9\x6b\xf6\x5f\xaa\x0b\xb1\x63\x5b\ +\xc3\x7c\x5c\xc6\xab\x4a\xb3\x34\x8a\xc8\xb7\x73\xb8\xde\xd1\x66\ +\x1f\xef\x06\x0c\x21\x72\x6c\x35\xde\xb7\xb9\xfd\x1a\xf5\xa8\x46\ +\x12\xf8\x4e\x8c\xd2\x7d\xaa\xf7\x69\x24\xd6\xe8\x2c\x02\x87\x24\ +\x5d\xef\x5b\xd1\xbd\xa8\xbe\xca\x21\xf0\x5d\xc8\x52\x6f\x3d\xe7\ +\xdb\xda\xc3\x37\x12\x78\xd2\xe5\xe8\x66\x5f\x8a\xad\x56\x73\x34\ +\x82\xb8\x9b\xe9\x60\xcd\xf1\x69\xb2\xf5\x9b\x68\xc8\xc9\xfe\xbe\ +\xb9\xdf\x97\x32\x9b\xa6\x71\x84\xdd\x42\xc7\x6b\x9a\x2f\x63\xf3\ +\x00\x8b\x8b\x24\x55\xbe\x2f\xcf\xa7\x57\xe8\x68\x1e\x42\x4d\x73\ +\x7c\x46\xa9\xcc\x87\x11\xfa\xb7\x0a\x08\x3b\x79\xf6\xd0\xab\x3e\ +\xdc\x6b\xbe\x97\x79\x69\x4c\x5a\xe9\x4e\x1f\xde\x25\x7c\x43\x7b\ +\x10\x75\xb2\xec\xa9\x77\x3d\x2f\xab\x87\xb4\x3f\x41\x3b\x30\x50\ +\x0f\x78\x3e\x56\xef\xa8\x27\x41\x27\xc7\x3e\x5a\xe0\x71\x41\x7d\ +\xac\xfd\x22\x31\x8d\x76\x54\xce\x03\xf6\xd3\x5c\x8f\x47\x6c\x01\ +\x2d\x20\x29\x7a\x78\x5c\x4c\x1b\x74\x13\x21\x7b\xe0\x7a\xad\xf3\ +\x74\xdc\x3e\xd5\xde\x84\x1c\x7f\xfd\x3d\x3e\xfc\xa7\xea\x30\x42\ +\xf6\xc8\x10\xbd\xe0\xf1\x23\xc2\x03\x08\x39\xde\xf6\xd3\x52\x4f\ +\x9f\xf3\xbb\x80\x3b\xfe\x9e\xca\xd3\x78\xd5\x79\x38\x82\x5f\xb1\ +\xdc\x78\xbc\x6f\xfd\x79\xf7\xc2\x4f\x93\xde\x64\xfa\x49\x5f\x74\ +\xd4\x9b\x1e\xae\x41\xf0\x59\x4c\xe7\x63\x82\xf6\xd1\x27\x1e\xce\ +\x3a\x77\x15\x01\xfb\xe8\x32\xad\xf7\xf0\x5e\x40\x1f\x02\x8e\xe3\ +\xe1\xbf\xc8\xb3\x92\x99\x1d\xea\xd5\xf2\xe2\xa9\xbb\x3e\xf0\x6c\ +\x3c\x17\x71\x3b\x30\x6e\xf6\xd0\xbf\x3d\x2a\x96\x1a\xdd\x49\xbc\ +\x01\xb9\x55\xd5\x9e\xcd\x1f\xd8\x99\x78\xe3\xa3\x40\xaf\x79\x54\ +\x28\xa5\x3a\x85\x78\x03\x74\x92\x4a\x3d\x7b\x3a\xb0\x35\xf1\xc6\ +\x43\x8e\x66\x79\x54\x24\xcf\x31\xb3\x4c\xe0\xba\xe8\xef\x9e\x3d\ +\x1d\x98\x47\xbc\x71\x38\xfc\x1f\xf6\xa4\x3c\xea\x74\x17\xe1\x86\ +\xc4\x9f\x3c\x7a\x79\xf8\x21\x5e\x16\x8e\xbe\x1b\x3c\x29\x8d\x72\ +\x8d\x27\xda\x10\x39\x59\xcb\x3d\x9a\x2f\x00\x1c\xfe\x3b\xb8\x4b\ +\xdc\x8b\x68\x43\xa6\xa7\xbe\xf0\x64\xac\x7f\x4f\xb4\xd1\x75\x8e\ +\x27\xb3\xce\x4f\x27\xd8\x90\xf2\xe2\x41\xe1\x7a\x9d\x4d\xb0\xd1\ +\x34\xd4\x93\x97\x47\x7e\x43\xb0\x21\xf6\x6b\x0f\x46\x7c\x9d\x86\ +\x11\x6c\xf4\x74\xf4\xe4\x59\x31\x7e\xf4\x0b\xbb\xb1\x9e\xbc\xdf\ +\xc9\xaf\x3d\x11\xd3\x46\x9f\x3b\x2f\x83\xb5\x1a\x4e\xb0\x11\x30\ +\x58\x6b\x3c\x98\x2d\x80\xd5\x84\x22\x24\x53\x4f\x3a\x2f\x81\xcf\ +\x35\x88\x60\x23\xe2\x40\xcd\x73\x3e\xfe\x4f\x29\x93\x60\xa3\xe2\ +\x3a\xe7\xc3\xff\xb6\xda\x12\x6b\xa4\xce\x00\x67\x38\xaf\x81\x6b\ +\x88\x35\x1a\x4e\x74\x3e\xf4\x2f\xd0\xfd\x23\xa7\x95\xa6\x38\xaf\ +\x83\xe3\x89\x35\xfc\x86\x38\x5f\xe5\xef\x71\xa6\x8d\x8e\xa4\x7c\ +\x3d\xe1\xfc\x66\xe0\x0f\x88\x35\xdc\x5a\x3b\x7f\x49\xf4\x0e\x16\ +\x8e\x88\xac\x2c\xdd\xe2\xb8\x1a\xfe\xc3\x2b\x42\xe1\x76\x8f\xe3\ +\x01\xff\x93\x32\x08\x35\xd2\xfe\xe8\xb8\x22\x78\xf9\x3b\xc4\xae\ +\x74\x3c\xd8\x93\xf9\xf6\x8f\xbc\x4c\xdd\xe4\xb8\x2a\x98\xfb\x29\ +\xa4\x8e\x72\x7c\xf5\x7f\x05\xdf\xfe\x31\x71\x85\xe3\x3b\x01\xa3\ +\x89\x34\x7c\x3a\x6a\x8e\xd3\x61\xbe\x8d\xc3\x3f\x46\xdc\xae\x01\ +\x3d\x5b\x1d\x89\x34\x6c\xee\x76\x3a\xc4\x77\xf3\xc3\x5f\xac\x64\ +\xe8\x4e\xa7\xf5\x71\x1f\x91\x86\xcb\x24\xc7\x37\x7a\xf8\xf6\x8f\ +\x9f\xdb\x9c\xd6\xc8\x25\x04\x1a\x1e\x03\xb4\xc9\xe9\xef\xfe\xdc\ +\xfa\x8b\xa3\x56\x4e\x9f\x0b\xd8\xa4\xfd\x88\x34\x1c\xb2\xf5\xb2\ +\xc3\x81\x9d\xa2\x36\x44\x1a\x53\xad\xf5\xa2\xc3\x4a\x79\x89\xe5\ +\x5f\xe3\x77\xfa\xff\x16\xd7\xfe\x31\x3f\x0b\x98\xc1\x65\x40\xbc\ +\xf4\x72\xba\x2c\x14\x2f\x7d\xc6\x5d\x3b\xa7\xcb\xc3\xb1\x86\x50\ +\xc0\x0a\x1c\x2e\xf7\xb5\x86\x27\xbd\x13\xe1\x40\xad\x75\xb8\x2e\ +\x04\xef\x89\x04\xca\xe5\x8b\xbf\x43\x89\x33\x21\x86\x39\xac\x9a\ +\xdf\x12\x67\x90\xa7\xff\x95\xce\x06\xf2\x54\xe2\x4c\x90\x71\x0e\ +\x97\x85\x65\x86\xe8\xc0\xbc\xcd\x54\x9f\x08\xfc\xdc\xf1\x5f\x84\ +\x19\x8c\xff\x75\x38\xe1\x07\x92\x67\x1a\x97\x01\x51\xd6\xc7\xd9\ +\xe9\xff\x02\xc2\x4c\xa8\x2f\x9c\xbd\x1e\xd4\x97\x30\xfd\x95\xa5\ +\x47\x1c\x0d\xde\x0a\x95\x10\x67\x42\xf5\x74\xb6\xa0\xd8\x23\x3c\ +\x3d\xea\xaf\x11\xce\x96\xf8\x3c\x9d\x30\x13\x6c\xbc\xea\x1c\x55\ +\xd2\x21\x84\xe9\x27\x57\x9d\x9b\x15\x7e\x93\xce\xd5\x5b\xa4\x4b\ +\x89\xd2\x3f\x93\x1d\x0d\xda\xd3\x44\x09\x3d\xcb\x4a\xc2\xd1\xe2\ +\xea\x49\xae\xb9\xea\x4c\x98\x50\x57\x7d\xea\xe8\x49\xd2\x81\x84\ +\xe9\xbd\x56\x7a\xc8\xc9\x70\x55\x6b\x1c\x61\x42\x92\x74\x8a\xaa\ +\x9d\xd4\xd4\x5f\x78\x3f\xd0\x7b\x3f\x50\xa3\x93\xc1\xfa\x23\x51\ +\xe2\xbf\xee\x72\x52\x53\x0d\x1a\x4c\x94\x5e\x73\x33\xf3\xdf\x6c\ +\x82\xc4\x77\xb8\x59\x4f\xe2\x43\x82\xf4\xd6\xb9\x8e\x9e\xdf\xde\ +\x8b\x28\xf1\x1d\x7b\x6a\x83\x93\xda\x3a\x93\x28\xbd\xd3\x5e\x4b\ +\x1c\x0c\x51\xa3\x2e\x27\x4a\x7c\xcf\xe5\x4e\x2e\x2e\x17\xb3\x8c\ +\xac\x77\x7e\xae\x26\x5e\xde\x80\x67\xde\x72\x50\x5d\x4d\xba\x9a\ +\x20\xbd\xd1\xc9\xc9\x5c\x2e\x0d\xea\x44\x94\xd8\xc9\x19\x66\x83\ +\x83\x0a\xfb\x94\x0a\xf3\xea\x14\xcd\xc5\x35\xda\x59\x04\x09\x8f\ +\xef\x31\x5d\x4a\x90\xee\x65\x3b\xe9\xce\x53\x95\x47\x94\xd8\xa9\ +\x3c\x4d\x71\xf2\x86\x09\xcf\x03\x38\xe7\xe2\x99\xed\xf5\x1a\x41\ +\x90\xd8\xa5\x43\x9c\xbc\x66\xce\x1a\xc2\x8e\x95\x68\xbd\x83\x61\ +\xb9\x81\x20\xb1\x5b\x2e\x56\x13\x5e\xa7\x9e\x04\xe9\xd2\xff\x39\ +\x18\x94\x8f\x88\x11\xcd\x32\xd7\x41\xb5\xfd\x8e\x18\xdd\xe9\xac\ +\x45\x0e\x86\x84\xa5\x9c\xd0\x3c\x03\x1c\x54\xdb\x97\xda\x83\x20\ +\x5d\xb9\xcc\xc1\x80\x3c\xc8\x9c\x2d\x68\xa6\x2c\xfd\x85\x75\x83\ +\xc2\xc4\xfe\xae\x56\xa5\xf6\x27\x46\x34\xdb\x01\x0e\x1e\x0c\xde\ +\x4c\x8c\x6e\x5c\xe3\xa0\x1b\xff\x89\x18\xd1\x22\x77\x3a\xa8\xba\ +\xff\x21\x46\xbb\x02\x07\xcf\xff\x7f\xc5\xe9\x3f\x5a\x28\x53\x2b\ +\x1c\xbc\x17\x90\x4f\x90\x56\xe7\xa9\xde\x3c\x10\x47\x13\x23\x5a\ +\xec\x28\x73\xdd\xd5\xeb\x7c\x62\xb4\xc9\xd7\x74\x9e\xfe\x43\x64\ +\x6b\xef\x45\x6a\xcf\xa6\x9f\xf9\xfd\xbf\x1a\x8d\x25\x46\xa4\xe5\ +\x64\xd5\x9a\x5f\x3d\x67\xc9\x10\x93\xfb\xcd\x3d\xf8\x03\x42\x44\ +\xda\xec\xf3\x4f\xdd\x4d\x88\xe9\x2b\x70\x70\x27\x96\xa7\xff\x91\ +\xbe\x43\x1d\x54\x60\x6b\x62\x4c\xd7\xed\xe6\xf0\x9f\x27\x44\x98\ +\xd8\xdf\x0e\xbc\x95\x10\xd3\xd3\x56\x5f\x99\xaf\xc0\xfa\x10\x23\ +\x4c\xfa\x98\x5f\x43\x5f\xa6\x36\xc4\xb8\x33\x99\xbb\xf8\xdf\xc6\ +\x98\x9f\xa6\x7e\x54\x8b\x89\x18\x26\x4b\xf4\x37\xe3\x5f\xe8\xac\ +\x31\xc4\xd8\x72\xd9\x7a\xcc\xd8\x79\xab\x34\x92\x18\x61\x76\xb8\ +\x36\x1b\x2b\xf1\x31\x65\x13\x63\x4b\x75\xd1\x46\x63\xec\xb3\x08\ +\x11\x4e\xbc\x6b\x7e\x13\x85\x45\xe8\x5a\xec\x62\xf3\xcd\x17\xbe\ +\xff\xe1\xc6\x68\x73\x2d\xfe\x98\x10\x5b\x6a\xa1\x31\xf2\xf7\x89\ +\x10\xce\x7c\x60\x9e\x1d\x00\x2d\x32\xc2\xdc\x73\x0f\x23\x44\x38\ +\x63\x7f\x1e\x60\x08\x21\xb6\xc4\x33\xc6\xb8\xdf\x55\x21\x21\xc2\ +\x99\x42\xcd\x36\x56\xe4\x93\x84\xd8\x7c\x5d\xb4\xda\xf8\xfb\x3f\ +\x6f\x62\xc3\xad\x9f\x19\x17\x0e\xfb\x9a\xe5\x42\x9a\xef\x2c\xe3\ +\x4b\x18\x6b\xd4\x8d\x10\xe1\x54\xb1\xd6\x98\x6a\xb2\x56\x67\x10\ +\xe2\xf7\xed\xe8\x41\xa0\x56\x3a\x42\x39\xa6\xbf\x3a\x4d\xe5\x44\ +\x0b\xa7\x56\xe8\x25\xd3\xfe\x39\x3a\x8a\x69\x69\x9a\xa7\x83\xca\ +\x8c\xd7\x5b\xbd\x08\x11\xce\xf5\x31\xff\x12\xd0\x9e\x10\x9b\xc3\ +\x7a\xc7\x75\x26\x11\xc2\x13\xd6\xd5\x83\x87\x13\x61\x73\x3c\x6c\ +\x8c\xf9\x18\x22\x84\x27\x4e\x30\x56\xe6\xbd\x44\xd8\x1c\xb5\xc6\ +\x13\xad\x76\x44\x08\x4f\xb4\x33\x3e\x9e\x56\x4d\x84\xdb\xfb\xfe\ +\x4d\xc0\xb1\xc6\x1b\x80\x53\xb4\x81\x58\xe1\x89\x0d\x9a\x62\xda\ +\x3f\x4f\xc7\x11\xe2\xee\x1a\xc0\x45\xa6\xbf\x57\xa5\xa9\x4a\x11\ +\x2b\x3c\x91\xd2\x34\x55\x99\xfe\xc2\x45\x84\xb8\x6b\x6d\xb4\x92\ +\x67\xae\x11\x62\x5f\x9a\xea\xb3\x82\x09\xc2\x76\x7d\x06\x30\x42\ +\x05\xa6\xbf\xf7\x38\x91\xc2\x53\x7f\x37\x7e\xc1\x31\x47\xe5\x2e\ +\x1b\xc0\x70\x63\x03\x60\x0e\x56\x78\xeb\xcf\xa6\xbd\x0b\xf8\x29\ +\x70\x57\x0d\x20\x5f\x07\x9b\xfe\xda\x7b\x5a\x49\xa4\xf0\xd4\x72\ +\xe3\x44\xf3\xc3\x59\x2a\x64\xe7\x3a\xeb\x6b\xd3\x15\x16\x4f\x5b\ +\xc3\x7b\x67\x1b\xef\x02\x30\x3b\xd0\x4e\x0d\x35\x45\xbb\x91\x68\ +\xe1\x83\x2e\xaa\x32\xd5\xe9\x41\x44\xb8\xb3\x4b\x00\xdb\x37\xf8\ +\xeb\xda\x48\xa0\xf0\xdc\x46\xbd\x6e\xda\xff\x87\x44\xb8\xb3\x06\ +\x70\x9a\xe1\x2f\xa5\x34\x43\x35\x04\x0a\xcf\x55\x6b\x86\xe9\x59\ +\x13\x2e\x54\x77\xa2\x93\xe9\xc4\x6a\x3d\x93\x2e\xc1\x27\xc3\x55\ +\x69\xaa\x55\xde\x0a\xdc\xe1\x19\xc0\xa9\xa6\xbf\xb4\x41\x73\x88\ +\x13\xbe\x78\x5f\x95\x5c\x04\xb8\x6f\x00\x27\x9a\xfe\xd2\xab\x3c\ +\x02\x0c\x9f\x34\x19\x5f\x39\x67\xc1\xfa\x1d\x5a\x6e\x3a\xad\x3a\ +\x92\x00\xe1\x9b\xe3\x4c\xb5\xba\x9c\x00\xbf\x6f\x90\xd6\x19\x22\ +\xad\x27\x40\xf8\x7c\x16\x90\x7e\xb5\xae\xd5\x00\x02\xdc\xfe\x12\ +\x60\xa0\xe9\x21\xe0\xe7\x88\x12\xbe\x7a\xc1\xb0\x6f\x81\x06\x12\ +\xe0\xf6\x0d\xa0\xbf\x69\x01\x45\x5e\x02\x82\xbf\x2c\x15\x97\xa3\ +\xfd\x08\xf0\xbb\x0d\x20\x4f\xfd\x0c\x7f\xa5\x46\xef\x11\x25\x7c\ +\x35\x4b\x75\x86\xbd\xfb\x29\x97\x08\xb7\x6d\x00\xf9\xa6\x06\xf0\ +\xb1\x36\x13\x25\x7c\xb5\x59\x9f\x98\x1a\x00\xf3\x02\x7c\xa7\x01\ +\x14\x98\x1a\x40\x29\x0d\x00\x3e\xab\x52\x29\x0d\xc0\x5d\x03\xe8\ +\xb3\xc3\x25\x42\x9a\x27\xa5\x52\x35\x10\x25\x7c\xd5\x60\x6a\x00\ +\x59\xea\x4d\x84\xdb\x36\x80\x51\xa6\x93\xb1\x52\x82\x84\xef\xe6\ +\x9a\xce\x3b\x47\x12\xe0\xb6\x0d\xe0\x10\x53\x03\x98\x4b\x90\x08\ +\xa0\x01\x58\x26\x08\x3d\x84\x00\xbf\x7b\x45\x95\xfe\x63\x15\x1c\ +\xfe\x08\xc6\x3c\x43\xd5\x32\x79\xfd\x36\x67\x00\x7b\x98\x6e\x89\ +\xbc\x4e\x8c\x08\xc4\x1b\x86\x7d\x0b\x79\x27\xf0\xdb\x06\x30\xd4\ +\xf4\x37\xfe\x49\x8c\x08\xc4\xcb\xa6\xbd\x87\x11\xe0\xd6\x06\x60\ +\x7b\x93\xff\x6d\x62\x44\x20\x6c\x95\xc7\xd4\x60\x4e\x1a\xc0\x06\ +\x26\x02\x43\x40\x2a\x4d\xb7\x01\x69\x00\xff\x6d\x00\x96\x77\xa3\ +\xde\x21\x44\x04\xe6\x5d\xc3\xbe\x83\x88\x6f\x4b\x03\x68\x67\x7a\ +\x0f\xf0\x5d\x42\x44\x60\x2c\x5f\x3f\x85\x2a\x24\xc0\x4c\x49\x3d\ +\x4c\xeb\x01\xcf\x22\x44\x04\xe6\xdf\x86\x7d\xb3\xd5\x9d\x00\x33\ +\x25\x75\x33\x35\x80\x4f\x09\x11\x81\xb1\x3c\x83\x92\xa3\x62\x02\ +\xcc\x94\xd4\xdd\xd0\x00\x96\xaa\x96\x10\x11\x98\x5a\xc3\xf4\x5e\ +\xb9\xea\x46\x80\x5b\xce\x00\xd2\x9f\x0a\x64\x09\x0d\x00\x01\xaa\ +\xd1\x62\x2e\x01\x6c\x0d\x20\xc3\xd4\x07\xcb\x4d\xd3\x32\x00\x36\ +\x75\x2a\x37\xec\x5d\x44\x80\x99\xca\x36\xad\xe8\x57\x41\x03\x40\ +\xa0\x0d\xa0\xc2\xb0\x77\x67\xd3\x34\x78\x34\x00\x49\xe5\xac\x06\ +\x80\x00\xa5\x4c\x67\x00\x5d\x68\x00\xb6\x06\xd0\x60\xea\xbf\x80\ +\xdd\x4a\xc3\x64\x34\x9c\x01\x18\x1b\x40\x1d\x0d\x00\x01\xb3\xdc\ +\x85\xa2\x01\x28\x53\x59\xea\x48\x03\x40\x64\x55\x18\x16\xa5\xe9\ +\x48\x03\xc8\x54\x96\xf2\x0c\x0d\xa0\x9c\x0a\x44\x64\xcf\x00\x5a\ +\x2b\x8b\x06\x60\x99\x16\xa1\x5e\x2b\xa9\x40\x04\x7c\x0f\xc0\xb2\ +\x2c\x5d\x21\x0d\xa0\xbb\xa9\x01\x00\xc1\x6a\x32\x55\x61\x37\x1a\ +\x40\x57\xc3\xde\x5f\x53\x7f\x08\xdc\x6a\x1a\x80\xa5\x01\x58\x9e\ +\x02\x58\x41\xf5\x21\x04\x77\x01\xd2\xb7\x07\x0d\xa0\xa3\x61\x6f\ +\x7e\x03\x40\xf0\x2c\x55\xd8\x91\x06\x40\x03\x40\x72\xcf\x00\x68\ +\x00\x34\x00\x44\x9c\xe5\x97\xa8\x4e\x34\x80\xf6\x34\x00\x70\x06\ +\x90\xdc\x06\x50\x40\x03\x40\x62\xef\x01\x14\xd0\x00\xda\x18\xf6\ +\xde\x44\xf5\x21\x70\x55\x34\x80\xa0\xce\x00\x98\x0d\x08\xc1\xab\ +\xa1\x01\x04\xd5\x00\xaa\xa9\x3e\x44\xba\x01\xb4\xa6\x01\xe4\x07\ +\x14\x3d\xe0\x46\x35\x0d\xc0\xd2\x00\xb2\x68\x00\x48\xec\x19\x40\ +\x2b\x1a\x80\xa5\x01\x70\x0f\x00\xd1\x6e\x00\xbc\x0e\x6c\xea\x81\ +\xbc\x0d\x88\xe0\xd5\xd1\x00\x82\x6a\x00\x40\xb4\x25\xbe\x01\xfc\ +\x3f\xf1\x1c\x11\x14\xdc\xaf\x20\xa0\x00\x00\x00\x00\x49\x45\x4e\ +\x44\xae\x42\x60\x82\ +\x00\x00\x54\xaf\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x01\x00\x00\x00\x01\x00\x08\x06\x00\x00\x00\x5c\x72\xa8\x66\ +\x00\x00\x00\x06\x62\x4b\x47\x44\x00\x2e\x00\x2e\x00\x2e\x69\xb7\ +\x4c\x3f\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x2e\x23\x00\x00\ +\x2e\x23\x01\x78\xa5\x3f\x76\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ +\xe4\x04\x14\x05\x24\x2d\xd9\x06\xea\x04\x00\x00\x00\x19\x74\x45\ +\x58\x74\x43\x6f\x6d\x6d\x65\x6e\x74\x00\x43\x72\x65\x61\x74\x65\ +\x64\x20\x77\x69\x74\x68\x20\x47\x49\x4d\x50\x57\x81\x0e\x17\x00\ +\x00\x20\x00\x49\x44\x41\x54\x78\xda\xec\x9d\x77\x98\x5c\x65\xf5\ +\xc7\x3f\xe7\x4e\xd9\x24\x74\x48\xe8\x26\x3b\x1b\x3a\xe1\x0d\xa1\ +\x4a\x17\x04\xa4\xa3\x58\xb0\x81\xa8\x28\x4d\x6c\x94\xec\x24\xb4\ +\x00\xd9\xd9\x50\x04\x7e\x14\xa5\x28\x52\x54\x04\x45\xa5\x4a\x13\ +\x41\xaa\x10\x20\x37\x74\x48\x66\x13\xa4\x86\x04\x02\x69\x3b\xe5\ +\x9e\xdf\x1f\xef\x5d\xd8\xec\xde\x3b\x3b\xe5\xce\x96\x30\xdf\xe7\ +\xd9\x27\x4f\xf6\xce\xde\xb9\xe5\x3d\xdf\x53\xde\x53\xa0\x81\x06\ +\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\ +\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\ +\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\ +\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\ +\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\ +\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\ +\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\ +\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\ +\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\x06\x1a\x68\xa0\x81\ +\x06\x1a\xa8\x0c\x12\xe5\xc9\x8c\x31\xf1\x84\xb0\xfb\xf4\x19\xee\ +\xbf\xfa\xfa\x6c\x67\x3a\xe5\x20\x6c\x2a\x2a\xdf\x01\xfd\x02\xb0\ +\x29\x90\x04\xde\x00\x1e\x03\xae\x51\xbc\xa7\x9b\x32\x73\xbc\xfc\ +\xa4\x94\xa8\x47\xcc\xc1\x19\xe5\xe1\x35\x23\xba\x9a\x28\x4b\x3c\ +\x95\x77\x44\xb5\x03\xc7\x29\x24\xdb\x67\x7b\x2b\xc2\x0b\xe9\x4c\ +\x37\x3b\xa8\x24\x11\xd9\x14\x74\x6b\x01\x03\x8c\x05\x52\xc0\x48\ +\x60\x0d\x20\xe1\x7f\x7c\x11\xf0\x3e\x30\x07\x78\x05\xe1\x69\x51\ +\x79\x1c\xd1\xd7\x13\x6d\xd9\x42\x63\x79\x37\xd0\x6f\x04\x70\xc4\ +\xb6\xe3\x86\xcd\xc8\x3b\x37\x03\xbf\x76\x5d\xf7\xee\x52\x9f\xcd\ +\xb7\xb6\x34\xab\xe8\xb9\xc0\xd7\x7d\xa1\x0f\xfc\x18\x70\x25\xaa\ +\xbf\x41\xe4\x9b\xc0\x57\x80\x4d\x00\xc7\xbf\x6e\xf5\x7f\xde\x03\ +\x1e\x04\xae\x45\x78\x28\x39\x04\x17\x7e\x67\xba\x39\x26\xea\x8c\ +\x46\xf4\x00\x60\x2f\x60\x57\x60\xad\x6e\xf7\x5a\x2e\x14\x28\x00\ +\x2f\x01\xb7\x82\x5e\x07\xce\xdc\x64\x66\xc5\x20\xc7\x06\x06\x29\ +\x01\x18\x63\xbe\x00\xdc\x15\x13\x9e\x2e\x2a\x7b\xb9\xae\x1b\x28\ +\x84\xb9\x74\xb3\x80\x1c\x00\xfc\x16\x58\xa7\xcc\x05\x5d\x04\x62\ +\x65\x5c\x6b\x11\x78\x42\x45\x7f\x2e\xe2\x4c\x4f\x4e\x9d\xad\x83\ +\xf9\xc1\xe7\x5a\x9b\x1d\x07\x67\x1d\x4f\xf4\x70\x9f\x08\xb7\xf3\ +\x35\x7b\x54\xa4\xac\xc0\x32\xe0\x26\x54\xda\xc5\x93\xd7\x12\xe7\ +\xcd\xd2\xc6\x92\x6f\x20\x52\x02\x18\x6f\xcc\xee\x0a\xf7\x39\x20\ +\xdf\xdd\xa0\xb8\xcf\xc9\x77\xbf\xf0\x50\xe8\xa2\x4f\xa7\x0e\x04\ +\x6e\x01\x86\xd7\xf1\x9e\x96\x00\x13\x41\x7f\x9d\xcc\x74\x14\x07\ +\xdb\x03\x2f\x9c\x96\x4a\x68\x91\x2f\x28\xfc\x18\x38\xc0\x7f\x16\ +\x52\xe7\xaf\x5d\x04\x9c\xaf\xc2\x05\x4d\x6d\xd9\x25\x7d\x7d\x78\ +\x82\x31\xc7\x15\xe1\x1f\xae\xeb\xbe\xd5\x10\x91\x15\x1b\x4e\x2d\ +\x7f\xbc\xdd\x78\x93\x88\x09\x93\x81\xa4\xc2\xff\x95\x12\xfe\x7c\ +\x6b\x6a\x7b\xe0\x86\x3a\x0b\x3f\xc0\x08\x60\x0a\xc8\xf6\x83\x4a\ +\xe3\x4f\x4a\x0d\xcf\x4d\x4a\x1d\xe5\x15\x79\x42\xe1\x6e\xe0\x6b\ +\xfe\xb5\x4a\x3f\x7c\xfd\xca\xc0\x59\xa2\xdc\x9b\x6b\x6d\xd9\xa8\ +\xaf\x0f\x17\xad\xfb\x31\xdd\x18\xb3\x41\x43\x44\x1a\x04\x10\x66\ +\xf6\x27\x72\xca\x3f\x0a\xca\xbe\x80\xa7\x70\x7f\xa8\x8f\x3b\xa9\ +\x65\x15\x15\xae\xc5\x06\xb0\xea\x09\x0f\xb8\x49\x94\xf1\xa8\x3c\ +\x39\x28\xfc\xfb\xd6\xb1\xc3\x73\xe9\xd4\xf7\x51\xa6\xa3\xfc\x16\ +\xd8\xc6\x77\x69\x06\xc2\xda\xdb\x05\xd1\x87\x72\xe9\xd4\xce\x7d\ +\x7c\x76\x2a\xf0\x4e\x83\x04\x20\xdf\x3a\x46\x56\xe4\xfb\xab\xfa\ +\xe6\xb6\x35\x66\x52\xde\x2e\x14\x80\xff\x40\xb0\xef\x9f\x6b\x4d\ +\x09\xc2\x99\xc0\x19\x75\xd6\x76\x1e\x70\xb6\xc2\xd4\xa6\xcc\xc0\ +\x07\x02\x97\x4d\x1c\xdb\xe4\x38\xde\x37\x81\x53\x81\xcd\x6a\xb5\ +\xb6\x22\xc6\x02\xe0\xd0\x64\x26\xfb\x48\x09\x82\x5f\x07\x98\xd1\ +\xf5\xba\x5d\xd7\x7d\xf3\xb3\x23\xf4\x2d\xe2\x88\xb7\x6a\x11\x39\ +\x04\xd8\x05\xf8\x4b\x32\x93\xbd\xbf\x41\x00\x9f\x2e\x8e\xbd\xb0\ +\x66\x6c\xd2\x01\x76\x5e\x4d\xcf\xb8\xe2\x3f\x33\xcf\x09\x34\x7d\ +\x5b\x53\x9f\x43\x78\xd1\x37\x43\xeb\x89\x3f\x01\x47\x26\x07\x58\ +\xf8\x73\x93\x5a\x04\xd5\x1d\x81\xf3\xfc\xc5\xe3\x0c\xd2\x77\x3f\ +\x0f\xd8\x23\x99\xc9\xbe\x54\xe2\x3d\x9f\x06\x9c\x03\xbc\xbb\xa2\ +\x93\x40\xbe\xb5\x45\xf0\xbc\x84\x17\x63\x7b\x41\x0e\x07\xbe\x05\ +\xac\xe9\xbf\xbf\xeb\x15\x8e\x6a\xca\x64\xf5\x33\x4f\x00\xc6\x98\ +\x26\xe0\x83\x6e\xbe\x7c\x61\xf3\x11\xba\xea\x9f\x9f\x98\xb9\xb4\ +\x97\x30\xd8\xa8\xff\x64\xe0\xec\x3a\x6b\xff\x45\xa2\x8c\x4b\xb4\ +\x67\xe7\x0c\xe8\x22\x9a\x98\x5a\x47\x1d\xce\x02\xbe\x0f\x34\x0d\ +\x81\xf7\xff\x92\xa0\xbb\x24\x32\x1d\x1f\x84\xbc\xeb\xb5\x7c\x2b\ +\x60\x03\xec\x76\xeb\x78\xd7\x75\xdf\x19\x4c\x37\x50\x48\xb7\xac\ +\xe6\xa1\x5f\x45\x59\x24\xc2\x4b\x45\x91\xd7\x1d\x8f\x9c\x8a\xa7\ +\x82\xa8\x87\x10\xc3\x23\x91\xe9\xd0\xce\xc9\x29\x11\x4f\x50\x3c\ +\x11\x11\x51\xd5\x18\x2a\xcd\x22\x7c\x1e\xd8\x19\xd8\x0f\xd8\x30\ +\xc0\x45\x3b\x2b\x99\xc9\x4e\x59\x11\x89\x2f\x5e\xc5\xdf\x9c\xcc\ +\xf2\x81\xbc\xc7\x5e\x5a\x22\xb9\xc0\x00\x43\x8c\xa4\x57\xe4\xc7\ +\xd4\x3f\xd0\xf5\x0c\xe8\x80\x45\xac\xf3\xa7\xb4\xc4\x34\xa6\xdf\ +\x50\xe1\x57\xc0\xba\x43\xe8\xfd\x6f\xae\xc8\x45\xcb\x4e\xdb\xe8\ +\x87\xc3\xce\x7d\xbd\xd7\x8e\x89\xeb\xba\xf3\xbf\xb8\xed\xf8\x7d\ +\xe7\xe5\xf5\x79\x60\x6d\xe0\xa7\xc6\x98\xc9\xae\xeb\x0e\x1a\x4d\ +\xe8\xa1\x3f\x02\xce\x43\x50\x05\xcf\x51\x5d\x86\x30\x4b\x90\xb7\ +\x81\x77\x1d\x74\xa1\x22\xf9\x5c\x3a\x55\xc4\x63\x18\xe8\x2a\x82\ +\xac\x81\x32\x46\x90\x66\x84\x55\x28\xbd\xcd\x5c\x04\x9e\x58\x51\ +\x2d\x9f\x8a\x08\x60\xef\xad\xb6\x6a\x7a\x0f\x26\xf7\xf8\xf5\xe3\ +\xae\xeb\x06\x6e\xb7\x79\x45\x0c\xb0\x5e\x3f\xdc\xc7\x7b\xea\x38\ +\x03\x62\xfa\xe7\xd3\xcd\x23\x15\xfd\x95\x6f\x32\xc6\x23\x38\x65\ +\x11\xe8\x04\x5e\x04\x5e\xf3\xad\xad\x8f\x81\x95\xb0\xd1\xf9\x14\ +\xb0\x95\x4f\xc2\x51\xb8\x17\xdf\x8c\x15\x8b\x57\x03\x8f\x06\x1d\ +\x9c\xb0\x7a\xfe\xa5\xfb\xe6\xc5\x67\xaa\xcd\x48\x6c\xc5\xe6\x17\ +\x4c\x1e\x44\x46\xec\xb3\xa0\xe2\x0b\xb0\xe3\xbb\x9a\xe3\xfd\x9f\ +\x48\xc2\x39\x8a\xba\xcb\x4e\x6b\x11\xc7\x53\x51\xc5\x71\xa4\x29\ +\xa6\x9a\x5b\x1d\x74\xa4\xff\x4e\x46\xf8\x3f\xc3\x44\xf0\x54\x29\ +\x62\x13\xd9\x3e\xf2\xdf\xdf\x07\x0e\xfa\x8e\x87\x14\x14\xbc\xa6\ +\x4c\xd6\x1b\x92\x04\xf0\xbe\xc8\x29\xdd\xb5\xbf\xef\xff\x2f\x72\ +\xc3\x5f\xce\xae\xf4\x4f\xc4\x7b\x35\x55\x62\xd8\x2c\xb8\xfe\xf1\ +\xf5\xd3\x29\x01\xf6\x55\xf8\xb5\x2f\x94\xb5\x62\x0e\xf0\x37\x51\ +\x6e\x17\xbc\xa7\x3c\x71\x96\xa8\xaa\x82\x68\x53\x7b\x56\x3b\x6d\ +\x6c\x41\x04\x44\x44\x56\x52\xd5\xbd\x80\x6f\x03\x07\x03\xc3\x6a\ +\xf8\xde\x26\x85\x8b\x73\x93\x9a\x77\x4f\xb6\x75\xf4\x72\xe3\x2e\ +\x78\xe0\x45\x35\xc6\xdc\x8c\x25\x00\x01\x8e\x35\xc6\xfc\xda\x75\ +\xdd\xff\x0d\x8e\x25\xac\xcf\xfa\x82\xb6\x6a\x9d\xbe\x60\xa1\x20\ +\x87\x49\x51\x5b\x80\x16\x81\xb1\xaa\x9d\x29\xdf\xc5\x93\x6e\x3f\ +\xf6\x6a\x96\xb7\x8d\xba\xfe\xa7\x1e\xd2\x09\xcc\x15\x98\x9d\x4b\ +\xa7\x66\x00\xff\x75\xd4\x79\xca\x13\xef\xdd\x64\x26\x5b\x51\xbe\ +\x8a\x31\x26\x16\xa6\x74\xeb\x16\x03\x08\xf0\xfd\x01\x0a\x5b\x8c\ +\xd0\x55\x6f\x0a\xf0\xff\x3b\x5b\x53\x22\xc2\x75\xc0\x11\xfd\xb0\ +\x0a\xe6\x7b\xaa\x9b\x0d\x6b\xef\x78\xbf\x3f\x96\xdc\xb2\x49\x2d\ +\x49\x47\x35\x0d\xa4\x23\xf4\xf5\x0b\xd8\xad\xb7\xb7\x80\x17\x15\ +\xee\x43\xe5\x01\x84\xf7\x9a\x32\xc1\x59\x8d\x9d\xe9\x66\x47\x90\ +\x4d\x81\x29\xd8\x54\xe9\x6a\x2d\x90\xa2\xc0\x77\x12\x99\xec\x9f\ +\x4b\xc4\x02\x9e\xef\xe6\xde\xbc\x02\x6c\x3e\x18\x5c\x81\x5c\x6b\ +\x6a\x04\xc2\x33\xd8\x5a\x92\xba\xb1\x4c\x1d\xdc\x58\xcf\x7f\xe7\ +\x4f\x01\x7f\x17\xf8\x9b\x42\x36\x59\x86\x75\x60\x8c\x99\x12\xb3\ +\xe9\xef\x0f\x3f\xeb\xba\x35\x59\x13\x95\x98\x90\xa7\x10\x90\xc4\ +\xf3\xe2\x12\xf1\x4a\x90\x4b\x73\x3f\xad\x83\xb5\x1c\x91\xb3\x3a\ +\xd3\xa9\xba\x5b\x1b\xb9\x49\xcd\x6b\x39\xaa\x7f\xc4\x6e\x6b\x46\ +\x19\xe8\x8b\xfb\x01\xa8\x1d\x80\xa3\x04\x6e\x10\xd1\x57\x05\xcd\ +\xe4\xd3\x9b\x04\xee\xa0\x34\x65\x3a\xbc\x64\x26\xfb\x92\xc4\xbc\ +\x6f\x83\xfc\xd0\x77\x15\xaa\x41\x4c\x61\xf2\xb2\xd6\xe6\xc0\xfb\ +\x71\x5d\x77\x3e\xd0\x7d\x07\x60\x13\x3e\xdd\x02\x1e\x58\x07\x20\ +\x4e\x27\xf0\x76\xbd\xbf\xa6\x0e\xe7\x74\xb0\x75\x30\xbb\x00\xe7\ +\xa9\x25\xd8\x1b\x72\xe9\xd4\x76\xb9\x74\xaa\x2f\xb9\x9c\x5a\x84\ +\xd3\x8b\x70\xbb\xaf\x98\xeb\x4b\x00\xc6\x98\x04\x30\xa9\xc2\x27\ +\x26\xfe\x82\xee\x2f\x1c\x2b\x30\xb1\x33\x9d\x4a\x44\x7d\xe2\xce\ +\xd6\x66\xc9\xb5\x36\xc7\x72\xe9\xd4\x56\xa8\xfc\x1b\xf8\x2a\xf5\ +\xdf\xde\x73\x7c\xb3\xf6\x54\x25\x7f\x67\xe7\xa4\xe6\xd5\xc2\x3e\ +\x98\x38\x77\x4e\x21\x99\x99\x7d\xbd\xd8\x28\xf6\xfc\x2a\xbf\x6f\ +\x13\x11\xd9\xa2\xc4\xf1\x23\x7a\x08\xc4\x31\xc6\x98\xf5\x06\x9a\ +\x00\x12\xe7\x66\x8b\x40\xc7\x10\x8f\xc5\x89\xef\xc6\x7d\x1b\x78\ +\x4c\xe0\xaa\x7c\x6b\xf3\xc8\xb0\x0f\xbb\xae\x9b\xf3\x5d\xbf\xbd\ +\x81\x0f\x8d\x31\x7b\xd6\x95\xd9\xfc\x2f\xb8\x2f\xc0\x9f\x2f\x00\ +\x2b\xbb\xae\xdb\x19\xe0\x23\xc7\x80\x59\xc0\x98\xfe\x74\x08\x81\ +\x87\x80\xf3\x29\xc6\xff\xa5\x4e\x21\x8f\xaa\x4a\xcc\x51\x3c\xc1\ +\x73\x3c\x86\xb5\xf5\xde\xcb\x5d\x3a\x79\xb4\xc4\xbc\x18\xa0\x0e\ +\x22\x22\xe0\x78\xca\x3a\xa2\x6c\x8a\xb0\xb9\x6f\x5e\x6e\x0e\x6c\ +\x0f\xac\x32\x40\x8b\xe4\x7a\xf5\x62\x3f\x68\x9a\xf6\x7a\xb1\x0f\ +\x93\x78\x57\x84\xbb\xaa\xb9\x4e\x85\x3f\xc4\x63\xa3\x8e\x88\x9d\ +\xfb\x5f\x2d\xc3\x0d\x00\x68\x77\x5d\x37\x3d\xe0\x6e\x40\x3a\x75\ +\x11\xf0\x73\x56\x2c\xcc\x02\x39\x5a\x91\x87\x9a\x32\xc1\x45\x5c\ +\xc6\x98\x03\x80\xdb\x7d\x39\xdc\xcf\x75\xdd\x07\xeb\x45\x00\xa7\ +\x63\xf7\xf2\xa9\x90\x00\x5e\xf4\xcd\xc5\xfe\x86\x87\xcd\x76\x9b\ +\xe1\x9b\x87\x4b\x7c\x72\xe8\x8a\xcc\x7e\x8c\xb2\x0c\x61\x38\x36\ +\x7a\xbb\x12\x36\xe9\x63\x43\xec\x9e\xf7\x06\x7c\x5a\xa4\x53\x69\ +\x49\x6e\xdd\x42\x0f\x08\x3b\x25\xdb\xb2\xcf\x95\x21\x10\x3f\x02\ +\x7e\x53\x85\x95\xf2\x16\x2a\xe3\x92\xed\xb3\xc3\xf2\x02\x9e\xc2\ +\x56\x2d\x76\x61\x01\xb0\xd5\x40\x17\x0d\xe5\xd2\xa9\xb3\x80\x33\ +\x59\xf1\xb0\x08\xe4\xbb\x9e\x70\xdb\xb0\xb6\xd9\x41\xa4\x1c\x03\ +\xfe\x05\xec\x0e\xe4\x80\xfd\x5d\xb7\xef\x5e\x1c\x3d\xfd\xce\x92\ +\xd8\x71\xbc\x89\xe5\x94\x3d\xaa\x08\x39\x2a\xb0\x70\x80\x1e\x9c\ +\x83\x6d\x9e\xf1\xc5\x12\xd4\xa7\x83\x44\xb0\xcb\xc5\x30\x94\xc9\ +\xc5\x33\xc6\x7e\x23\x76\x76\x5f\x65\xbd\xfa\x07\x90\xe3\x81\xad\ +\x2b\xfc\x8e\x51\x38\x6c\x80\x0d\xf6\xf6\x3e\x98\x90\xef\xcd\xcb\ +\xeb\x0b\xdd\x7e\xb5\x26\xf0\x53\xec\xf6\x60\xff\x33\x62\xeb\x58\ +\x71\xb4\x98\xa4\xfe\x35\x26\x03\x85\x95\x41\x6f\x70\x54\xf6\x25\ +\x20\x17\xc1\x75\xdd\xa2\x31\xa6\x0d\xdb\x3f\x22\x09\xdc\x6d\x8c\ +\xa9\x88\x04\xfa\xd4\x10\x4b\x15\xa7\x08\xbb\x55\x7e\x0e\x55\x6c\ +\x63\x8a\xc1\xec\x77\x0d\x35\x6c\x5f\xc8\x7b\x7d\x9a\xf6\xc9\x4c\ +\xc7\x12\x6c\x90\xb2\xd2\x08\x71\x02\xd5\xfd\xc2\x0e\xce\xcb\x6b\ +\x3e\xe0\xd7\x3f\x36\xc6\xac\xdf\x9f\x0f\x21\x3f\x29\x15\xcf\xa5\ +\x53\xe3\x1d\xf1\x32\x38\xf2\x32\xf0\x13\x56\x5c\xac\x02\xfa\xbb\ +\xce\xd6\x96\xb0\x6d\xce\x7f\xfb\x96\x2d\xdd\x48\xe0\x0b\x91\x11\ +\x80\xcf\x2e\xb1\x12\x16\xc4\x69\x21\x8b\x50\x7d\x17\xa0\x81\xe8\ +\xb0\xbe\x53\x76\x62\x95\x3c\x4c\x75\xd1\xf1\xc3\x8a\x67\x6e\x1e\ +\x48\x8e\x7b\xac\xae\xc5\x35\x62\xf4\x24\x81\x35\x80\xcf\xd5\xfb\ +\xc6\x3b\x5b\x53\x4e\x67\xba\x65\xed\x5c\xba\xe5\xa7\xaa\x3c\x02\ +\x3c\x0d\x4c\xc4\xee\x34\x39\x2b\xf8\x7b\xdf\x4c\x44\x8f\xcd\xb5\ +\xb6\x48\x80\x15\xd0\x09\x5c\xd2\x5d\xf4\x80\x49\x7e\xe0\x3e\x12\ +\x02\xd8\x8d\xd2\xc9\x3c\xa5\x8e\x3d\x8d\xcd\x6c\x6b\x20\x1a\x24\ +\x54\x18\x57\xce\x07\x93\x99\xd9\x0b\xb1\x81\xdb\x8a\x49\xc6\xcb\ +\x77\x06\xf6\x6c\xd8\x7c\x35\x2f\xfb\x61\x91\x17\x02\x0e\x5d\x5f\ +\x37\xc1\x9f\x94\x4a\x74\xa6\x5b\xf6\x10\xe1\x77\x82\xbe\x06\x7a\ +\x31\xb0\x23\xd1\x64\x5d\x0e\x15\x08\xf0\x13\x0f\x6f\x44\x98\x51\ +\xd4\xe3\xff\xfb\xc4\xa4\x3c\xb7\x2c\x0a\xe6\xdc\xc9\x0f\x46\xf4\ +\x82\x27\x3c\x8e\x0d\x14\x35\xf0\x69\x5c\xa4\xd6\xe4\x99\x0d\x2a\ +\x58\x35\x37\x57\x71\xfe\x95\x14\x0d\x74\x33\x8e\xbf\xfd\x05\xd5\ +\xe0\xeb\x1f\x15\xe6\x06\x18\x63\xc4\x18\x53\x91\xbb\xd5\xd9\x3a\ +\x56\x0a\x13\xc7\xae\x91\x4f\xa7\x4e\x14\xe5\x29\x41\x1f\x00\xbe\ +\x87\xdd\x16\x95\xcf\xe8\xda\x59\xdf\x11\xd9\x25\xe4\xd8\x54\x7a\ +\x64\xc1\x16\x95\xd3\xca\xd9\x1e\x8c\x82\x45\x77\x8e\x09\xf1\x20\ +\x4d\x2f\xca\x52\xe0\x4e\xe0\xa8\xcf\xe0\x0b\x2b\x62\x7b\xf2\x4d\ +\xc7\xe6\xf4\xff\x0f\x78\xcb\x0f\x3d\x8e\x02\x26\x00\xfb\x50\x69\ +\x0a\xab\x96\xd5\x4b\xd1\xff\xa8\xbe\x06\xb2\x04\xbb\xd3\x51\x2e\ +\x56\x46\x59\x05\x5b\x02\x5c\x2e\xd6\x00\x46\x63\xb3\x18\xbb\x0b\ +\xff\x5a\xd8\xba\x81\x9b\x80\xff\xf6\x75\x92\xdc\xc4\x94\x23\x8e\ +\x8e\x55\xbc\x63\x3c\xe1\x7b\xd8\x3c\xfb\xcf\xaa\xc0\x07\x59\xda\ +\xbb\x01\xf7\x06\xe9\xda\x20\x23\xd0\x77\x05\x1e\x2e\x95\x36\x5c\ +\x92\x00\xb6\x1b\x6f\x62\x05\x65\xf7\x3e\x22\x49\x4e\x0c\xf6\x04\ +\xfe\xd9\xf3\x40\x53\x26\xab\xf9\x74\xcb\xe5\x8a\x7e\x9b\xf0\xee\ +\xbf\x2b\x12\x3c\x5f\xd0\x6f\x01\xee\x75\x28\x3e\xe9\x69\x7c\x11\ +\x78\x5e\xb2\xbd\x63\x39\xcd\xd9\x99\x4e\x39\xe2\xe8\x7a\x78\x72\ +\x19\x70\x68\x05\x0b\x7d\x51\x05\x96\xe3\x22\x60\x69\x85\x04\x30\ +\x0c\x64\x44\x15\xf7\x7e\x3d\xfe\x96\xaf\x31\x46\x04\x8e\x56\x2b\ +\xfc\xb2\xcf\xc8\xc2\x49\x6e\x89\x3f\xcc\x4f\x4c\x39\x2a\x32\x01\ +\xd1\x93\x14\xf9\x32\xf5\x6f\x1b\x37\x54\x31\x3e\x97\x4e\x39\xc9\ +\xf2\x8b\x89\xf6\xc4\x6e\x11\x3e\x58\x15\x01\xe4\x14\x87\xd2\x3b\ +\x00\x00\x4e\xd1\x6e\xb7\xfd\x33\x58\x22\xf4\x19\xdf\x14\xfd\xee\ +\x0a\xfc\x62\xf2\xc0\x3f\x51\xae\x14\xd1\x07\x8a\xd0\x39\x2c\xd3\ +\x51\xd2\xd4\xf7\x2b\xc2\xde\xcc\xa5\x5b\xbe\x07\xfa\x18\xb0\x65\ +\x99\x7a\xbd\xec\x7a\x07\x45\x96\x09\xda\x59\x85\xc3\x59\x0d\x01\ +\xc4\xbf\x3b\x6e\x9c\xb8\x8e\x03\x70\xb2\xc2\x34\x9f\xd4\x2e\xbc\ +\xf0\x5f\x2f\x06\x3e\x8b\xc2\x69\x1b\x3b\x5a\x2c\xec\xa0\x70\x0a\ +\xe8\x41\x9f\x11\x25\x51\x93\x1b\xa0\x95\x39\x90\xb1\xbe\xac\x80\ +\x48\xa2\xa7\x45\xe5\xa7\xc6\x98\x11\xe1\x0b\x5d\xd3\xd4\x3f\x5f\ +\x7b\xa0\x7c\xfa\x7b\x41\xf6\x10\x87\xaf\x24\xdb\xb3\x77\x26\x32\ +\x1d\xcb\xfa\x12\xfe\xe5\xec\xb4\xcc\xec\x8f\x80\x4b\x2b\x10\xea\ +\xb9\xe5\x7e\xd6\x11\xf5\xaa\x89\x39\x78\x68\x35\xdd\x9b\x9a\x5d\ +\xc7\xd9\x0e\x5b\x33\x32\xad\x9b\x45\xf3\x97\x40\xc6\x6c\x6d\x89\ +\x7b\xc5\xc2\x14\xb5\xdb\x58\x87\x35\x84\xbf\xbc\xf8\x8c\x48\xc5\ +\x2e\xd1\xee\x7c\x3a\x48\xa6\x2e\x31\x80\x2e\x7f\xe3\x4c\xec\xb6\ +\x4c\xc0\x22\xef\xf8\x5f\x2e\x9d\x3a\x0e\x6b\x09\xac\x48\x2f\xfa\ +\x11\x41\x0f\x4b\x64\xb2\x8b\x6b\x3c\xcf\x4c\xca\xab\x38\x5b\x2c\ +\x36\x1d\xb7\x3c\xb2\x50\x12\xd5\xbc\x63\xb1\x99\x91\x55\xfc\x19\ +\xa7\x60\xbb\x1d\x77\xdd\xc7\x87\xbe\x4b\xd4\xfb\xda\x1c\xef\x00\ +\x54\xd2\x0c\x4c\x83\xd4\x20\x2c\xc4\x4e\xa4\x2a\x74\xfb\x89\x63\ +\x53\xaa\xbb\x7e\x56\xc7\x26\x98\x39\xdd\x7e\xfa\xd5\xd2\x54\xad\ +\x4a\x36\x4f\x06\xce\xad\xd6\x02\x28\xd7\xdf\xf8\x79\x98\x15\xe0\ +\xab\xae\xdb\xb0\x05\x45\x2b\xca\xb6\xe0\x6c\x75\xf4\xf0\x44\xa6\ +\x63\x71\x04\xe7\x2a\x37\x6f\xff\x6d\x11\x3e\xac\xc0\x40\x19\x46\ +\x15\x15\x8b\x6a\xe3\x06\xd5\xe0\xeb\x3d\x48\xec\xc3\xd0\xbe\x01\ +\x2a\x47\x0f\x22\xe1\xf7\x80\x49\x88\x1c\x0c\xfa\x65\xd0\xaf\xa9\ +\xf2\x4d\xe0\x6b\x88\xec\x87\xb0\x8b\xaa\x8c\x07\xc6\x78\x2a\x6b\ +\x23\xb2\x1d\xc2\x91\xc0\x05\xd8\x72\xde\x4e\x6a\xdf\xdd\x29\x07\ +\xf3\xaa\xfc\x9e\x2f\x84\xed\xd4\x95\xd4\x0e\xae\xeb\xe6\x27\x8c\ +\x37\xe7\x15\x95\x33\xca\x64\x9a\xb3\xb0\x5d\x70\x7b\x1f\x6c\xcf\ +\x6a\x67\x3a\x75\x91\x58\xd2\x69\x63\x68\xef\xe3\x2e\x40\xf9\x46\ +\xd3\xd4\x8e\xa8\xdc\x9a\x03\x28\x2f\x08\xf8\x92\x13\x6f\x5a\x52\ +\xfe\x69\x9d\xe1\xa0\x95\x12\x80\x8a\xed\xff\x57\x33\x56\x76\x58\ +\xf5\xd2\x83\xb6\x5c\xf7\xc4\x3b\x5e\x08\xea\x23\xd8\x59\xc3\xa9\ +\xd5\x17\x5a\x89\x48\x0b\xcf\x12\xe5\x86\x44\x66\x76\x39\xca\x69\ +\x21\xb6\xc6\x64\x46\x67\x6b\xf3\x1f\x1d\x75\x1c\x44\xd6\x53\xf1\ +\xbe\x0e\x1c\x89\x6d\x9c\x52\x2f\xcb\x60\xae\x48\x55\x04\xb0\x9b\ +\x7f\x4d\xc5\x8a\x2d\x80\xa2\x56\xa4\xb1\x7f\x61\x8c\xd9\x3f\xec\ +\x60\x53\x26\xeb\x21\x5c\x88\xdd\xd3\x1d\xaa\xf9\x01\x79\x51\x3d\ +\x21\xd9\x9e\x9d\x5e\xce\x87\xc7\x1b\x93\x30\xc6\x84\x92\xdd\xd2\ +\xd6\xd1\x09\x60\xff\x32\x8d\xec\x5b\x62\x67\xbf\x5c\xfe\x02\x10\ +\x5d\xa7\x0a\x0b\xa0\x50\x83\x05\xb0\xbc\xbf\xe2\xb1\xe6\xb5\x6f\ +\xc4\x9a\x43\x0e\xff\x1f\xe5\x77\x70\xea\x1a\x11\xf7\x2e\xf0\x77\ +\xdf\xd5\xd8\x03\xdb\x8e\xbe\xe6\x10\x16\xe8\xe4\x44\x7b\xb6\xe2\ +\x5e\x0a\x4d\xed\x1d\x9a\x98\x36\xbb\x98\x68\x9f\xf5\xbf\x64\x26\ +\x7b\x11\x9e\xee\x88\x0d\x76\xbf\x5e\xa7\x78\xd3\x8c\x64\x75\x9d\ +\x89\x65\xc7\xd5\x9c\x78\xb5\x2e\x40\x25\x88\x63\xa3\x8e\xa1\x8b\ +\x2e\xd9\x96\xf5\x92\x99\xec\x1f\x51\xdd\x15\x3b\x4c\x64\x28\xb9\ +\x04\x0a\x5c\x0e\xb1\x5b\x4a\x7d\xc8\x18\x93\xd8\xde\x98\x4d\x26\ +\x8c\x37\x67\xf9\x8b\xf6\xb6\x50\x1d\x2d\xce\xe7\x28\xaf\x91\xe8\ +\xc7\x78\xfa\x40\x85\x57\xbb\x7f\x15\xef\x38\x27\xb6\xb2\x2c\x92\ +\x87\x35\x2c\x24\x00\xa5\xb6\xb8\xe5\x86\x12\x26\xad\xfa\x56\xc2\ +\x83\xc0\xc9\xa8\xec\xe0\xe0\x8c\x51\xe1\xab\xc9\x4c\xf6\x42\x51\ +\x7d\x8d\x68\xfa\xfe\x3d\xe3\x39\x7a\x5b\x24\x81\xb0\x69\x1d\xb9\ +\x64\x26\xfb\x27\x85\x9d\xb0\xb9\x0f\x51\xba\x05\x85\x12\x84\xf7\ +\x85\x3e\xde\x73\xe2\xc9\x85\xde\xc9\xd5\x12\x40\xa5\x02\xba\x2b\ +\x70\xef\xb6\xe3\x4d\x49\x13\x3f\xd9\xde\xf1\x92\x38\xc3\xf7\x07\ +\xbe\x03\x3c\x3b\x44\x88\xe0\x11\x81\x49\x89\xf6\x59\xc5\x12\xc2\ +\xbf\x09\xf0\x6e\x27\xbc\x50\x54\xce\x54\x9b\x24\x73\x4c\xb8\x52\ +\x77\x76\xa6\xbc\x99\x09\xb3\x11\x29\x7b\x0b\xd0\x4b\x8f\x75\xca\ +\xb6\x2c\x7a\x28\x6e\xad\x28\xd7\xa0\x34\x16\x29\xbf\x0f\xca\x04\ +\x6c\xca\x64\xf3\x2a\x9c\x00\x5c\x8c\x4d\x98\xea\x12\x96\x1c\xf0\ +\x10\xc8\xf1\xa2\x9a\x02\xf6\x49\x66\xb2\xbf\x4a\xb6\xcf\x7e\x26\ +\x9e\x99\xd5\xd9\xd4\x66\xf7\xc0\x3d\x91\xaf\x63\x83\x72\xb5\x20\ +\x07\x9c\x32\x6c\xea\x9c\xce\x28\x17\x49\x53\x26\xfb\x3e\x9e\x7e\ +\x0f\xb8\x2e\x42\x12\x78\x86\x82\x3e\x57\x25\x01\x84\xca\x7a\x39\ +\x7e\xf8\x85\xd8\xca\xb2\x64\x85\x24\xb0\x3f\xb6\x59\x41\x38\x2d\ +\x4d\x7d\xb1\x00\xfc\xb9\x30\x39\x75\xab\xe7\xb1\xaf\x4f\x06\x07\ +\xf8\x41\xb1\x28\xad\x93\x28\x4a\x7f\xdf\x44\xf8\x6e\xa2\x2d\xbb\ +\xb4\x0f\xe1\xff\x2f\xd0\xbd\x7b\x4f\x16\x82\x03\x77\x85\xe3\xd7\ +\x16\x0f\xfd\x41\x99\xd7\xf6\xf7\x64\x26\x5b\xb6\x66\x2e\xe0\x8d\ +\xc4\x66\xe7\x55\x8a\x8f\x63\x78\x1f\x05\x1d\x98\x60\xcc\x5a\xc5\ +\xca\xbb\x3c\xa7\xb0\x3d\xf7\x7b\x75\x1d\x6e\x6a\xcb\x2e\xcd\x9f\ +\x9a\x3a\xd9\x73\xb8\x5a\x84\xfd\x81\x55\x11\x6e\xc1\x93\x97\x93\ +\xed\xe1\xfe\x78\xbe\x35\x95\x54\xeb\x6f\xd7\x8a\x9b\x44\x0a\x0f\ +\x87\xbc\xcb\x58\xc8\x1a\x2c\xba\x65\xf4\xe1\x4b\x4e\xeb\xc8\xe5\ +\x27\x6d\x72\xbc\x6a\x7e\x0b\x6c\x9b\xb7\xda\xdc\x4e\xe4\x8c\xc4\ +\xf9\xbd\x9b\x87\xfa\xd7\xb9\x73\xb5\x27\x2e\x87\x00\xaa\xd1\xcc\ +\x4e\x5e\xb9\xd5\x18\x73\x98\xeb\xba\xb7\xf7\x79\x11\x53\xb3\x79\ +\xe0\xce\xce\x74\xcb\xdd\x82\xb7\x2a\xc8\x6e\x3e\xab\x6d\x0f\x6c\ +\xe1\x0b\x54\xcf\x0e\xac\xd2\x83\x5d\xb5\xdb\x4f\x97\xbf\xf8\x2a\ +\xb6\x22\x71\x75\xdf\x37\xab\x96\x54\x72\x22\x9c\x90\x68\xcb\x86\ +\xee\xc1\xff\x74\xcf\x2d\x9d\x7f\xcf\xe7\xc1\x1e\xc2\x0f\x70\xa1\ +\xeb\xba\x81\xfe\xa5\xb7\xda\x88\x0d\xb1\xb3\x02\xfb\xfc\x7e\xb5\ +\x26\x65\x25\xd8\x11\x9b\x4a\x5b\x29\x5e\x89\x67\x82\x35\x62\xd1\ +\xd6\xff\x57\x3a\xf7\x40\x80\x7f\x18\x63\x26\xb8\xae\xfb\x46\x2f\ +\x25\x70\x5e\xd6\xc3\x96\x8d\xbf\x54\xc1\x19\x37\xa7\xf2\x5e\x07\ +\x3d\xb1\x40\xe0\x8c\x44\xdb\x1b\x61\x1a\xfa\x06\xec\x96\x66\x4f\ +\xfc\xc3\x18\xf3\x82\xef\x9e\x5c\x08\x14\xc2\x08\x21\xd1\xf6\xea\ +\xd2\x5c\xba\xe5\xa7\xa0\x8f\x50\x7d\xd0\xdb\x53\xb8\x00\x34\x6c\ +\x34\xd9\xde\xbe\xc2\x2d\x2d\x63\x52\xbd\x05\xe0\x01\x0f\xfb\x5f\ +\x54\x29\xb9\xdc\x3a\x61\xbc\xc9\x14\x95\xa9\x41\x5d\x83\x7a\x9b\ +\x4e\xb3\x3d\x5f\x5b\xde\x9e\x9f\x3c\xe6\x0e\xf5\x1c\x47\x1c\x62\ +\x14\xd9\x40\x85\x0d\x40\xd6\x43\x74\x75\x3c\x46\x20\x34\x89\x0d\ +\x58\x15\x6d\xbe\xbb\x2e\x50\x58\x80\xca\xdb\x31\xcf\xf9\x9f\x17\ +\xf3\x96\xa9\xaa\x8a\xc3\x5a\x28\x8f\xd6\x68\x51\x5c\x83\xa7\x77\ +\x94\xd0\xfc\xce\xbf\xe7\x73\x19\xb0\x7e\xc0\xb3\xfb\x6b\x89\x95\ +\xbc\x37\xe5\xd5\x02\x74\x88\x50\x76\x02\x90\x77\x46\xb3\x53\xc8\ +\xf3\xb3\x6a\xac\x1e\x81\xbb\x6a\x54\x18\x41\x58\x0b\x1b\x20\x3e\ +\xa9\xd6\x4e\xc2\xcb\x26\xa7\x1c\xf5\x68\xa5\xb6\x7c\x12\x0f\x38\ +\x33\x91\x09\x9e\x24\x95\xf9\xd2\xb8\xaf\xfc\xe9\x6d\x0e\x0f\x59\ +\x33\x5f\xeb\x46\x0c\x53\xb0\x8d\x39\xdb\x80\x67\x43\x88\xe0\x19\ +\xec\x56\xe1\x4e\x55\xba\x28\xe7\x39\x4e\x6c\x4a\x62\xea\xeb\x5e\ +\x88\xf6\x9f\x54\xce\xda\x4e\x35\xe9\x2f\x9f\x09\xe8\x9a\xd4\xe7\ +\x0b\xf5\xbb\x8e\x3c\x58\x05\x01\x00\xc4\x8b\xca\xe9\xc0\x9e\xc6\ +\x98\x7d\x5d\xd7\x2d\x3b\xba\x9c\x98\x3a\xa7\x4b\x93\x17\x7d\x33\ +\x3a\x5b\x71\xd4\xe4\xe4\x96\x98\x97\xd0\xf3\x81\x8d\x6a\x58\x2c\ +\x2e\x5e\xac\x35\x51\xa2\x17\xdf\x9e\x6b\xe8\xb4\x07\x3f\x90\xe3\ +\x02\x0e\x3d\x40\x48\x51\xcd\x92\xc9\x63\xe3\x78\xde\x4f\xcb\x14\ +\xd2\xbb\x93\x6d\xd9\xb2\xb7\xff\x0a\x05\xd9\x00\xd8\xb6\x9a\x40\ +\x93\xa2\x0f\x95\x38\x5e\x4b\xd9\xef\xcf\x01\x35\xc6\x9c\x5c\x0b\ +\x09\xc4\x54\x46\x29\xfa\xa5\x1a\xb5\xff\x4c\x71\x9c\x6b\x43\xc8\ +\x7c\xd8\xcd\xef\x70\x76\x99\x0a\x23\x81\xcd\x62\x3c\x14\xb8\xc5\ +\x18\xf3\xed\x9e\xf7\x96\xcc\xcc\xce\xe7\xd2\xa9\xbb\x2b\x20\x80\ +\x3c\xb6\xb1\xeb\x7d\xc0\xe5\x2a\x3c\x95\x0c\x10\x7e\x1f\xfb\x94\ +\xa3\xfd\x01\x5e\x5b\x26\x2b\x57\x1b\x04\x64\x94\xea\xaf\xa8\x2d\ +\x32\xbc\x2b\xf0\x81\x31\xe6\xd0\xb0\x84\x84\x7a\xc0\x4b\xe8\xb7\ +\xb0\x9d\x56\xab\xc5\xc7\xc0\xf7\x93\xd3\x5e\xff\xb8\x84\xf6\xdf\ +\xf4\xe1\x0f\xe5\xd8\x90\xc3\x3f\x08\x5b\xec\x31\x4f\xb7\xc4\x4e\ +\x0d\xee\xdb\x9d\x57\x7e\x57\x61\xc4\xe3\x87\x55\x06\xc8\xe6\x81\ +\x04\xf6\xf7\xbb\xe2\xe0\x2d\x45\x6a\xcb\xdd\x10\xe0\x17\xc0\x05\ +\xe5\x36\xab\x08\xbc\x35\xd5\x9f\x50\x5b\x0b\xb0\x65\xc0\xf1\x89\ +\xa9\xb3\xc2\x12\xb8\xbe\x51\xd4\xf2\x7a\x2e\x74\x7f\x9d\x3e\x11\ +\x6c\x1d\xaa\x44\xca\x0b\x06\xce\x71\x94\x66\x60\x34\xc2\x51\xc9\ +\x4c\xf6\xc9\xae\xa0\x67\x4f\x6c\x33\xde\x24\x9d\x32\xb5\x7f\x29\ +\x57\xbe\xac\x3f\x9e\x27\x92\xf3\xdd\x80\x5a\xd0\x04\xdc\x0a\x3c\ +\x6c\x8c\x39\xf0\x92\x03\xb7\xac\x2b\x11\xe4\x5b\x53\x1b\x63\x3b\ +\xa5\x54\xbb\x68\x15\xb8\xa8\x50\x8c\x3d\x1b\xf6\x81\x69\x07\x6e\ +\xeb\x08\x3c\x58\xd4\xc0\x28\xfe\xec\xb0\xe0\x9f\x95\x06\x3d\x81\ +\xf2\x26\xfa\xcc\xad\xc4\xfa\xc9\x4f\x4a\xad\x0d\x1c\x57\xe5\x3d\ +\x3f\x1b\x8b\x8d\x0c\xec\x07\xf8\xd2\x42\x27\xb5\x7a\xac\x62\xc1\ +\x08\x23\x81\xb7\x8d\x31\xbf\x34\x36\x47\xa2\x6c\x37\x25\xd7\xda\ +\xbc\x06\x76\xf0\x6a\xd5\xfc\x21\x70\x99\x2a\x8f\x87\x90\xf9\x5a\ +\x50\x21\xd9\x76\x53\xf6\x61\x04\x20\x22\xd9\x32\x08\xa0\x00\xa4\ +\xe3\xed\xd9\xb7\x92\x99\x6c\x3e\xd9\x16\x5e\xf1\x67\x8c\x49\x16\ +\x94\x7b\xbd\xbe\x0b\xf5\xba\x0b\x7a\x5b\xd5\x3e\x9d\xeb\xba\x5e\ +\x0d\x6e\x40\x4f\xc2\xd9\x59\xe0\xb6\xa7\x17\x38\x0f\x1a\x63\x2e\ +\xde\x71\x55\xbd\xe7\xea\x47\x66\xe6\xa3\x14\xfe\xce\x74\xf3\x70\ +\x85\xab\xb1\x41\xab\x6a\xf1\x2c\x30\x6d\xc4\x79\xaf\x87\xb5\x64\ +\x76\xfe\xf0\x46\xfe\x0a\xc2\xa3\xe2\x17\xba\xae\x1b\xb8\x9d\x96\ +\x4b\xa7\xd6\x0b\x09\x30\x05\x07\xe5\x8a\xcb\xca\xde\x96\x53\xe5\ +\x48\xec\x20\xcf\x2a\x08\x4f\x7f\x1b\x3b\xf7\xa9\xc0\xfb\x7d\xe8\ +\x43\x69\x22\x9a\xec\x4d\xf1\x63\x02\x17\x00\xed\xc0\x0d\xc6\x98\ +\xee\xc1\xc1\xdf\xbb\xae\xdb\x11\xfc\x97\xce\x31\xa0\x1b\xd4\xf0\ +\xdd\xaf\xa9\x48\x5b\xd8\xa4\x25\x81\x2b\xb4\x0e\xe9\xc9\xaa\xda\ +\x57\xbc\xc2\x03\x2e\x03\xfe\xdc\xd7\xb9\x8c\x31\x49\x6c\x4f\x80\ +\x3d\x2a\x0c\x7a\x14\x6b\x0d\xea\x5c\x88\xed\xff\x57\x73\xad\xb6\ +\x82\x33\x63\xb1\x7c\x11\xd8\xf3\xdd\x4e\x16\x6d\x3f\xde\x5c\x32\ +\x36\xe6\x4d\x7b\xb1\xe0\xe4\x5c\xd7\xad\x89\x0c\x6c\xdf\x34\x3d\ +\x99\x0a\xd8\x31\x00\x8b\x81\x63\x92\x99\x92\x7e\xf7\xfe\x84\xec\ +\xef\x3b\x30\xbb\x49\x4a\xf8\xcb\xc2\xc1\x68\xd9\x26\xfa\x3d\xce\ +\xf9\x6f\x97\xe5\x33\xe7\x26\x36\xaf\x4f\x48\x2a\x76\x19\x98\x0f\ +\xf2\x68\x89\xe3\x37\x44\x2c\x17\xe2\xfb\xd0\x3f\xe8\xe1\x72\xfd\ +\x3e\x98\x34\x5b\x56\x03\x3d\xb6\x16\xbd\x00\x7a\x7c\xb2\x2d\x1b\ +\xd6\xf2\xfc\x08\x2d\x9f\x94\x2b\xc5\x2a\x25\x62\x3d\x05\x54\x2e\ +\x72\xe2\xc5\xc9\xf1\x73\xe7\x78\x7d\x08\xff\x70\xe0\xee\x4a\x85\ +\xbf\x64\x90\xae\xec\x48\x98\xeb\x76\x1a\x63\xa6\x12\x52\x55\x54\ +\xad\x45\xd0\xd1\x29\xab\x02\xa7\xbf\x58\x70\x5a\xfd\x20\xd1\xf9\ +\xbe\x39\xf4\x8c\x7f\xb3\x5d\xdf\x5f\x16\x31\x88\xb0\xb3\x56\xe6\ +\x1b\x05\xba\xbc\x9e\xca\x33\x25\x5e\xc4\xc6\xc0\x1f\x4b\xb0\xed\ +\xab\x4f\xce\x08\xd6\xfe\xf9\xf4\xd8\x61\xaa\xde\x2f\xcb\x0c\xfe\ +\x15\x15\x29\xab\xaf\x5f\xb1\x75\x8c\x53\x74\xa4\x0d\x65\x54\x95\ +\xf7\xfc\x68\x2c\xae\xef\x95\x30\x8d\x37\xa0\xfe\x08\xd7\xfe\xe8\ +\xd1\x54\x3f\x64\x46\x81\x6b\xc5\xc9\xff\x2b\xe4\xfe\x56\xc6\x56\ +\xb2\xd6\xb2\x66\x72\x40\x48\xa2\x8e\x6e\x11\xf0\xba\xd5\x77\x13\ +\x4f\x02\xb9\xbd\x94\xf0\xfb\x82\x9f\xf6\xc9\x3d\xca\x71\x74\x15\ +\x9b\x74\x17\x60\xbb\xbc\xd4\xa3\x63\x4b\x57\x60\x68\x72\x37\xb3\ +\xa8\xcb\x6c\xd1\x6d\x8d\xb9\x20\x0f\x53\xfc\xb1\x48\x61\xfe\xef\ +\x6a\xaa\x7a\x25\xb5\x4d\xcb\x7d\x4d\x3c\xce\x1d\x36\x6d\xb6\x17\ +\x66\xfa\x63\x73\xd1\x57\x2d\x61\xce\x1d\x5d\x42\x54\xb7\x01\x29\ +\x77\x9a\xf0\xfb\x82\x96\xd5\x9a\xab\xe8\x38\x3b\xa1\x1c\x5e\x6d\ +\xc8\x04\xe1\xfc\xd8\x39\xa1\x7d\x0c\xd6\xa1\xf2\xfd\xff\x4a\xf1\ +\x01\xf0\xab\x60\xed\xbf\xd1\x1a\x50\xac\x65\xf2\xcf\x6b\xa0\xa7\ +\x25\xa6\xbe\x19\x76\x7f\xbf\xa3\xec\x86\x2c\xa1\x58\x82\x2d\x12\ +\x5a\xde\xec\x98\x38\x5a\x40\xb6\xed\x16\x88\x2b\x00\x4f\x02\x7f\ +\x50\x47\xfe\xd8\x34\x75\xf6\xa2\x12\x82\xdf\xe4\xcb\x43\xad\x82\ +\x5f\x4c\x0a\x8f\xd4\x4c\x00\xae\xeb\x76\x6e\x37\xde\x1c\x9c\x53\ +\xee\xa1\xfe\xa5\x9c\xcb\xd5\x5b\xe7\x6d\xfc\x61\x4a\x28\xfd\xa6\ +\xc7\x8a\xaa\xd7\x8a\x4d\x1c\xaa\xfa\x41\x01\x27\x27\xa6\x65\x3f\ +\x2a\xf1\x99\x03\x80\x1f\x95\x38\xfe\x2f\x7a\xf4\xc6\xfb\xe4\x1e\ +\x4e\x18\x2d\x8a\xfc\x9c\xf2\xf7\xb0\x3f\xa4\x8c\xc2\x9c\xdc\xa4\ +\xd4\x9a\x28\xd7\xd4\x40\x7c\xaf\x68\xd1\x29\x55\xdc\x74\x7d\x9d\ +\xdf\xb5\x02\x5f\x0d\xd2\xfe\xc5\x93\x3f\x27\x1e\xc5\x93\xb4\xfa\ +\x39\x93\xcb\x14\x7e\xd0\x94\xe9\x98\x1f\x22\x64\x47\x62\x67\x3d\ +\xd6\x84\x26\x38\xe5\xa9\x80\x3c\x00\x27\xe6\x88\x2a\x7f\x55\x78\ +\x48\xd0\xff\x39\x0e\x33\x3d\x4f\xe6\xf7\x35\x12\x7c\x3b\x63\xe2\ +\x39\xbb\x15\xb8\x5b\x04\xcf\xb7\x58\x0c\x09\xe2\x57\xd3\x2c\xe2\ +\xc1\x18\x3c\x50\x84\x7d\xe9\x3f\x14\x80\x7d\x4a\x69\x7f\xd0\x9d\ +\xb0\x7b\xcd\xb5\xa4\xfc\xde\x85\x38\x77\x97\x60\xe4\x4d\x80\x3f\ +\xf4\x71\x8e\x5b\xc3\xb6\xfe\x74\xd5\xf8\x86\xa0\x95\x3c\xb7\xa5\ +\xea\x51\xd2\xf5\x59\x7a\xea\xd8\x38\xea\x5d\x40\x79\x5b\x8a\x81\ +\x16\x8b\x28\x6d\xc9\x69\xb3\x96\x0d\xa0\xf9\xff\x06\xb6\x33\x50\ +\xef\x8b\x4b\xc4\xd7\x51\xf8\x71\x0d\xc4\x32\x0d\xdb\xe8\xa3\x5e\ +\xa6\x7f\x57\x80\x21\x70\x6d\x26\xda\x3a\x3c\x6c\x63\xdc\xb2\xb1\ +\xfb\x04\x13\x53\xe5\xa0\x9c\xc7\x2e\x11\x3d\x5f\xaf\xa8\xe1\x5a\ +\xb6\x22\x3c\x35\xc3\xf5\x8e\xd8\xb0\x78\x46\x2c\xa2\x8a\xb1\x72\ +\x5e\xe2\x08\x87\xef\xb8\xae\x1b\xaa\x95\xf3\xad\xcd\xab\x82\xfe\ +\xa6\x46\xd3\x7f\x21\xc8\x2f\x92\x6d\xb3\xf2\x55\x9a\xfe\x5d\xe6\ +\xff\xad\x25\x8e\x7f\x89\xca\xba\x00\x2f\x8b\x7f\x14\x5e\x32\x9b\ +\x9b\x94\x92\x58\xcc\xfb\x21\xb5\xf5\x5b\x7c\x1d\xd1\xd0\x74\xed\ +\x43\x46\x79\xdf\xad\xb3\xf9\xaf\xc0\xf7\x82\x48\x73\xd9\xe4\x31\ +\x8e\xda\xec\xb5\x6a\xe3\x1a\x4f\x8a\xea\xf9\x4d\xe1\x25\xb4\xd7\ +\xd6\x68\x31\x76\x61\x51\xa9\x98\x50\x25\x30\xc6\x34\x7d\x58\xe4\ +\xdf\x0b\x3d\xfe\x4a\x74\xf5\x30\xe7\x85\xc5\xd0\xaa\xfa\x82\x5f\ +\xde\xf5\xc2\x93\x31\x5b\xbc\xd1\x1f\x15\x7c\xcf\x14\x95\xbf\x87\ +\x0a\x41\x6b\x4a\x54\x64\x22\xd4\xbc\x47\x7d\x4d\x51\xbd\xd9\x25\ +\x8e\x1f\x54\xda\xb7\xb7\xe6\xbf\xeb\xba\x81\x3e\xfb\xb2\xc9\x63\ +\x12\xa0\x27\x56\x68\xa1\x38\xce\x1a\x25\x25\xe7\x0b\xd8\xdd\x99\ +\x6a\x13\x6b\x3c\x81\xd3\x12\x99\x8e\x45\x21\x8b\x51\xee\x7a\xdf\ +\xf9\x41\x3d\x5f\x6e\xc2\x6e\x7f\x05\x66\x1f\xc6\x3c\xd9\xbc\x06\ +\x72\xfb\x08\xe5\x87\x89\xf6\xe0\x8e\x4d\xc6\x98\xef\x61\x93\x77\ +\xa2\xb2\x50\xbd\x08\x84\xff\x40\xdf\x85\xdc\x35\x42\xe1\x2f\x86\ +\x3d\xdf\xaa\x5c\x80\x6e\xae\xc0\xbf\xb1\xfb\x91\xfb\xd7\x71\x7d\ +\x14\x81\xb6\xa7\x66\x94\x30\xfd\x45\x3f\x0f\xf2\x4b\x6a\x33\xfd\ +\xdf\x10\x95\xb6\xe1\xed\x59\x2d\xa1\xfd\xaf\x2a\xe3\x3c\xa1\x79\ +\xff\x8e\x27\x9b\x51\xf9\xa4\xe4\x44\xa1\xa8\x31\x02\x1a\x67\xe4\ +\xd2\xa9\x9d\x51\xfe\x42\x75\xfd\xfb\xba\xf0\xb4\xc2\x1d\x25\x8e\ +\x6f\x5e\x50\xb6\xaa\xe3\xfb\xfd\xa8\x60\xf3\x25\x34\x80\xd8\x93\ +\x0a\x17\x51\x5e\xa9\x74\x60\x2c\x27\xd9\x9e\x0d\x1c\x4d\xb7\xe3\ +\xd6\x66\xe5\x4e\x8f\x56\x2f\x3a\x21\x3b\xb9\x9c\x0a\xc1\x12\x82\ +\x3f\x02\x1b\xe5\x6f\x25\xfa\x4e\x59\x79\x4a\x34\x4e\xa9\xfa\xcb\ +\x9e\x9a\xe1\x7a\xc6\x98\x43\xb1\x1d\x5a\x0e\xa8\xd3\x02\xf9\xad\ +\xeb\xba\xa1\x26\x75\x2e\xdd\xbc\x0a\x48\xad\xa6\xbf\x22\xb4\x27\ +\x32\xb3\x4b\x75\x28\xfa\x0d\xf4\x39\x90\xc3\x03\xfe\x56\x82\x32\ +\x0f\xaf\xe2\x3a\x47\xaa\x23\x2b\xd1\x6d\xca\x72\x6e\x52\x4a\x50\ +\xf6\x03\x6e\xa4\xb6\x44\xa7\xa5\xc0\x4f\x92\x99\xe0\xf2\x66\x3f\ +\x43\xef\x46\xea\x3b\x98\xe3\xda\x19\xae\x3b\x27\x44\xc3\xec\x8b\ +\xed\x6b\x5f\x0d\xfe\x1e\x93\xe0\x7c\x02\x63\xcc\xf0\xa5\x1e\x8f\ +\xd7\x10\x33\x59\x9e\xa1\x05\xdd\x71\x55\xed\xa4\x47\x7a\x73\xa9\ +\x6d\xeb\x6d\xec\xbc\x0d\x07\xd8\xcf\x81\x1d\x3d\xeb\x5a\xd6\xab\ +\x59\x6e\xa6\xd4\xb5\xd4\xc4\x36\xae\xeb\xe6\x7d\x12\xf8\x47\x1d\ +\x48\xa0\xe0\xfb\xdc\xc1\xce\x71\x7a\xb4\x80\x9c\x00\x35\x6b\xa8\ +\x99\x28\xd7\x96\x60\xe7\x4d\xb1\x7d\x0a\xfa\xb2\x88\x1e\xd8\xca\ +\xf3\xde\x0b\x1a\x80\xb1\xac\x75\x6c\x02\xbc\x6a\xb6\xe8\xd6\x03\ +\xd9\x7e\xd9\xa4\xe6\x07\x44\x1d\x50\x56\x46\xf5\x67\xd8\x3c\x87\ +\x5a\xb7\x62\x7f\x1f\x2f\xc4\x4b\x45\xfe\x2b\x2d\xb9\xd5\x4f\x1f\ +\x45\x59\x58\x40\xc8\xb6\x5f\x67\x3a\xb5\x32\x70\x7e\x95\xeb\x73\ +\x2e\x70\x62\xac\x2d\x1b\xb6\xe8\xbf\x15\x81\xbb\xf8\xa9\x7a\x55\ +\xe4\xb1\x85\x72\x2d\xcb\xa7\x10\xe7\xc6\x1b\x73\x61\xc8\x18\x35\ +\x3c\x4b\xe0\xdb\x00\x31\xaf\xbe\x9d\x85\x97\x61\x83\xa0\xd4\x85\ +\x00\x7c\x12\x28\xd4\x81\x04\x34\x06\xdf\x7a\xb6\x44\xe0\xcf\xc1\ +\xd9\xd4\x17\x84\x5a\x34\x94\x22\x72\x4e\xb2\x6d\x76\xa9\xad\xb6\ +\x53\x28\x63\x50\x86\x82\xde\xf8\xfc\xf3\xc1\x29\xa6\x52\x6c\x06\ +\x59\xaf\x8a\xeb\x8b\x83\xde\xe4\xa8\xfc\x09\x34\x8e\x8d\xbb\x8c\ +\x8e\x40\x2b\xbf\x82\x30\xc9\x39\xff\x35\xaf\x84\xf6\xff\x43\x25\ +\xdf\xb3\xed\xca\xfa\xdc\xf4\x45\xf2\x67\x6c\x7a\x6f\x9f\x8f\x6b\ +\x98\xc3\x61\xff\x7d\xce\x9d\x1b\x4c\xa6\x72\x3c\x68\x35\x1a\xba\ +\x13\x38\x3a\x99\xc9\xbe\x1d\x72\x5f\x6b\x02\x57\x44\x2d\x65\x5e\ +\x6f\x39\x4a\x28\xe1\x8d\x74\x3d\xfa\x0d\x6d\x7d\x95\xe1\x47\xc2\ +\x3e\xae\xeb\x16\xb0\x25\x91\x77\x46\x74\xe1\x4f\xab\x25\x94\x60\ +\xd3\xe0\xb4\xe6\x18\xc8\x05\x94\xdf\x4e\x3b\xf4\x7b\x20\x3c\x02\ +\xbe\xeb\x04\xb3\x85\xaf\x31\xca\xc1\xdf\xc2\xad\x03\x19\x47\x75\ +\xd3\x76\xc0\xe6\xcd\xff\x04\x38\x16\x9b\x09\x57\xab\xf0\x2f\x06\ +\x7e\x90\x6c\xcb\x96\x6a\x2f\x3e\x99\x0a\xfb\xed\xbd\xb2\x44\x8e\ +\xf7\xe3\x24\xe5\xf4\x2d\x98\xb3\xcc\x0b\xde\x97\xce\x4f\x6e\x5e\ +\x0f\xf4\xa4\xaa\xc8\x1c\x2e\x66\x99\x77\x7f\x89\x75\xba\x60\x35\ +\xd5\xb5\xb1\xdd\xab\xa7\x60\xe7\x31\xe4\xfb\x57\x26\xfb\x0d\xf7\ +\x97\x43\xc6\x91\x99\x1f\x3e\x09\x7c\x39\x02\x12\x28\x02\x99\xe7\ +\x4a\xf8\x2d\xc5\xa2\x1c\x86\xdd\x52\xab\x91\xb8\x75\x4a\xb2\x2d\ +\x1b\xc8\x90\x7b\x6f\x63\x9c\xe6\x61\x7a\x47\x05\x82\xfb\xb7\x12\ +\x4b\xf3\x4b\x0c\x8e\x21\x97\x45\xe0\x0c\x67\xd9\xf0\xc7\x4b\xb8\ +\x3c\x23\x7d\xc2\xa9\xe4\x7a\x3f\x58\xe4\x31\xd7\x75\xdd\x0f\xb0\ +\x7d\x08\xe6\x94\xf8\xec\x42\x60\xaf\xa0\xc0\x5f\xe7\xc4\x8d\x62\ +\xea\x49\x1b\xd5\x15\x33\x4d\xf7\x60\x6a\xf2\xa2\x39\x25\xeb\x26\ +\xfe\x33\x73\xe6\x47\xae\xeb\x4e\x71\x5d\xf7\x2c\xdf\x0c\x5f\x09\ +\x5b\x32\xde\x8e\xdd\xda\x5e\x11\xc8\x20\x07\x4c\x2d\x27\x7d\x3e\ +\x52\xff\xa3\x1b\x09\x9c\x49\x95\x7d\xdf\x63\x70\xb5\xeb\xba\xa1\ +\xc2\xd4\xd9\xda\x32\x52\xaa\xf7\x0f\xbb\xe3\x71\xb5\x99\x56\x81\ +\x78\xaf\xc0\x21\xee\xe2\xf2\x52\x76\x05\x34\x1e\x22\x2e\xc5\x33\ +\x36\x74\x10\xf6\x1a\x24\x0b\xe3\xcf\xc0\x65\xf1\x8b\x5e\xd4\x12\ +\xa6\xff\xf1\x50\xfe\x04\x62\x1f\xf3\x5c\xd7\x7d\xcb\x5f\x03\xef\ +\x03\xdb\x01\x1d\x04\xfb\xc0\xbf\x73\x5d\x37\x1b\xe2\x2a\x6d\x03\ +\x55\xa5\x33\x2f\x11\xf8\xd1\xb0\x4c\x65\xad\xbd\x5d\xd7\x2d\xb8\ +\xae\x9b\x77\x5d\xf7\xcf\xae\xeb\xa6\xb1\x3b\x0e\xdf\xc2\xa6\xf4\ +\xe6\x87\xa8\xf0\x17\x87\x09\x07\xb8\xae\xfb\xef\x72\x3e\x1c\x79\ +\x00\xc2\x7f\xa8\x67\x63\x53\x77\xef\xa6\xb2\x5c\x81\x42\xb1\x44\ +\x35\x5b\x2e\x9d\x12\x11\x4d\x53\x5d\xb3\xcb\x1e\xda\x5f\xda\x9a\ +\x32\x1d\xb9\x10\x41\x70\x80\x2b\xcb\x3d\xd9\x7a\x09\x5e\xfe\xfe\ +\xe7\x8a\x81\x8b\xaf\x90\x4f\xac\x4e\x75\xbd\xf9\xa2\xc6\xc3\xc0\ +\x71\x7d\x34\x16\xdd\xd2\x37\x8f\x2b\xc5\x91\x3d\xd6\xc0\xfb\x1b\ +\x3a\xba\xb1\xd3\x7b\x54\xdc\x02\xec\xd6\x5e\xc0\xbb\x6d\x19\x8e\ +\x70\x19\x95\x07\x37\x15\x68\x57\xd5\x19\x11\xac\xdd\xbc\xeb\xba\ +\x37\x03\xdb\xad\xae\xba\x56\xc2\x5a\x05\x43\xcd\x22\xb8\xcf\x29\ +\x31\x0d\xb8\xee\x04\xd0\xed\x61\x3e\x32\xcc\xe1\xa0\x55\x62\xec\ +\x45\xf9\x83\x26\xae\x0e\x6b\xa0\xd9\x15\x6b\xf2\x35\x54\xad\xe6\ +\xf4\xb3\xe0\x95\xea\xb1\x7f\x68\x25\x66\xe8\x5b\x79\xe6\x9e\x78\ +\xc7\x0b\x4b\x42\xac\x83\x91\x44\x5c\xc1\x55\x05\x9e\x17\xe1\x6b\ +\xc9\x4c\x78\x8d\xc3\xd6\xc6\x8c\x02\xee\xa9\xe2\xd9\x7e\x10\xb3\ +\xa9\xbc\xcb\xe1\xae\xe7\x66\x16\xbc\xde\x4d\x5b\x0f\x0d\x6a\x0c\ +\xea\x1f\x3e\x0a\xdb\x04\xb6\x52\xcc\x50\xf5\x2e\xee\x39\x7e\xbd\ +\x56\x25\xf6\xf0\xcc\x99\x1f\x4f\xb7\x56\xc1\x37\xb1\x55\x7e\x43\ +\x81\x08\xfe\x09\x1c\xf2\xc4\x8c\xf2\x73\x12\xea\x3a\xdc\xf0\xbf\ +\xcf\xb9\xde\xa3\xcf\xba\x0f\x63\x5b\x38\x9d\x96\x90\x92\x16\x41\ +\x81\x90\xe1\xa2\x00\x85\xd3\x47\xc7\xb1\x5b\x1a\xc3\x6a\xbc\x2c\ +\x45\xb9\x30\x99\xe9\xe8\x8c\x42\xfb\x97\x81\x55\xa8\x3e\x53\x2f\ +\x0a\xbc\xa2\xca\x41\x89\xb6\xec\xbc\x12\x7e\xbf\x24\x1d\x7e\x42\ +\xef\xa6\xa6\x65\x99\xff\xcf\xfa\xe6\x7f\x40\x2c\xe1\x67\x5d\xff\ +\x8f\x0b\x17\x13\xd0\x1a\x1c\x20\x9f\x6e\x5e\xcf\x77\x1b\x2b\x25\ +\x9f\xa5\x20\xc7\x34\xb5\xcf\xf9\xb8\x5e\x0f\xcf\x75\xdd\x5b\xd6\ +\x10\xdd\x3e\x61\xa7\x59\x0d\x56\x12\x28\xfa\xd6\xf6\x21\x95\xf6\ +\xd3\xe8\x97\xf9\x7c\xfe\x56\xc4\xd4\x5d\x27\x18\x27\x5f\xe4\xf3\ +\xd8\x00\xde\xc4\x1e\x9a\xf1\xaa\x52\xda\xbf\x58\x88\x1d\x2c\x76\ +\xd4\x71\xad\x78\x09\x09\x4f\x2d\xf6\x63\x18\xa3\x22\xbc\xfd\x95\ +\x18\xb8\x39\x88\x2f\xaa\xa7\xfb\x37\x4d\xeb\x98\x5b\x4a\xf8\x81\ +\x33\x96\x79\x9c\x5e\xe9\xc9\x05\x88\x49\xef\xfe\xfc\xfe\x39\x6f\ +\xe3\xd3\x0a\xbe\x85\x45\xe5\xa2\xc0\x8c\xbf\xc9\x2d\x31\xf5\xf4\ +\xfc\x2a\xe2\x0e\x8a\x72\xb1\xa2\x4f\x75\x4e\x4a\xc5\x44\xd5\x51\ +\x91\xd5\x44\x59\x0f\x58\x0f\x61\x5d\x94\x26\x3e\x99\x1f\x28\x39\ +\x45\xdf\x11\x78\x5b\x91\xb7\x04\x6f\x3e\xe2\x14\x93\x6d\xb3\xfb\ +\xb4\x1c\x1e\x9a\x31\xb3\x00\xdc\x68\x8c\x59\x86\xdd\x7a\x9e\x30\ +\x88\x84\x3f\x97\x14\xf6\x53\x78\x78\xfa\x0c\xb7\xe2\xd4\xfc\x7e\ +\x5d\x98\x8f\x3c\xeb\x7a\xd8\xca\xac\xc7\x8c\x31\x0b\xb1\x79\xec\ +\x60\x87\x69\x84\x6e\xfd\xe4\x26\xb5\xac\x82\x6a\x26\xa2\xeb\xbd\ +\xb2\x44\xf6\x9b\x83\xcd\xfa\x8b\x12\x31\x06\x66\x07\xe0\x49\x47\ +\xe5\xb0\xf8\xb4\xec\x5b\xa5\x3e\x94\x84\x71\xb9\xea\xb4\x2f\x2b\ +\xc7\x78\xff\x9b\x1b\x14\xb3\xcf\xf4\xf0\xbe\xbf\xb2\xb6\x77\xcc\ +\xdf\xde\x73\x3e\xdf\xcd\xf4\xbf\x7a\x46\x98\xe9\xef\x79\x3b\x83\ +\x54\xd3\x89\x67\x21\xc2\x7b\x02\x17\xa2\x18\x90\xad\x44\x59\xe3\ +\x13\x81\xd7\x9e\xf7\xa3\x88\x3f\x37\x42\x50\x0f\xa4\x03\xd5\xc7\ +\x73\xe9\xd4\xe3\x22\x7a\x3b\x2a\x6f\x27\x32\x59\xaf\x0f\x45\xf6\ +\x17\x63\xcc\x5d\xd8\xd4\xda\x6d\x06\x52\xea\x1d\x50\xcf\xa6\xe2\ +\xb7\x3d\x3d\xc3\x7d\xb8\x86\x3f\x82\xf4\x68\x00\x00\x20\x00\x49\ +\x44\x41\x54\xf3\xf4\x3f\xfc\xae\xb0\x53\xba\x2d\x90\x36\xd7\x75\ +\x97\x85\x73\xbd\xfe\x10\xd8\x38\x82\xaf\x7e\x57\xd4\xbb\xbe\x1f\ +\xb5\x7f\xd7\xfd\xf5\x27\x14\xb8\x15\xcf\xfb\x52\xbc\x7d\xf6\x5b\ +\x7d\xbc\x87\xf5\x72\xd5\xf9\xfd\x00\x7c\x5c\xe4\x9a\x13\xef\x78\ +\xe1\xbd\x1e\xe7\x5c\xfd\xb6\x79\xce\xe4\x6e\xe7\xcc\x12\x12\xd8\ +\xcd\xb7\xb6\xac\x04\x72\x69\x95\x31\x92\xd5\xb1\x99\x84\xbf\x00\ +\xbe\xe8\xc7\x6c\x12\xbe\x92\x70\x58\x7e\x90\x8c\x7c\x6a\x09\x10\ +\xf3\x3f\xb7\x31\x36\x78\x79\x85\xaa\xbc\xa6\xf0\xd7\xce\x74\xea\ +\xb0\x42\x7a\x4c\xb2\x0f\x12\x58\x82\xad\xd1\x7f\x66\x00\xe5\x7f\ +\xe9\x6e\xab\xeb\xc4\x9d\x56\xd5\x83\x5c\xd7\x7d\xb8\x46\x22\x19\ +\x10\x1c\xc7\xa7\x45\x1e\xff\xa5\xc4\x30\x8a\x7c\xba\x65\x03\x6c\ +\x62\x4a\x14\xd7\xfa\xb7\x62\x2c\xb1\xb0\x1f\xb5\x3f\xd8\x74\xcc\ +\xfe\x9a\x7b\x98\x03\xa6\xa9\xca\x77\x92\xd3\xe6\x2c\xec\x4b\xf8\ +\x81\xe9\x54\x3e\xea\xab\x0b\x0b\xb0\x13\x7e\x7b\x9a\xfe\x3f\x2e\ +\xea\x27\xa6\xff\x87\xc0\x17\x43\xfb\x23\x08\xc7\x63\xc7\x69\x57\ +\x8b\x28\x2c\x2b\xc1\xee\x3c\x7c\x59\xe0\x66\x0f\xe7\xc1\x5c\x3a\ +\xb5\x57\x67\x6b\xca\x29\x83\x04\xa6\x0f\x80\xec\xdc\x09\xac\x71\ +\xe9\xc3\x33\xcf\xbf\xf2\x91\x99\x85\x5a\x4f\xd6\xef\x04\xb0\xdf\ +\x56\x5b\x25\x80\xa9\xdd\xb4\x55\x68\xb1\x42\xae\xb5\x59\x14\x9d\ +\x88\x8d\xa4\xd7\x2c\x88\x82\xfe\x6a\xd8\xd4\xd7\xc3\x34\xf2\x57\ +\xea\xa0\xfd\x11\xf8\x88\xfe\xd9\x53\x9e\x87\xf2\x2d\xf1\x8a\x93\ +\x9b\xda\x67\x2f\xeb\x43\xf8\xd7\xf7\x35\xd8\x7a\x35\x7c\xdf\x55\ +\xae\xeb\xf6\x4c\xb9\x1d\xcb\xa7\xd9\x67\x8a\xdd\xd5\xe9\x08\xfa\ +\xe3\xce\xd6\x96\x31\xd8\x77\x3b\x18\x12\xa4\xba\xbb\x6b\x3b\x03\ +\xf7\x88\x70\x6d\xa7\x6d\x43\x5e\x8a\x04\x76\xef\x67\x12\xb8\x03\ +\xf8\x72\x39\x53\xb6\x06\x25\x01\x18\x63\xe4\x2d\x91\x5b\xca\xd5\ +\xfe\x6a\x0b\x7d\x7e\x14\xd1\xd7\x3f\xac\x21\xfd\xf5\x7d\xcd\xf5\ +\xeb\x1a\x84\xdc\xf9\xee\xb8\x71\x12\xac\xe5\xe4\x5d\xca\xdf\x06\ +\xad\x06\x1e\xd6\x8c\xdf\x29\xd9\x9e\xbd\x35\x31\x6d\xae\x57\x86\ +\xf0\x4f\xa7\xb6\x26\x1f\xbd\xb4\xff\xa5\x07\x6d\xb9\xea\xc8\xf8\ +\x72\xee\xc4\x6c\x42\x76\x75\x0a\xe9\x96\x84\x88\x5e\xc2\xe0\xc8\ +\x8f\x08\x8b\x8d\x1d\x29\x22\xff\xee\x4c\xa7\x4c\x19\x24\xf0\x74\ +\x9d\xaf\xa7\xe8\x0b\xff\x57\xfc\x64\x3b\x86\x24\x01\x00\x9f\x07\ +\x0e\xec\xe1\xfb\x07\x6b\xff\x74\xb3\x23\x22\xa7\x51\xfb\xb6\x5f\ +\xd7\x77\x5d\x9c\xcc\x74\x84\x3d\xbc\xcd\xa9\xae\xee\xbc\xeb\xe4\ +\x5f\x74\x1d\x27\xd0\x7a\x70\x62\x6b\x2d\xf0\x85\xa1\x1e\xf8\x10\ +\xf8\x19\xc2\x21\xc9\x4c\x76\x56\x19\x04\x1c\x85\xf0\x03\x5c\xd9\ +\x53\xfb\x5f\x33\x37\x76\xec\xfc\x02\x2d\xdd\x9e\xf7\x11\x61\xa6\ +\xbf\x87\xee\xd7\x6d\x1d\x0c\x66\x18\x81\x07\x73\xe9\xd4\x9e\x9d\ +\xe9\x66\x09\x23\x01\x07\x76\x17\x3b\x47\xa2\x2e\xfe\x3e\xb6\x2c\ +\xfa\xd0\xa8\x85\xbf\x5f\x09\xc0\xf7\xb1\x27\xf1\x69\x24\xff\x49\ +\xba\xb5\xfd\x0e\xd0\xab\xdb\x61\x13\x72\xa2\xc0\x8b\x68\x70\xcf\ +\x39\x1f\xa7\x52\x5b\x79\xad\x43\xc8\x0e\x45\xfc\x9c\xa7\x14\xe5\ +\xb7\x11\x3f\xce\x3c\x70\x13\x2a\xdb\x22\x5c\x9e\x6c\xeb\x7b\x6c\ +\x78\x84\xc2\xbf\x00\xb8\xb4\xc7\xb9\x47\x29\xfc\xac\x9b\xb4\x5f\ +\x08\x3c\x11\xec\xd6\xa5\x56\x07\x2e\x66\xe0\xb6\x46\x2b\xc5\x9a\ +\xc0\xdf\x05\xd9\x2f\xd7\x9a\x0a\x24\x81\xe7\x5c\x77\xa9\x42\x26\ +\xe2\x58\x4f\x11\xbb\x95\xba\x86\xeb\xba\xff\xa9\xa5\xe1\xc8\x60\ +\xb1\x00\x76\x04\xf6\x2b\xcb\xf7\x4f\x37\x3b\xd8\x21\x24\x51\x35\ +\x49\xb8\x2e\xd9\x1e\xbc\xf5\x77\xd8\xb8\x71\x31\xe0\x90\x08\xbe\ +\xe3\xea\x12\x16\xc2\x5d\x94\x18\x13\x56\xa1\xb9\xff\x14\xca\x81\ +\xa2\xf2\xdd\x64\xfb\xec\xd9\xc9\xb6\xac\xf6\xe5\x76\x8d\x37\xe6\ +\x44\xdf\xe7\x8f\xa2\xb7\xdf\x72\xda\xdf\x77\x9f\xee\xc0\x4f\x22\ +\x8a\x09\x1f\x00\x17\x07\x69\x7f\x2f\x3d\xd6\x41\x38\x03\x3e\xb1\ +\x14\x86\x0a\x56\x05\x6e\x29\x55\xd3\xe1\xba\xee\x2d\x23\x1c\xbe\ +\x4b\xed\xbb\x3e\x45\xdf\x2d\xde\xc3\x37\xf9\x3b\xeb\x79\x63\xfd\ +\x42\x00\xdb\x56\xae\xfd\x77\xe9\x46\x16\xb5\xe2\x7d\x71\xbc\xd0\ +\x79\x6f\xaf\x3b\xce\x61\xd4\x36\x6c\xb2\x0b\x9b\xfa\x5d\x66\x7b\ +\xa1\xa9\x3d\x3b\x17\x3b\x53\xc1\xab\x41\xf0\x67\x28\xfa\x6d\xd0\ +\x5d\x93\xed\xd9\xfb\x12\xed\xb3\xfb\xd4\x36\x3b\x59\xe1\x3c\x5b\ +\xed\x8c\xc4\x75\x22\xb8\xc7\xf7\x7a\x6a\x7f\x6c\xbf\x84\xae\x14\ +\x5e\xdd\x67\x4d\xef\x22\xd7\x75\xdf\x0c\xf4\xfd\xd5\x8b\x32\xa6\ +\xd3\xdf\x58\x09\xb8\x29\xdf\x1a\x1e\x13\x28\x28\xb7\x52\x9d\x2b\ +\x50\xc4\xce\x15\x38\xdb\x8f\x29\x1c\xec\xba\xee\xa3\xf5\xd2\xfa\ +\x3d\x83\x1d\x75\x87\x67\x7d\xff\xfd\xca\xf1\xfd\xf3\x27\x6d\x18\ +\xf3\x9b\x29\x44\x95\x3e\x7b\x2f\xc4\xc3\xc6\x41\xc5\x88\xae\x41\ +\xc4\x58\x60\x35\x6c\x87\xd8\x20\x3b\xe0\x42\x90\xcf\x61\x47\x61\ +\xf5\x75\x6f\xea\x0b\xfd\xbb\xc0\x3f\x05\x6e\x57\xe5\xee\xa6\xf6\ +\x8e\xb2\xb5\x81\x31\x66\x83\xa5\xd6\x8a\x3a\x86\x68\x22\xed\x0a\ +\x1c\xd0\x5d\xfb\x6f\x6d\xcc\x9a\x9e\x4d\xf7\xed\x3a\xff\xac\x7f\ +\xce\x77\x02\x27\x47\xe5\xd3\x2d\xc3\x15\xbd\xa2\x96\x58\xcb\x20\ +\xc0\x48\x15\xfe\x9a\x6f\x4d\xed\x9e\x68\xef\xdd\x74\xe4\xe9\x19\ +\x6e\xce\x18\x93\x01\x6e\x22\x7c\x6e\x46\xf7\x75\xff\x4f\xdf\x32\ +\xbb\x07\x78\xba\xd6\xb1\x78\x83\x92\x00\x8c\x31\x4e\xb1\x9b\xf6\ +\x17\x78\xd2\xb1\x37\x1e\xbc\xca\x92\x89\x3d\xb1\xdd\x6e\xa3\x80\ +\x2a\x72\x75\x72\xea\xac\x30\x26\x3d\x94\x68\xb6\x18\x3f\x89\x85\ +\x11\xd2\x24\x35\x99\xe9\x58\x96\x9b\x34\xfa\x78\x34\xf6\x47\x6c\ +\x1e\xc4\x2e\xbe\x49\x2e\xdd\x82\x3d\x6f\x02\x33\x10\x9e\x42\x79\ +\x02\x78\x1a\x25\x97\x68\xcf\x6a\x05\xcf\x5b\x80\x13\x81\xc9\x5e\ +\x75\x75\xf5\x61\xc8\xd0\x2d\xf9\xc5\x18\x23\x71\x38\x36\xb7\x7c\ +\xfd\xc0\x91\x25\x02\x7f\x47\x88\x55\x04\x43\x1d\x1b\xa9\x70\x79\ +\xf1\xf4\xe6\xc3\x63\xe7\x74\xe4\x03\x5c\x81\xbf\x18\x63\xae\xe5\ +\xd3\x0e\xd2\x79\x6c\x0d\x4b\x11\xc8\x8d\x1d\xae\x17\xce\x5a\x2a\ +\x5d\xeb\xb1\xd8\x1f\x5a\x7e\xa0\x2d\x80\xae\xdc\x7f\x00\x1d\xee\ +\x90\x79\xe2\xb9\x60\xa6\x2b\x9c\xbc\x69\xcc\x23\x77\x46\x84\xd7\ +\xf5\xba\xa0\x4f\x96\x38\x7e\x68\xc4\xf7\xba\xef\x3e\x5b\x6f\xb5\ +\xc1\x7d\xcf\xcd\x0c\x34\x81\x93\x6d\x73\x3d\xe0\xe1\xce\x89\xcd\ +\x8f\x38\x31\x89\x09\xac\x52\x44\x86\x8b\xa7\x45\x11\x16\x28\xe2\ +\xa9\xe2\x35\x65\x66\x57\xb5\x28\xce\xdb\x6f\xdc\x7a\xff\x7c\x8f\ +\xc9\xef\x17\x22\xa9\x98\xec\x8e\x77\x81\x4b\x7b\x08\xf7\xc6\xb9\ +\xde\x73\x22\x03\xa3\xd4\xb9\xd6\xd4\x28\xdf\xbc\x75\x58\x31\x70\ +\x48\xb1\x20\xc7\x2c\x4d\xb7\x5c\x3e\x3c\x78\xd2\xf0\xc9\xc0\x51\ +\xfe\x3a\x4e\x00\x9b\x8d\x4a\xc8\x37\x1e\x98\x3e\x43\xdd\x41\x76\ +\x23\x75\x25\x80\x80\xc8\x7f\x21\xa7\xe1\x4d\x38\xbc\x44\x6e\x77\ +\x60\xa7\x08\x2f\xe1\x8e\x58\xa2\x10\x98\x14\x33\x65\xbf\x6d\x62\ +\x7f\x7d\xab\x70\x48\xc4\xb7\xec\xbc\xeb\xc9\xbf\x8c\x31\x3b\xb8\ +\xae\x1b\x9a\x89\xd7\x34\xad\xc3\xf3\x4d\xfc\x05\x51\x7c\xe9\xc1\ +\x3b\x8c\x97\x39\xcb\xf4\xc4\x3f\xbc\xc5\x64\xa2\xd5\xfa\xdd\x4d\ +\xff\x77\xba\xbd\xd7\x35\xb1\x2d\xa7\xca\x23\x19\xa1\x09\xca\x9e\ +\x86\x3c\x14\x10\x03\xda\x62\xa2\xf7\x01\xaf\x04\x58\x01\x0b\x8d\ +\x31\xbf\xef\x66\x05\x1c\xba\xa4\xa8\x3b\x12\xb2\x33\x32\x90\xa8\ +\x37\x23\xef\xc4\xf2\xad\xbb\xae\x78\x66\x86\xbb\x34\x44\x4b\x38\ +\xd8\x82\xa0\xa8\x48\x29\x0f\x7a\x75\xec\xec\x37\x02\x4d\xd2\xbf\ +\xbe\x55\xf8\x6a\x9d\x16\xe5\x26\xc0\xcd\xbe\x29\x5e\x6f\xf7\x4a\ +\x8c\x31\xa3\xdf\xe9\xd4\xdf\x00\x17\x2b\xac\xad\xd1\x0b\xff\xd4\ +\x80\xc0\xd6\x31\xc0\xe7\x7a\x7e\x78\xb8\x13\xfc\xee\xd4\x0e\xfe\ +\x9c\xc3\x8a\x85\x55\x50\xce\x2b\x9c\xdc\x12\xe6\xeb\x9f\xd4\xcd\ +\x22\x4a\x2c\xf6\xb8\xf7\xe4\x2f\x4e\x48\x7c\x66\x08\xc0\xd7\xfe\ +\xe9\x6e\x02\x9d\xe7\xd3\xc9\xbf\x41\x5a\x62\x02\xb0\x4f\x84\x97\ +\xf0\x2c\x22\xb3\x4a\xf8\xc9\x97\xd7\xf1\xb9\xee\xed\xc0\xf1\x7b\ +\x6c\xb3\x55\xbc\x9e\x82\x8f\xcd\x5e\x9c\xd5\xa9\xfc\x98\xfa\xa4\ +\xd4\xbe\x04\x9c\xd1\xdd\xf4\x9f\x60\xcc\xda\x7e\x8c\xa1\x17\x96\ +\x7a\x5c\x17\x68\xf1\x64\xb2\x8b\x55\xb9\xa4\x0e\xd7\xa7\xbe\x6f\ +\xfd\x11\xb6\x8d\xd7\x03\xd8\xf8\xd2\xe3\xd8\x1d\x8b\x7a\xd7\x60\ +\x1c\xe0\x25\x34\x30\xa1\xc9\x1f\x65\xd7\xbd\xdd\xfc\x2a\xf7\xce\ +\x2b\xfe\x69\xa7\xad\x8d\x7c\x26\x08\x20\x40\xfb\x5f\xee\xba\x6e\ +\xe0\x98\xa6\xce\x74\x4a\xb0\xd1\xe4\x28\x87\x23\xdc\x52\x22\x41\ +\x66\x0b\xea\x1b\x8d\x76\x3c\xb8\xf4\x83\x82\xbc\x60\x8c\x59\x23\ +\x2a\x6b\x60\xdf\xad\xb7\x12\x63\xcc\x18\x6c\xd1\xd2\x2c\x5f\x13\ +\xd7\xcb\x8d\x7b\x17\xd8\xb7\xbb\xf0\x1b\x63\xa4\x68\x8b\x51\xc2\ +\x6a\x08\x46\x1a\x63\x02\x07\x89\x3a\xc2\x8d\xc0\xeb\x11\x5d\xdb\ +\x32\x5f\xd8\x4f\x52\x64\x07\x15\xd6\xc5\x76\x8b\xfa\x12\xe8\x81\ +\xa0\xbb\xa9\xf0\x39\x6c\x6f\xc2\x0b\x81\xf9\x75\x74\xa1\xcf\x5d\ +\xd6\xda\x12\x96\xad\x7a\x72\x8f\xb8\xc8\x21\x79\x65\xc7\x15\x3e\ +\x06\x10\xe0\xfb\x17\xcd\xca\xce\xe9\x61\x01\x10\x51\xd9\x04\xd1\ +\xaf\x45\x78\x09\x8b\x54\xe4\xc6\x12\xc7\xb7\xa2\xb2\x14\x63\xaf\ +\x0a\xb2\x14\xdf\x1d\x78\x17\xf8\xa5\x31\xe6\x4a\xa0\x10\x16\x25\ +\x2f\xa5\xed\xfd\xe7\x78\xc2\x7c\x8f\x2d\x80\xef\x53\xff\xe0\xed\ +\x3b\xc0\xb6\x6e\x8f\x4e\x3f\x2b\xc7\xd8\x74\x51\x91\x6d\x4b\xfc\ +\xdd\x1a\xd8\x7e\x8d\xbd\x82\xa0\x89\x4c\xf6\xa3\x5c\xba\xe5\x47\ +\xa0\x77\x52\x5d\x8b\x74\x05\xe6\x01\xd7\x78\xe2\x5d\xe5\xa8\xf3\ +\xbf\x3e\x46\x6c\x17\x81\xe7\x72\xad\x2d\x33\x70\xbc\x4b\x50\xf9\ +\x15\x76\x0c\x78\xd4\x1a\x78\x0b\xc7\xd1\x83\x80\xbf\x04\x59\x01\ +\xc6\x98\xeb\x80\x1f\x76\x3d\x86\x9c\x72\xaf\x31\x66\xcd\x7a\xa4\ +\xf5\x56\x03\xa9\x13\x01\xec\x82\x9d\x1d\xd8\xb5\x50\x1f\x03\xf6\ +\x08\xba\xe9\xfc\xa4\x31\xa2\xea\xfc\x0a\x3b\xda\x3b\x2a\xdc\x2b\ +\x1e\x07\x26\xa6\x65\x7b\x7d\xdf\x76\xc6\x48\xce\x2e\xa4\xb2\x0b\ +\x51\x62\xc2\x5d\x9e\xb2\x83\x56\xbf\x65\xa8\xbe\x26\xf8\xbf\x31\ +\x31\xcd\xcc\x29\xca\x47\x0e\xe8\x73\xdd\x9e\xc7\x15\x07\x6f\x29\ +\x7f\x79\x33\x16\x7f\xdf\xff\xcd\x7a\x49\xf9\xdc\xdb\x39\x3d\x12\ +\xbb\xcd\x76\x94\xff\x2c\xeb\x6e\x3e\xc6\x85\x79\x02\x5b\x4f\x9f\ +\xb1\xbc\xf0\xfb\x63\xc2\x9f\xe3\xd3\x2e\x3f\x61\x78\x1d\xd8\x24\ +\x30\x13\xf0\x8c\xd1\x52\xc8\xc7\xd2\xd8\x1d\x81\x58\x05\x97\xf5\ +\x21\x70\x09\x2a\x97\x22\xc5\x05\xc9\xcc\x9c\x8a\x43\x1d\xb9\xd6\ +\x54\x12\xe1\x5c\xdf\x37\x8f\xda\xf2\x9d\x19\x13\xdd\x3e\xd6\xd6\ +\x3b\x4f\xc3\x18\xf3\x55\xe0\x96\x1e\xef\xee\x78\xd7\x75\x7f\xbd\ +\x42\x12\x80\xaf\xfd\x6f\xe7\xd3\x29\x41\x1e\xb6\x57\x59\xe0\xbc\ +\x80\x5c\x3a\xb5\xbe\xef\x6b\xae\x1a\xe1\x65\x9c\x90\xcc\x64\x03\ +\x13\x7c\x26\x18\x33\xae\x08\x4f\x55\x68\x01\x1c\x8d\xdd\xce\x3c\ +\x3a\x82\x6b\xcb\xfb\x0f\x7e\x2e\x70\x63\xd7\x4a\x1e\x9d\x64\xdd\ +\xf9\x05\x8e\x5a\xec\x7d\xb2\x38\x85\xfe\xcf\x97\xd7\x03\x47\x7a\ +\x13\x33\xff\x7a\xfe\xfc\x80\xf7\xba\x09\x01\x11\xef\x10\xb2\xdb\ +\xc5\x75\xdd\xc7\x83\xdf\x77\x73\x1c\xe4\x0c\xdf\x42\x8c\x95\xf1\ +\xac\xfe\x2c\x30\x59\x3d\x7d\x23\x39\xad\xb6\xc6\x9f\x9d\xe9\x54\ +\x5c\x6c\x46\xe6\x4f\x23\x5e\xfb\x05\x94\xfd\x93\xed\xd9\x5e\x43\ +\x49\x76\xde\xda\x24\x97\x2a\x8f\x15\x75\x39\xcb\xe9\x63\x60\x50\ +\x58\x01\xf5\x88\x01\xec\x0c\xec\xbb\xdc\xc3\xb1\xfe\x5a\x18\x0e\ +\xa7\xf6\x09\x3f\xcb\xfb\x87\xaa\xa1\x3d\xff\x9a\x1c\x4c\x85\xc2\ +\xff\x0a\xf0\x27\x4a\x4c\x2a\xaa\x10\x09\xec\xe8\xa8\xb1\x6a\x5b\ +\x71\x9d\x09\x9c\x39\x37\xc7\x31\x8b\x3d\x9a\xba\x8e\x0f\x84\xf0\ +\x03\xe7\xdc\xf9\xbe\x73\x41\x04\x4a\xe5\xb6\xb0\x58\x40\x32\xd3\ +\x51\x50\x6b\x01\x9c\xe2\x0b\x42\x18\xb2\xc0\x97\xc9\xeb\xf7\x12\ +\x99\xec\xdc\x5a\x85\x1f\xa0\x29\x93\x2d\xa0\x32\xd1\x57\x50\xd1\ +\xba\xd2\xc2\x31\xcb\x5a\x7b\x57\x0c\x3e\xf6\x9c\x9b\x2b\x2a\x4f\ +\xf5\xf8\xf5\x2a\xc0\x8f\x57\xd4\x20\xe0\x2e\x3d\x16\xef\x65\x61\ +\xed\xbe\x8a\xa7\xb6\x34\x61\xb3\xe2\xa2\x64\xe3\x19\x88\xbc\x1f\ +\xe6\x4f\x2f\xf1\x96\xaf\x63\xef\xd3\xfc\x87\xf3\xfd\xba\xef\x57\ +\xb1\xf9\xda\x2b\x22\xd4\x37\xcb\xcf\xaa\x34\x46\x11\x82\x91\xc0\ +\x2f\xc2\x82\x9f\x4d\x99\x6c\xc1\x41\x2e\x46\x65\x37\xe0\x5f\xbe\ +\x92\xd0\x6e\x0a\xe3\x46\xf1\xbc\x1d\x92\x99\xec\x5d\xc9\x0b\x3a\ +\xca\x4a\x8a\x32\xc6\x24\xba\xfd\x84\xae\xeb\x64\xfb\xec\x4e\x4f\ +\xbd\xa3\xb1\x41\xd4\x28\x71\xa0\x38\xc1\x83\x64\xd6\x4b\xea\x44\ +\x7a\xd7\x81\xb4\x9b\x1e\x13\x85\x87\x3c\x01\x18\x63\x92\x2c\x3f\ +\x14\x31\x47\x89\x21\x89\xc5\x98\xee\x4d\xe4\x95\x61\xf2\x40\x89\ +\xe1\x17\x5b\x62\x8b\x3a\xca\x0e\xfe\x79\x76\xfc\x39\xae\xeb\xbe\ +\x8a\xdd\x6e\x5a\xd1\x50\xf0\x85\x7f\x4a\x44\xc2\xdf\x85\x5f\x02\ +\xe7\x85\x91\x40\x3c\x33\x5b\x93\xed\xb3\x67\x38\x2a\x5f\xc2\x56\ +\x8a\xfe\x0c\xdb\x40\x64\x6f\x47\x63\xdf\x4f\x4c\x9b\xf3\x7e\x19\ +\xeb\x6d\x75\x63\xcc\x99\xc6\x98\xf3\xb0\x33\x0f\xbb\x7e\xae\x37\ +\xc6\x9c\xba\x5d\x08\x11\x0c\x6b\x9f\x33\x0f\xe1\x58\xa2\xed\xd4\ +\x34\x4c\x34\x38\x0d\x7c\x93\x11\x14\xc6\x34\xf5\x6a\x0a\x33\x28\ +\xac\x00\x89\x98\x00\x4e\x01\xce\xfb\xc4\xd6\x15\x2e\x99\x3e\xc3\ +\x0d\x0c\xee\x2d\x4b\xa7\x1c\xc7\x0a\xd7\xc1\x11\x5e\x82\x07\x4c\ +\x48\x66\xb2\x6e\xc8\xf5\x7d\x1b\x3b\xf5\xb6\x5c\xfc\xdd\x75\xdd\ +\xaf\x74\xfb\xfb\x83\xb1\x35\xda\x2b\x0a\xde\x1e\x33\x4c\xb6\x9f\ +\xb3\x4c\xdf\xea\x4b\xf8\x2b\x88\x01\x2c\x67\x59\xac\x2c\xec\xbe\ +\x48\x79\x34\x4a\x72\x31\xc6\xac\xee\xc7\x10\x7e\x4e\x89\xc2\x2a\ +\x07\xae\xf9\xf2\x88\xfc\x31\x67\x3d\xf1\x52\x2f\x2b\xa2\xb3\x35\ +\x15\x13\xe1\x7a\xec\x5c\xc0\xa8\xf0\x84\x2a\xbb\x34\xb5\xf7\xee\ +\x2e\x3c\xc1\x98\xab\x8a\xbd\x2b\x21\x17\xf9\xb1\x80\x01\x1b\x43\ +\x16\x99\x05\xb0\xb5\x31\x71\x96\x1f\x2b\xe5\xe5\x4b\xa4\xfd\x3a\ +\xca\x26\x3d\x62\x05\x51\xe0\x75\xe0\xb5\x30\xf3\x1f\x2a\x32\xff\ +\x3d\xec\x3e\x7b\x77\xdc\x89\xdd\x22\x5b\x21\x84\x1f\xd8\xf6\xf6\ +\xff\xce\x78\xb3\x1c\xe1\x1c\x2e\xcc\x97\xca\xef\x5d\x16\x29\x0f\ +\x02\xe7\x47\x91\x0b\x61\x8c\x89\x1b\x63\x8e\xc7\x26\xf9\x9c\x42\ +\x1f\x55\x95\x1e\x1c\xfd\xf7\x25\x89\xab\x82\x5c\x82\xa6\xf6\x6c\ +\x11\x61\x32\x76\x58\x69\x64\x62\x80\xc8\xd8\x40\x6b\xd7\x26\x28\ +\xf5\x7c\xce\x2b\x13\x4d\x60\x79\xe0\x09\x40\x6c\xe2\x4f\xb2\x87\ +\x69\x19\x18\xfc\xcb\xa5\x9b\x05\xe1\x08\xa2\x1f\x99\xf5\x88\xe7\ +\x48\x60\xbc\xa1\x45\x35\xb4\x6b\x4f\x08\xee\x74\x5d\x77\xb9\x96\ +\xd7\x7e\xe5\xd6\x8f\x86\xb8\xe0\x2b\xf0\x16\xb0\x4d\x40\x53\xcf\ +\x50\x3c\x39\xc3\x9d\xaf\xd5\x91\x5f\xdc\x77\x07\xce\xaf\xc5\xe7\ +\x1d\x6f\xb5\xfe\x0b\xc0\x65\x54\x50\x2a\xee\xd9\x3d\xf8\xc0\x9a\ +\x0f\xd5\xe2\x5c\xec\xc0\xd4\xa8\xd0\x24\x68\xd8\xf0\x9a\x7b\x02\ +\x08\x00\x81\x2f\x4d\x30\x03\x97\x1d\x18\x09\x01\xf4\x2c\xf9\xf5\ +\x71\x69\x58\xf0\x4f\x25\xb6\x12\xb6\x2e\x3e\xda\x85\xad\xdc\x32\ +\x6c\x6a\xf0\xa4\x97\xd9\x22\x5f\xc5\xd6\xeb\x97\x6d\xfe\x87\xfc\ +\xfe\xae\x21\x6c\x05\xa8\x6f\xa5\x8d\xe9\x5e\xdc\xd3\x0f\x28\x62\ +\xd3\x87\x67\x56\xb1\xb6\x64\x6b\x63\x8e\x12\xdb\x40\x76\x93\x2a\ +\xdd\xd6\x2b\xbf\x36\x6e\x5c\x6f\x2b\x20\x33\xd7\x13\xd1\x0b\x88\ +\xae\x69\xab\x00\xfb\xe5\xd2\x81\xad\xc3\x72\x04\x74\x10\x56\x38\ +\xa0\x08\x3b\x0c\x75\x0b\x60\x17\xec\x34\xe0\xee\x2f\xfc\xcc\xd0\ +\xa7\xa4\xba\x37\xd1\xb7\xe0\xfe\x10\x27\xf6\x68\x09\xf3\xbf\x92\ +\x58\x83\x47\xc8\xb6\x9f\x6f\x05\xec\xe9\x07\x9b\x86\x12\xde\x02\ +\x7e\x02\x9c\x53\xc3\xfe\xf3\x2d\x65\x7e\xae\x80\x0d\xb0\x5d\x8d\ +\x1d\x00\xb3\x89\x6f\xee\x6e\x55\x85\xaf\x7f\x9e\x07\xbf\xf3\x6a\ +\x1b\x0c\xb3\xf6\x2c\xc7\x09\x2e\xfd\x56\xc9\x62\x13\xd5\xa2\xc2\ +\x9e\xea\xe9\xf0\x80\x75\x93\x27\x78\x98\x48\x82\x01\xec\x93\x10\ +\x09\x01\x24\xec\x90\x84\xee\xda\xff\x09\xa0\x33\xd8\xfc\x4f\x09\ +\xe8\xb7\xa9\x2c\x13\xac\x1c\xb8\x42\xb1\x54\x3f\xfc\x4a\x08\xe0\ +\x1f\xae\xeb\x86\xe6\x8f\xbb\xae\xfb\x32\x70\xe3\x50\xd1\xfa\x8e\ +\x6d\xe3\xb5\x8d\xeb\xba\x57\xd4\x18\x8c\xbb\xa5\x0f\xeb\x22\x0f\ +\x5c\x35\xca\xd1\x8d\xb1\xbb\x2d\xc7\xb8\xae\x7b\x96\xeb\xba\x59\ +\x7f\xf4\x76\xa5\xc1\xae\xc3\x59\xbe\xe3\x50\x2d\x26\xc8\x6f\x82\ +\x62\x01\x89\x4c\xb6\x00\x7a\x03\xd1\x4d\x70\x5a\xd5\x71\x82\x89\ +\x2e\x16\xe2\x06\x00\xe7\xfa\xdd\xa9\x86\x1e\x01\x18\x63\x92\x79\ +\xdb\x7a\xaa\x3b\x1e\x2d\xa1\x65\xd6\xe5\xd3\x2c\xc1\x28\xf1\x78\ +\xa2\x2d\x1b\xb6\xc0\xc6\x55\x10\x6f\xd0\x95\xe0\xd8\x32\x3e\x77\ +\x3c\xd1\x4e\x11\xae\x07\xde\x04\x4e\x58\x49\xf5\x67\xae\xeb\xbe\ +\x1b\xc1\xf9\x16\x60\x83\x87\x3d\x05\xff\x3d\xec\x88\xae\x95\x80\ +\x63\x1f\x78\x6e\x66\x87\x2f\xf0\xb5\x0a\xd5\x55\xc0\x3a\x09\x5b\ +\xd0\x53\x6b\xe7\x9c\xb5\x81\x83\x02\x2d\x52\x91\xdb\x88\x2e\x18\ +\x18\x57\x64\x5c\x88\x59\x79\x6f\x08\x01\x34\x51\xdd\x68\xf4\x41\ +\x61\x01\xfc\x8c\xe5\x5b\x6a\xe7\xf8\x74\xee\x5f\x98\x26\x1e\x11\ +\xf1\x7d\x28\xc2\x9d\x25\x8e\x6f\x49\xf9\xd9\x7f\xaf\x2c\x2e\x9d\ +\xa1\xf6\x89\x2b\x30\xce\xf3\x8e\x6f\x12\xae\x1a\x84\x82\x9f\xc7\ +\xee\x78\x6c\xeb\xba\xee\xaf\x1f\x9d\x39\x33\x12\xed\xe6\x5b\x45\ +\xff\xeb\xf6\xab\x79\xbe\xe0\x6f\xe8\xba\xee\x25\x11\x09\x7d\xf7\ +\xef\x53\xd7\x75\x3f\xd8\x38\xee\x9d\x82\x4d\xdf\xad\x89\x04\x12\ +\x76\xfa\x53\x6f\xc1\x54\x16\x01\x51\x35\xeb\x11\x60\xc7\xa0\x16\ +\xe2\xfa\x69\xaf\xc7\x20\x37\x60\xc7\x21\x47\x00\xbe\x49\xf5\xc5\ +\x1e\x77\x7f\x99\x9f\x39\x17\x64\xfe\x3b\xd8\x81\x8c\x51\x47\x3d\ +\x3f\x40\xf5\xb9\x12\xfe\xff\x41\x15\x9c\xeb\x05\xd7\x75\xcb\x0a\ +\x0a\xfd\xf1\xf9\xe7\xbd\xf1\x8e\x1e\x47\x7d\x66\x0a\x56\x47\x84\ +\xf0\xc6\x4a\xb6\x59\xc7\xcf\x23\xd2\xfa\x3d\x71\xa4\xff\x3d\x57\ +\xfa\x04\x73\x49\xbd\xf7\xb1\x6f\x7a\xe6\x79\xc5\x36\x6f\x3d\xb1\ +\x16\x12\xc8\xc3\x21\x5f\x0d\x0c\x06\x66\xf3\xc0\x43\x11\x5e\xf2\ +\xce\x82\x48\x00\xa1\x2d\x11\x08\xeb\x50\x3d\x20\x6e\x40\x4d\xf9\ +\xe6\xbb\xac\xe1\x24\x1e\xfd\xc0\xfb\xc2\xf2\x64\xca\xfd\x25\x56\ +\xe7\x66\x52\x1f\x53\xe7\x65\x90\x52\x1d\x73\x0f\xaa\x40\x80\x8e\ +\xaf\xe4\x8b\xaf\x79\x76\xa6\x67\x8c\x39\x01\xd0\x18\x1c\x5b\x1c\ +\x98\x59\x77\xea\x6b\xe6\x36\xe0\x9a\xc7\xeb\x5b\x64\xf2\x1e\x36\ +\x01\xe7\xd2\x6a\xb4\x7d\xe7\xa4\x94\x23\x0a\xa2\x8a\x07\x08\x8e\ +\x26\xdb\x67\xf7\x79\x1e\xd7\x75\xd5\x18\xf3\x6b\x60\x51\x93\x60\ +\x3a\x95\x9f\x52\x79\xe7\xe8\x35\x5f\x73\x9c\x83\x08\x4c\xe6\xd2\ +\x07\x40\x4e\x8b\x48\x39\xa5\x44\xbc\x35\x81\xf7\x03\x5e\x54\x18\ +\x81\xc5\x7c\x85\x5c\x1c\x32\x04\xf0\xe8\x07\xde\x2f\x7a\xf8\xd6\ +\x05\x6c\x6e\x77\x98\xb9\xf1\x15\x8d\xae\xdd\x77\x77\xcc\x8c\x27\ +\x0a\x81\x8b\xfe\xcb\xeb\xc5\x63\x7f\x7f\xbb\x50\x01\x91\xf4\x6d\ +\xfe\x07\xb9\x03\xbb\x4d\x30\x3f\xd9\x63\x4d\x6f\xe6\x6d\xf3\x9c\ +\xa9\x44\x33\x67\xa0\x6c\x8d\xef\x0b\xfe\x6f\xfb\xa3\xba\xcc\x75\ +\xdd\x5e\x73\x01\xc3\x50\x48\x37\x8b\x47\x3c\x86\x7a\x1b\x22\xba\ +\x27\xb0\x29\xca\x68\x60\x65\x15\x49\x08\x7c\x0c\xba\x20\x97\x4e\ +\xbd\x8e\xea\x4b\x38\xf2\x18\x1e\x0b\x93\x01\x99\x74\x5d\x24\x00\ +\x5c\xff\x83\x5d\xb7\x96\xa7\x3f\xf2\xce\xa5\x8c\x6c\xc0\x00\x29\ +\x0b\x69\x3a\x13\x9f\x01\xc5\xc5\x44\xd3\x28\x26\xa1\xb0\x69\x10\ +\x01\x94\x10\xf0\xe1\xd8\xd4\xe0\xcb\xfb\x93\x00\xaa\x66\x3b\x63\ +\x33\xff\x3e\xea\xe1\xff\x5f\xe8\xba\xee\xc9\x81\x8b\xe1\x94\x4d\ +\xe3\x5e\x3c\xe7\x62\xe7\xf0\x45\x2b\x04\xca\x77\x92\xed\xd9\x3f\ +\x85\x5c\xe7\xe1\xd8\x3e\xed\xe5\xe0\x2f\xae\xeb\x7e\xbd\x96\x8b\ +\xd9\x73\xab\xad\x12\xf3\x45\x2e\xf6\x5f\x66\xbc\x4e\x42\x5f\xf0\ +\x4d\xe2\x97\xfa\x43\xf0\x8d\x31\xeb\x00\x87\x95\x5b\xc3\x9e\x3f\ +\x75\x4c\xcc\x8b\x3b\x9b\x88\xf2\x1d\x6c\x9b\xf4\x71\xbe\x90\x4a\ +\x89\x7b\x52\x6c\xb1\xd5\x7d\x02\xe7\xab\xf2\x44\xb2\x8c\x76\xe8\ +\xe5\xa6\x05\xf7\xb0\x60\xd6\xed\x69\xbd\xe4\xd2\xa9\xe1\xd8\x21\ +\x9f\x5b\x44\xf0\xc8\x3c\xe0\x5b\xc9\x4c\xf6\xe6\x80\xeb\x5d\x05\ +\xdb\xdf\x20\xc8\xfd\xfe\x85\xeb\xba\x17\x0f\x95\x18\xc0\x2e\x2c\ +\xcf\xa6\x1e\x25\xca\x7e\x35\x9e\xdb\x16\xd8\xa8\x0e\xf7\xe0\xe1\ +\xc8\x23\xa5\x63\x3f\xf5\x31\xff\x83\xf0\xe0\xcc\x99\x79\xec\x7e\ +\xfb\x96\xd8\x60\xe8\x07\x84\xb4\xcb\xae\xcc\x7d\x25\x8f\x9d\xa9\ +\x77\x2c\x36\xda\xfe\x0b\xd7\x75\xaf\xac\xa7\xf0\xfb\xbd\x07\x8f\ +\xc3\x26\x3f\xf5\x59\x03\xd1\xd9\xda\x92\xc8\xa5\x53\x87\x6a\xcc\ +\xb9\x43\x94\x67\xb1\x3d\x20\xb7\xf1\xd7\x89\xf4\xa1\x88\x1c\x5f\ +\xfb\x7e\x45\xe1\x41\x84\xf3\x3b\x27\x8e\xed\x73\xe7\xc6\x75\xdd\ +\x0f\x5d\xd7\x3d\x15\x1b\xe5\x7f\xb9\x0c\x8d\x16\x48\xca\x8e\xe7\ +\xe4\x7c\x6b\x2a\x2a\xb9\x1a\x1d\x72\xac\x94\x89\x7f\x4e\x7f\xc7\ +\x01\xaa\xd6\x50\x49\x61\xb7\x9c\x2e\xb7\x97\x1f\x6a\xfe\x77\xa6\ +\xc7\x88\xc2\x97\xa9\x8f\xf9\xff\x26\xaa\xa1\xe5\xbf\x50\x76\x33\ +\xca\x97\x09\x9d\xea\x53\xb1\x99\xac\xd8\xf2\xe1\xb3\x8c\x31\x53\ +\x81\x66\xe0\x3b\xe3\x57\xd2\xef\xcf\x58\x2c\xeb\x75\x5b\xf4\xf1\ +\x00\x41\xef\x8e\xcb\xb0\xdb\x53\x97\xfb\xff\x16\x22\xae\xd8\x0b\ +\x67\x77\x63\xe2\x8b\xa0\x55\xe1\x04\x60\xbb\xb0\x71\x5f\x00\xf9\ +\xd3\x9a\x1d\x8a\xce\x2e\x8a\x4e\xc1\x8e\xb6\xaa\x75\x11\x37\x01\ +\xbf\xc0\xf1\x3a\x29\xd5\x48\xb6\x07\x11\x4c\xd9\x67\xdc\xf6\x2f\ +\x7c\x24\xad\x2f\x2f\x95\x74\x29\xe5\xb6\xb1\xe7\x89\xdb\x63\x3b\ +\x2e\x3e\x6d\x56\x31\x97\x4e\x75\x44\xf8\x08\x37\xea\x9c\x94\x92\ +\xa6\xb6\x6c\x25\xef\x2b\x36\xa2\x9f\x27\x27\x54\x45\x00\xc6\x98\ +\xa6\x9c\x32\xa9\xc7\xaf\x2f\x0d\x1b\x64\x18\xd3\x58\xc2\x13\xfd\ +\x66\x9d\xee\xe1\x75\x42\xca\x3a\x0f\x18\xe9\x39\x77\xbd\xef\x94\ +\xbb\x18\x5f\x2a\x37\xfa\x5f\x21\x19\xe4\xb1\x05\x4a\x67\x1d\xb6\ +\xcf\xb8\x73\x67\x2c\xfe\x24\x3a\x3c\x1a\x38\xa2\xbb\x05\xb2\x76\ +\x42\x2f\x7f\x2f\x2f\xdd\xf7\xa3\xfb\x4d\xe0\x7b\xbc\xdf\x75\x3f\ +\xb6\xe6\xf0\x7a\x6b\xc6\x65\x87\x7f\x3f\x33\x23\x5c\xf8\x27\xa6\ +\x56\xd6\x22\x53\x41\x8f\x25\xda\xa6\xae\x8e\xc0\x89\xb9\x74\xf3\ +\xb5\xc9\x4c\x47\x59\xcd\x44\xcf\xbc\xef\xf9\x45\xc0\x69\xc6\x98\ +\x39\xd8\x9d\x19\x27\x24\x10\x78\x30\xc1\x99\x9e\xaf\x47\x49\x00\ +\xa2\x5a\xca\x45\x08\xba\xb6\x84\xd8\xb4\xe0\xc7\x07\xbb\x05\xf0\ +\x73\x7a\x8f\xd3\x0e\xdd\x0a\xf2\x44\xb7\xa3\xef\x5e\x72\xd5\xe2\ +\xe5\x64\x26\x1b\x68\x06\x2f\x2a\xc8\x21\x49\x61\xd5\x5c\x79\x22\ +\x74\x7b\xbd\x1f\xf6\x59\xf7\x3d\xdf\xfd\x3a\x67\xb1\x7c\xf5\xe4\ +\xa0\x80\x31\x66\x5d\x6c\xce\xfa\xfa\xc0\xd4\x05\x05\x7d\x26\x54\ +\xf8\xd3\x2d\x9b\x29\x7a\xa3\x6f\xe6\xd7\x63\xf7\x63\x15\x90\xdf\ +\xe5\xd2\xa9\xfb\x51\x79\x11\x47\x1f\x04\x3e\x48\xb6\x65\xbd\x3e\ +\x48\xf7\x6a\x63\xcc\x41\x84\x4f\x7d\x0e\xd3\xb3\x59\xdf\x15\x8c\ +\xe2\x5e\x36\xc2\x6e\x05\x6a\x8f\x6b\x5b\x32\xc1\x98\xdf\x15\x83\ +\x7b\x01\xc4\x17\x7b\x6c\x3f\xa8\x09\xc0\xdf\xfb\xef\x39\x26\xb9\ +\x00\x9c\x53\xe2\xcf\xbe\x58\xbf\x80\x98\x84\x26\x70\x3c\xfc\xa1\ +\x0c\x2f\xf3\x65\x86\xe6\xfe\x7f\x96\xd0\x43\xf8\x83\xc6\x81\x75\ +\x13\xfe\xe6\x09\x8a\x7e\x32\x16\xbc\x8e\xd8\x0d\xd8\x0d\x51\x0f\ +\x65\x29\x70\x7f\x2e\x9d\xba\x18\x69\xfa\x4f\xb2\xed\xe5\x52\xfe\ +\xf4\x77\xb1\x29\xe9\xe5\x07\xf5\x54\xe6\x21\x91\x19\x5c\x6b\x39\ +\x05\xcf\x21\x60\xdb\xaf\x58\x22\x0e\x10\x17\xfa\x35\x06\x50\x8d\ +\xc7\x91\xa0\xf7\xf0\xce\xc7\xb1\x19\x80\x01\xfe\x7f\xca\x29\xc1\ +\xc4\x11\x50\x80\xbe\x50\xca\xa7\x2a\xf3\x2c\xff\x70\x5d\xf7\x83\ +\x86\xf0\x7f\x22\xfc\x0a\xec\x17\x96\x48\x94\x4b\x37\x4f\x50\xe4\ +\x9f\xfd\x20\xfc\x3d\xd7\xea\x4a\xd8\x79\x8e\xf7\xa1\x9d\xb7\xe6\ +\xd3\xa9\xd1\x25\xac\x80\x8f\x7d\x45\xe5\x55\xf0\x0d\x51\xce\x0f\ +\x70\xbc\x98\x0c\xab\xf4\x8f\x0a\xca\xd9\xfd\x19\x08\xac\x86\x00\ +\x7e\x11\xe0\xeb\x3d\x56\x22\x1a\x3d\x9a\x0a\xab\xc0\x2a\x40\xd1\ +\xb1\xc1\xbb\xa0\x05\x2d\xd8\xa8\x79\x39\xc8\x7f\xc6\x85\x7f\x14\ +\xb6\x52\x6d\x7d\xec\xcb\x6d\xc3\x4e\xda\xe9\x2d\xfc\x93\x52\x6b\ +\x83\xdc\x42\xf4\x33\x08\x2b\xb5\x5c\x0f\x51\xf8\x6f\x7e\x52\xcb\ +\x3e\x9d\xad\x2d\x12\x42\x02\xef\x36\x49\x59\x75\x1d\xbe\xc4\xea\ +\xa2\x08\xaf\x51\xc0\xa9\x26\xe5\x7d\xf0\x5a\x00\x21\xe6\xbf\x87\ +\x9d\x01\x10\xf6\x14\xbe\x10\x71\x70\xa8\x3b\x3e\xf4\x4a\x37\xea\ +\x2c\xe7\x61\x2a\x76\xdb\xee\xb3\x2a\xfc\x82\x8d\xf4\x77\xed\x4e\ +\xbc\x5f\x08\x31\xfd\x73\xad\xa9\x61\x28\xd7\x01\x63\x07\xc9\xe5\ +\xaf\xa3\xaa\xb7\x8a\x04\x8f\xe7\x02\xc8\x2b\xb7\x96\x6d\x05\x48\ +\xa4\x59\x78\x82\xe8\x88\xfe\x90\xcb\x7e\x23\x80\xad\x13\x5e\x12\ +\xd8\x23\x80\x00\x1e\x0c\xf6\x13\x53\x82\xad\x9d\xaf\x57\x7a\xec\ +\x02\xa4\xe6\x2a\xb1\xae\xc4\x9a\xcf\x2a\xce\xa0\x5b\xe3\xd6\xb8\ +\xf0\xeb\xe7\xc2\x6a\x08\x84\x6f\x12\x7d\x1b\xb7\x5a\xb1\x32\x70\ +\x43\x2e\x9d\x0a\xf4\xf5\x9f\xb3\x05\x4c\xb7\x95\x29\xb2\x5e\xa4\ +\x04\x50\x5d\xd1\xdb\x70\x28\xdf\x6a\xe9\x57\x02\x78\x2e\xef\x04\ +\x9a\xff\x61\x02\xa4\xf9\x62\x2c\x80\x30\xa2\xc4\x07\x20\xb5\xbe\ +\xb4\xcf\xac\xff\xbf\xc3\x78\xb3\x9e\x58\xed\xdf\x45\xd0\x5a\x54\ +\x6e\x0e\x34\xfd\x5b\x53\x23\xb1\x93\x82\x9d\x41\x78\x2b\xab\x03\ +\xd7\xe7\x5a\x5b\x86\x87\x1c\x3f\x86\x32\xea\xfd\x3d\x65\x78\xb4\ +\x97\xa5\xd2\x1f\x72\xd9\x2f\x04\xe0\x9b\xff\x7b\x06\x1c\x7a\xdc\ +\x75\xdd\x60\xd3\x29\x11\x1b\x4b\xf8\x20\xc9\x28\xb0\x08\xd5\x40\ +\x02\xd8\x7e\xb5\xa2\x53\xa6\xe5\xf1\x99\xf4\xff\x8d\x31\xb2\x4c\ +\xb9\x47\x97\xef\xcc\xf4\x82\xda\xbe\x7b\x41\xfa\xec\x97\xf4\x6f\ +\xd0\xaf\x52\x6c\x4d\xb8\x2b\xb0\x88\xb2\x3a\x1a\xeb\x6a\x11\x8b\ +\xd7\xa0\x5f\x5b\x95\x30\x4d\x22\xc8\xfc\x8f\x85\x98\xff\x5d\x72\ +\x48\x7d\x27\xdc\xe4\x25\x64\xdf\xe6\xa9\x85\xb1\xaf\xd3\xf7\xb8\ +\x31\x5d\x69\x10\xf9\xff\xc6\x98\xd1\xc6\x98\xb5\x4c\xff\x34\x89\ +\x3c\x13\x9b\xa3\xdf\x1d\x47\x04\xfa\xfe\xe9\xe6\x35\x58\x3e\x69\ +\x69\x30\x22\x06\x4c\x2a\x9e\xbe\x59\xaf\xb8\x8f\x5f\x9e\xde\x27\ +\x01\x08\x12\x65\x11\x97\xaa\xe8\x0a\x45\x00\x27\x05\x98\xff\x9e\ +\x17\x12\x00\xf4\x47\x7e\xef\x5c\x67\x73\x26\xaf\xe1\xfb\xb6\x65\ +\x7d\xef\xe2\xc1\xe7\xff\xbf\x80\x9d\xaa\x13\xaf\x23\xd1\xac\x8d\ +\xad\x7b\xe8\x4e\x34\xf3\xb0\x7d\x03\x83\x04\x63\x5f\x60\x83\x41\ +\xb8\x7e\x15\xbb\xa7\xde\xd5\x83\x30\x55\xcc\x77\x36\x87\x7c\xf6\ +\xb6\x32\x4e\x36\x86\xe8\xe2\x55\xea\x68\x75\xd6\x65\x42\xd8\x67\ +\xe7\xad\x8d\x33\xa8\x08\xc0\x09\xc9\xe3\xd7\xf0\x08\x88\xd0\x0f\ +\x6d\x8e\x54\x6a\x7a\x5f\x2f\x61\x67\xcd\x0f\x0a\xb8\xae\x3b\x17\ +\xf8\x3a\x70\x3e\xf0\xaa\x31\x66\x64\x9d\xac\x81\x13\xe8\xd1\x94\ +\x35\x26\xcc\xeb\xd9\x06\x1d\xa0\xf3\xd4\x31\x31\x8d\xa8\x2f\x5f\ +\x84\x42\xbf\x18\xb8\x19\xf8\x39\xca\x5e\x2a\x32\xc6\xf1\x74\x5d\ +\xc7\xf3\x36\x10\x27\x3f\x3b\x2c\xd6\xe3\xff\xed\x52\x42\xb6\x8e\ +\x81\x54\xb4\x17\xea\xe5\xaa\xd3\x6a\xec\xb5\xc8\xeb\x9f\xe7\x5d\ +\x96\x96\x31\xc6\x24\x3c\x3b\xb6\xa9\x27\x42\x03\x80\x9e\xe7\x0d\ +\x77\x1c\x67\xb3\x3a\x5f\xff\x30\x34\xf4\x41\x3d\x8f\x6d\x4c\x5a\ +\xaa\xa2\xec\xc5\x7a\xe4\xff\xd7\x88\x47\xb0\xf9\x0b\xbf\xf0\x35\ +\xf2\x24\x63\xcc\x85\x51\xd5\x04\x74\xd3\xfe\xcb\x61\xbf\xb5\xbc\ +\x6b\x9f\x0d\x22\x72\x47\x46\x61\x6b\xdb\x07\x03\x16\x03\xe7\x3b\ +\xe8\xd5\x9e\xf2\x4e\xb2\xbd\xa3\xec\x00\x70\x5c\xa0\x68\x7b\x72\ +\x05\x12\x80\x97\xde\x48\x0a\x14\xa3\xac\x56\xf5\x88\xc9\xa0\xef\ +\x1c\x5d\x96\x05\xf0\xf9\xd5\x9d\x18\xc1\x7b\xea\x8f\x85\x05\x00\ +\x25\xe6\x6c\x4a\xf4\x83\x3f\x7a\x62\x78\x89\x48\xeb\xcc\xc1\xa4\ +\xdd\x2b\xb0\x02\x14\x38\x19\xe8\xf0\xe3\x2e\xe7\x01\xbf\x32\xc6\ +\x8c\x8c\xe8\x2b\x7e\x82\x1d\xde\xb9\x1c\xee\x7c\xdf\xf9\x67\xb0\ +\x29\x27\x3b\x00\x6b\x0e\x82\x47\xf3\x1c\xb0\xb3\x14\x39\x3b\x9e\ +\xe9\x78\xab\x12\xe1\x07\xf8\xfe\xe7\x8a\x8b\x37\x4c\x32\x33\x4c\ +\x61\x15\xf1\x12\x11\x5b\x00\x0b\x28\xca\xd2\x12\xf1\x8a\xa1\x43\ +\x00\x4f\x7c\xe8\xfd\x92\xe0\x64\x9e\xd0\xc4\x09\x51\xb6\xe8\x87\ +\x1b\x5d\x45\x54\x1c\x56\x30\xf8\x24\xb0\x17\x30\xdf\x37\xbd\x7f\ +\x0e\x3c\x5d\x2b\x09\x5c\x72\xc8\xb6\x22\xf0\x8d\x80\x43\xf3\xb0\ +\x8d\x32\x82\xf0\xe5\x41\xf0\x48\x5e\x16\x38\x28\x99\xc9\xba\x89\ +\xf3\xb2\x55\x59\x42\x27\xde\xf1\xc2\xb2\x37\x72\x64\x63\x70\x42\ +\x90\x35\xa5\x68\x92\xf0\x1a\xfe\x6a\x30\x47\x03\x3c\x64\x63\xcc\ +\x08\xa2\x1f\x8a\x53\x5f\x02\x88\x49\xa0\xf0\x17\xb6\x88\x7b\x6d\ +\x81\x3e\x8c\x0d\x00\x6e\x49\xfd\xf7\x33\xd7\x41\xbc\xaa\x49\x26\ +\x21\xdc\x31\x88\x49\xa0\x03\x9b\xf7\xde\xb5\x88\xc6\xd4\x4a\x02\ +\xbf\xed\xc8\x8f\x57\x3b\xa4\xa3\x17\x01\x04\xf9\xff\xb9\x89\x2d\ +\x09\x06\x70\x6a\x8d\x8f\x25\x22\xf2\xad\x44\x26\xfb\x66\x04\xe7\ +\x2a\x14\x43\x6a\x56\x04\x6f\x34\xb0\x4e\x84\xd7\xdd\x51\xa2\x1c\ +\xd8\x19\x32\x04\x60\x8c\x49\x14\x35\xd0\xff\xe7\xc5\x82\x53\x0c\ +\x76\x7e\x44\x08\x5e\x68\x51\x63\x94\xa3\x32\x22\xcc\xc6\x72\x28\ +\xd9\x2a\x5c\x0b\x3a\xe8\x27\xfd\x3e\x86\xed\x89\xdf\x9d\x04\xa6\ +\xd7\x40\x02\x37\x52\x49\x30\xcf\xd1\x55\x83\xdc\x85\x7e\xc6\x75\ +\x9e\x06\xd7\x25\x54\x8a\x2d\x3d\xef\x9b\x42\x30\xe9\x2b\xce\xa1\ +\x11\x0b\xe6\x9c\x64\x7b\x87\x0e\xf2\xf5\x55\x56\x10\x30\x56\xe9\ +\x83\x11\xeb\x97\x6f\xd4\x0f\xd7\xef\x78\x8e\x6c\x86\x0d\x9c\x2d\ +\x87\x67\x6d\x17\xd9\x3b\x29\x31\xfe\x79\xb0\xbf\x1d\xbf\x13\xee\ +\xa9\xc0\x61\x40\x8b\xff\xeb\xd1\x3e\x09\x6c\xeb\xba\xee\xfb\xe5\ +\x9e\xcb\xef\xeb\x17\xa6\xe1\xc2\xb6\x42\x47\x60\x2b\xf0\x06\x0a\ +\x39\xe0\xb7\x4d\x99\xe0\xae\xc1\x7b\x4c\x30\xab\x7f\x50\x2c\x7f\ +\x87\xe2\x05\xb8\xc9\x9f\xea\xb4\xfc\x97\xa4\x5b\xe2\xa0\x7b\x47\ +\x78\xdd\x8a\xed\xf7\x30\xa8\xfd\xff\x72\x09\xe0\xa4\x4a\xfd\x7f\ +\x27\x26\x71\xaf\xa8\xa9\x7e\xb8\x7e\x41\x75\x8f\x20\x02\xf8\xc4\ +\x18\x29\xed\xda\x0c\x89\x78\x80\x31\x66\x2f\x6c\x87\x9e\x91\x3d\ +\x48\x60\xeb\x0a\xd2\x98\xd7\x2f\xa1\xcd\x8f\x0c\x7e\xb8\x0c\x57\ +\xa2\x4e\x8f\xad\x08\x1f\x79\x21\x5d\x7a\xf6\xdc\xc6\xfc\x38\x2e\ +\x5c\x44\xb1\xec\x7c\xfb\xa5\xc3\x25\xb8\x39\xac\xa8\xae\xa5\xc2\ +\x76\x11\x5e\x77\x41\xc2\x13\x8f\x32\x43\xca\x05\x28\xc1\x58\x97\ +\x84\xb5\x00\xf3\x8a\xde\xfa\xd4\x7f\x07\xa0\x0b\x9f\xcf\x9d\x9a\ +\x0a\xbb\xc6\x5b\x08\x1f\xf9\x24\x4d\x52\xc7\x3e\x05\xd1\x92\xc0\ +\x1c\x6c\x4f\x85\xee\x9a\x70\x34\x70\x4c\x05\x79\x02\x37\x94\x5a\ +\xb0\x81\x6a\x4c\x59\x6b\x80\x35\x56\x67\x32\x31\x2c\xb0\x4d\xfb\ +\x82\x02\x5f\x7d\x37\x5f\x7e\xb1\x8d\x03\x37\x3d\x39\xc3\x0d\xdc\ +\xff\x57\xe1\x3b\xf4\x9d\x35\x5a\x09\xf2\x62\x7b\x42\x0e\x7a\x0b\ +\xc0\xe9\xc3\x6c\x8c\x61\x3b\xb2\x50\x89\x05\x00\xb2\x61\x3f\xb2\ +\xdc\x96\x1a\x0b\x2d\x37\xf6\x20\x3c\xd0\xb7\xc4\xab\x68\x62\xd0\ +\x40\xe3\x89\xb8\xf0\xab\x1e\xbf\x6b\x03\x4e\x29\xc3\xfc\x17\xaa\ +\x48\xc9\x56\x19\xf0\xc5\x9a\x57\x34\x0a\x4f\xed\x63\xcf\x6e\xa7\ +\xf6\xfe\x82\x74\x6a\x18\xf0\xbd\x68\xcd\x52\x3a\x54\x65\x41\x0d\ +\x4a\x77\xd0\x58\x00\x0e\xb6\xcb\x6b\xa5\x18\xd9\x8f\x37\x3a\x06\ +\x09\x6e\x38\xe2\x6f\xf7\xdc\xce\x0a\x00\xd7\x75\xb5\xa8\x5c\x84\ +\xed\x29\xdf\x6d\xad\xf1\x33\xbf\xa1\x47\x29\x8c\xa3\x9a\xa0\xac\ +\x0e\x78\x98\x64\xe5\x62\xbe\x73\x95\x10\x27\xbb\xdc\x3a\xff\x8f\ +\x81\x9d\x82\x7c\x7f\x1f\x07\x11\x71\xc3\x1a\x85\x27\x8a\x01\x5b\ +\x00\x83\x6d\x0b\xb0\x1c\x02\xa8\x56\x03\xac\x45\xff\xa5\x8e\x3a\ +\xa2\x1c\xe1\xa5\xc7\x84\x7d\xdf\x5f\xb0\x53\x6d\x83\xb0\x85\x31\ +\x66\xf8\x50\x21\x81\x19\xb6\x35\xf7\xd5\x01\xbe\xfd\x9d\x7d\xb8\ +\x02\x52\xcd\xfb\x70\xd0\x81\xce\x64\x5b\x89\x90\xbd\x79\xe3\x79\ +\x57\xf9\xa5\xcc\xf9\x90\x85\xab\xbe\xfb\xf7\x79\xd7\x75\x03\x2b\ +\x1c\x73\x13\x9b\x47\x28\x9c\x1e\xf1\x5a\x55\xe0\x3f\xc3\x32\x81\ +\xf9\x0a\x32\xd4\x2c\x80\x93\xa9\xae\x9b\xcf\x48\xfa\x37\x77\x7c\ +\xef\x82\x3a\xc3\x42\x34\x67\x31\x66\x9b\x92\x06\x65\x05\x6e\xce\ +\xc0\x46\xb9\x7b\x9b\xa4\xad\x2d\x7b\xe6\x5b\x53\xa5\xae\xe9\x12\ +\xec\xb0\x91\xee\xd8\x6e\x95\x58\x49\x0d\x5f\x72\xda\xd1\x5a\x71\ +\x09\x9e\xd7\x20\xb2\x10\x9b\x4e\x3d\x50\x18\x4e\x48\x22\xd2\x8d\ +\xcf\x3f\xaf\x33\x5c\xf7\x37\xd8\xd6\x64\x67\x01\x53\x9a\x84\xb3\ +\xe3\xc2\x14\x60\xca\xfe\xa3\xbc\x13\x87\x3b\x8c\x72\x5d\xf7\xc5\ +\xa0\xbf\x5f\x3a\xb1\x45\x70\xe4\x54\xa2\x6f\x57\xd7\xa9\xc2\xa3\ +\x21\xc7\xf6\x65\xf0\xd4\x54\x40\x19\x7e\x61\x18\x41\x78\xb1\x90\ +\x69\xaa\xb9\xd6\x66\xa1\xff\xfb\xc5\x6d\x8a\x70\x28\x21\x23\xc0\ +\x9a\x54\x67\x2c\x11\xb9\x85\xde\x25\xad\x82\x1d\xba\x71\xf8\x60\ +\x78\x19\xb9\x74\xf3\x68\x45\x6f\x06\xa6\x01\x17\x84\x10\xda\x9b\ +\xc6\x98\xf7\x59\x7e\xfe\xa0\x7c\x5c\xe4\x3e\x7f\x57\x60\x41\xa5\ +\x04\x30\xbf\xa0\xd7\x03\x26\x40\x5f\x2d\x43\x59\x42\xff\x05\x74\ +\x83\xf0\xd3\xce\x74\xf3\xef\x9a\x32\x1d\x6f\x85\x3c\x8f\x0f\x09\ +\x18\x47\xdf\xd7\xac\x6f\x27\xc6\x8e\x28\xbf\xac\x83\x40\xbe\xee\ +\x48\x21\x6c\x0b\xf0\x4b\x83\x8d\x00\xaa\x35\x47\x42\xcb\x80\xfd\ +\xda\x9c\xb5\xfa\xf9\x3e\x04\x48\xe7\x5a\x53\x81\x56\xc0\x13\x33\ +\x67\xaa\xc0\x51\x4e\x70\x24\x7c\xdc\x60\x70\x03\x0a\x93\x9a\x47\ +\x80\x5c\xed\x5b\x4f\x27\xe5\xd2\xcd\xa5\x12\x70\x82\x82\x56\x9f\ +\xc3\x76\xbe\xa9\x06\xb1\x10\x5b\xf6\x63\xc2\x77\x51\xfa\x0b\xa3\ +\x04\x99\xea\x4d\x6a\x8e\x6c\xaa\x54\x3e\xdd\xb2\x89\xa8\xde\x02\ +\xac\x52\x87\xeb\xfd\x4b\x62\xea\x1b\x5e\x80\xff\x9f\xc0\xce\x4f\ +\x28\x07\x0f\xd0\x4f\x69\x2a\xa1\x04\xe0\xef\x00\xec\x5e\xca\xd1\ +\x09\x96\x44\xad\xb6\x17\x5a\xad\xd8\x0a\x09\xde\xcf\xf6\xfd\x67\ +\x6f\x33\xcf\x3b\x0a\xb8\xbe\x67\x1c\x00\xf8\xe6\x40\xae\xf0\xe2\ +\x69\x2d\x31\x4f\x65\x0a\xb0\x8f\xff\xab\x75\x41\x4e\x28\xf1\x27\ +\x73\x03\xdc\x00\x80\x13\x8d\x31\x91\x15\xee\xc4\x96\x38\x8b\xb1\ +\xc3\x32\x06\x1a\x47\x14\x90\xd3\x0a\xa7\x8e\xa9\x79\x57\x22\x97\ +\x6e\x36\x8a\xde\x4e\x7d\x06\xd5\xe4\x09\x9f\x8e\x3d\x1c\xd8\xb6\ +\xcc\xf3\xdc\xef\xba\xae\x37\xa0\x04\xe0\x1f\xdb\xad\x3a\x65\x3c\ +\x20\x04\x20\xc0\x94\x5c\x6b\x4b\xe8\x8b\xbd\xe9\xf9\xe7\x3d\xe0\ +\xfb\xc0\x75\x2c\xbf\xf7\x7d\x4d\x19\x91\xf4\xfa\x68\xfe\xc9\x29\ +\xa7\x58\xf4\x4e\xc6\x16\xfc\x74\x37\x0f\x8f\xc8\xa5\x9b\x57\x0e\ +\x73\x03\x08\x1e\x3d\xbd\x5e\x4f\x2b\xc0\xcf\x00\xec\x2b\x9d\x77\ +\x94\x5f\x26\xbc\x3c\x01\x5c\x3c\x4b\x81\x7b\x06\x01\x01\xc4\x50\ +\x26\x79\x31\xe7\xb2\xce\xd6\x54\x55\x6d\xbb\x3a\xd3\x2d\xf1\x5c\ +\x6b\xea\x1b\x20\xff\xa2\x7e\x69\xea\x6f\x78\xc2\x53\x21\xc7\xda\ +\x19\x84\xfd\x14\x9d\xba\x88\xe1\xc0\x65\x8f\xad\x8b\xe8\xff\x75\ +\xa6\x53\xa1\xe6\xa2\xcf\xac\x3f\xf0\xd9\xf8\xcf\x3e\x11\x38\xc0\ +\xbf\x8c\x31\x2b\xf7\xab\xf0\x9f\x3e\x3a\xee\x79\x9c\x02\x72\x4e\ +\x40\x3c\x26\x05\x32\xbe\xc4\x9f\x07\x5a\x3b\x6b\xc6\x39\xfe\xd2\ +\x83\xb6\x5c\x7d\xb9\x67\xd2\xb7\x4b\x36\x2a\x2c\x6e\x23\x2a\xb7\ +\x11\x52\x40\x33\x00\xf1\xaa\x63\x44\xf8\x4f\x6e\x52\xea\xdb\x9d\ +\xe9\x96\xe1\x7e\xd1\x59\x28\x96\x9c\x3a\x5a\x72\xe9\xe6\x78\x67\ +\xba\x79\x67\x41\xef\x40\xf8\x63\x1d\xdd\x53\x05\xae\x18\xd6\x96\ +\x5d\x16\x62\xfe\x6f\xcf\x20\x44\xbc\x2e\x8f\x41\x06\xc4\x02\xe8\ +\xc2\x97\x05\x4e\xca\x4d\x4c\x9d\x97\x9c\x16\x3c\x43\xce\x27\x01\ +\xd7\x18\xf3\x6d\x6c\xd5\xe2\x57\xb1\x4d\x2f\xae\x06\xbe\xd5\x1f\ +\x17\x99\x9b\xd4\x3c\xdc\x2b\xc8\x34\x6c\x73\x8e\x58\x08\x39\x1f\ +\x09\xa1\x11\xe5\xc0\x44\xac\x0f\x0a\x6c\x78\xed\x1b\xb1\x75\x59\ +\x3e\x5f\xa0\x1c\x7c\x1d\xdb\x44\x65\xf9\xd7\x29\xfa\x06\xf0\x26\ +\x11\x77\xcb\xa9\x41\xbd\x6c\x85\x72\x83\xa0\xb3\x14\xee\xce\xa5\ +\x53\x0f\x83\xb8\x0e\x3a\x5f\xc5\x59\x24\x8e\x26\xd4\xf3\xd6\xf7\ +\x94\x31\x82\xec\x01\x1c\x28\x36\xd2\x1f\xaf\xf3\xb5\x2d\x05\xef\ +\x2f\x25\xcc\xff\x72\xfd\xff\x02\xf0\xdf\xc1\x60\x01\xc4\xa8\x22\ +\x62\xe9\x77\xe8\x4a\x0c\xf0\x22\x39\x13\x47\x0e\x2d\x4e\xde\xa4\ +\xe4\xf5\xbb\xae\xeb\xb9\xae\x3b\xd3\x75\xdd\xb3\x80\xef\xd0\x4f\ +\x8d\x2f\x73\xe9\x54\x0a\x95\x3b\xb0\xcd\x39\x4a\xf9\xb5\xbb\x74\ +\xb6\xb6\x84\x8d\x97\x7a\x33\x28\x0e\xa0\x40\x51\xb9\xf1\x8a\x83\ +\xb7\x94\x2a\x08\xa0\x17\x92\x99\xec\x22\x94\x7f\x0e\x42\xcb\x75\ +\x63\xe0\xa7\xc0\xcd\xa0\x2f\x7a\xf0\x8e\xaa\xf7\xa1\x57\xd4\xf9\ +\xaa\xf2\xa2\xd8\xd1\x65\xa7\x01\x13\xfa\x41\xf8\x01\x6e\x03\x99\ +\x1b\x81\xf9\x9f\x1f\x2c\x04\x70\x4a\x35\x82\xec\x27\x8f\x15\x07\ +\x78\x81\x0c\x03\xbd\xae\xe8\x15\x0e\x2b\x9e\x3a\xb6\x2c\x41\xf0\ +\xc9\xa0\xae\x0d\x42\x8b\xe9\xe6\x64\x2e\x9d\xfa\x3e\xf0\x24\xb6\ +\xe1\x47\x5f\xd7\x36\x0a\xd1\x95\x42\xae\xf7\x2d\x82\x03\x81\xac\ +\x1e\x63\xfc\x4b\x0b\x9d\x4a\x35\xf6\xc8\xa0\x38\x80\x4f\xea\x97\ +\x5a\x0d\x37\x28\xe1\xf8\x02\x9e\xf0\x35\x6d\x93\xff\xff\xfe\x4c\ +\x63\xce\x03\x57\x26\x33\xbd\xcb\x7f\x27\x18\x93\x94\x41\x6a\xfe\ +\xf7\x45\x00\xd5\xc5\x07\xf4\x13\x33\x66\xa0\xb1\x0a\xe8\xf5\x5e\ +\xcc\x3b\x3a\x9f\x6e\x8e\x0f\xe4\x85\xe4\xd2\x29\x27\x97\x6e\xde\ +\xbe\x88\xdc\x05\x5c\x43\x8f\x86\x9c\x25\xb0\x86\x54\x11\x50\xfd\ +\xa0\x48\xfc\xf1\x0f\xa5\xd2\xbd\xfb\x51\x84\xf6\xfd\x97\xd7\xe9\ +\x7b\x6b\xfd\xbc\xf3\xdc\x4d\x00\x00\x13\xff\x49\x44\x41\x54\xb3\ +\x8c\xc7\x9c\x98\xfe\x27\xc4\x4f\x3b\x58\xcb\x8f\xfe\x23\x70\x7f\ +\x0c\xbc\xc1\x40\x00\x55\x1a\xe0\x32\x18\x2c\x80\x2e\x8c\x50\xf8\ +\x8d\x22\x57\x17\x6c\x6f\xfb\xfe\x17\xfc\xd6\xd4\xd6\xc0\x1f\x41\ +\x1e\xc1\x66\x24\x56\x38\x8b\xa1\xba\x5e\xf5\xb9\xd2\xd5\x7f\x61\ +\xb8\x21\x28\xa5\x38\x91\x99\x9d\xc7\x8e\x7f\x2f\x36\x64\xbd\xb7\ +\xf6\x17\x65\x4a\xfc\xdc\x8e\x5e\xcf\xc6\x18\x93\x04\xd2\x95\xb8\ +\xd2\x0a\xf7\x3f\x1b\x51\x03\xd8\x72\x50\x2f\xcd\x38\x98\x16\x8a\ +\x03\x7c\xcf\x43\xf6\xcc\xa5\x53\xe7\xe0\xe9\x8d\xc9\x69\x1d\x75\ +\x4b\x6f\xcd\xb7\x36\x8b\xe7\x48\x13\xb0\x2f\xca\x71\x08\x7b\x51\ +\xd3\x70\x54\xaf\xda\x7d\xfd\x0d\x8c\x31\x6b\x01\xef\x60\x7b\x0b\ +\x96\x13\xfd\xde\xd2\xff\x79\x3e\x60\x69\x3e\x00\x32\x9d\x81\x6f\ +\x11\x36\xd8\x70\x07\x9d\xf9\x87\x42\x8e\x1d\x4c\xf9\xc1\xbf\x4f\ +\x5e\x78\x7f\x5e\x7c\xd5\x04\x10\xde\x4c\x43\x00\x5d\x32\xc8\x5e\ +\x92\x60\xdb\x69\x5d\x85\x23\x3f\xcb\xa5\x53\xbf\x55\x91\x3f\xa1\ +\xfa\x7e\x53\x26\x5b\xf3\x03\xcf\xa5\x5b\x1c\x07\x12\x1e\x6c\xa3\ +\xe8\xd7\x44\xf9\x2a\x36\x33\x2f\x02\x0b\x4b\xaa\x75\xa7\xd6\x05\ +\xd6\x72\x5d\xf7\x55\x3f\x75\x78\xad\x32\x9f\xd3\x37\x82\x08\x20\ +\x99\xe9\x58\x96\x6b\x4d\x9d\x8c\x70\x3f\xf5\x9b\xf6\x3c\xd4\xb0\ +\x08\x74\x52\xe2\xa2\xff\x05\xae\xa1\x18\x1c\x58\xac\x2c\x90\xbe\ +\x14\xb8\x72\x28\x10\x80\x13\xb3\x63\xbf\xef\xe9\x6d\xc2\x78\x2a\ +\xc8\x60\x1d\xb6\xe9\x60\xb7\x84\x2e\x12\xd5\x73\x80\x47\x72\xe9\ +\xd4\x83\x82\x3c\xea\x89\xbe\x24\x1e\x1f\xab\xa0\x20\x8a\x38\xda\ +\xd4\xf6\xfa\x27\xa6\xd8\xb2\x74\xb3\x38\x9e\x80\x83\x20\x38\xaa\ +\x24\x81\x2d\x45\xd8\x16\xd5\x5d\x3c\x3b\x37\x71\x5d\xa2\x0e\x3e\ +\xa9\x7c\xdc\xcf\xef\xf7\x38\x63\xcc\xe5\x6e\xe0\x84\x60\xe7\x71\ +\xf0\x6e\x03\xbe\xd6\x90\x7d\x3c\xe0\x5c\x55\x0d\xec\xfc\x63\x8c\ +\xf9\x6a\x91\xf0\xcc\xd4\x12\x96\xf3\x90\xb0\x00\x9c\x9c\xb2\x47\ +\x10\x01\x88\x07\x38\xcc\x1f\xe4\x2f\x4f\xb0\x79\xe0\xfb\x03\xfb\ +\x29\xea\x89\x52\x40\x78\x43\xa0\x03\xf4\x5d\xb4\x38\x2f\x97\x4e\ +\x2d\xc3\x46\x78\x1d\x60\x65\x1c\x56\x05\xd6\x47\x19\x23\x56\xc3\ +\x0f\x43\x29\x77\x08\x69\x75\xae\xbc\x68\x2d\xcf\xf2\x7a\xa8\xb8\ +\xd5\xd5\x48\x81\x9f\x5c\x71\xf0\x96\x67\x1c\x7f\xfb\x0b\xcb\xf9\ +\xa2\xc9\xf6\x59\x85\x7c\x7a\xcc\xcf\x15\x67\x77\xfa\xbf\xe0\x6b\ +\xb0\xe1\xbf\xea\x79\x97\x36\x4d\x9b\xa3\x25\x7c\xff\x4a\x95\xc1\ +\xe9\xa1\x83\x76\x87\x4a\x0c\x20\x39\xad\x43\x73\xe9\xd4\x1b\x43\ +\xe8\x45\x8a\xff\xa2\x62\xd8\x46\xa6\x1b\x0d\xa2\x6b\xfb\x98\xda\ +\xca\x71\xbb\xde\xef\x2d\xd8\x3d\xf1\xb2\xb0\x4a\x8c\xd6\x99\x1f\ +\x3a\x37\x63\x87\xab\x2c\x87\x44\x66\xce\x9b\xb9\x74\xf3\x44\xbf\ +\x70\x29\xfe\x19\x15\xfe\x05\x02\x3f\x48\x4e\x9b\x13\xe6\xea\x1e\ +\x52\x85\xef\x0f\x03\x30\xa9\x3a\xd4\x47\x8d\x4b\xe0\x28\xf0\x72\ +\xf1\xbf\xfe\x36\x65\x56\x50\xcc\x47\x83\xc7\x4b\x7d\x77\xdc\xb8\ +\x4a\x9a\x7c\xdc\x52\xc9\x97\x7e\x54\x24\xfe\xe8\x42\xb9\x29\xac\ +\xc9\x88\xa0\x7f\x00\xfe\xf4\x19\x7d\x27\x79\xe0\xb8\x44\x26\xfb\ +\x52\xd0\xc1\x6d\xc6\x9b\xe1\x4e\x85\x91\xff\x6e\xfe\xff\x55\x83\ +\x86\x00\x0a\xda\x6b\x14\x78\x40\x8c\x23\xc4\x6d\x45\x66\x35\x08\ +\x20\x12\xcc\x89\x37\x79\x81\x09\x38\xae\xe3\x6c\x03\x34\x97\x1b\ +\x49\xa0\xf2\xf2\xd2\xcd\x09\xa8\xb3\xf7\xad\x80\xbc\x28\x27\x06\ +\x59\x08\x2b\x38\x8a\xc0\xb9\x48\x38\xa1\x16\x94\x4b\xbd\x2a\xb4\ +\x7f\x1c\xee\x1f\x2e\xfd\x9f\x3f\x53\x4b\x94\xfa\x67\xc6\x98\xc0\ +\x64\x13\x11\x5e\x61\xf0\x8d\xdd\x1e\x82\x90\x3b\x9c\x29\xa1\xc3\ +\x25\xca\x89\x3d\x88\xaf\xc5\x9f\x27\xbc\x4b\x6d\x29\xd7\xe8\x78\ +\x63\x4c\xe0\x58\xf0\x44\x7b\x76\xa1\x0a\x5f\xf1\xad\xbd\xcf\x02\ +\x3c\xe0\xda\x58\xdc\x69\x4b\xb6\x05\x8f\x27\x33\xc6\x7c\x1d\x38\ +\xaa\x9a\x93\x17\xe0\xde\x27\x67\xb8\x3a\x94\x08\x20\x56\x42\xdf\ +\x2c\xaa\x62\xc1\x35\xb0\x3c\x72\x8a\x77\x77\x89\xe3\xe5\x44\xe2\ +\xb7\x02\x36\xf6\x9b\xa3\x56\x43\xc8\x6b\x01\xf7\x85\xb9\x02\x4d\ +\x6d\xd9\x59\x62\x5b\x76\xcd\x5f\xc1\xdf\x85\x02\x37\x78\xc2\x89\ +\xb1\x73\x66\x05\x3e\xc7\x1d\xb6\x36\xc3\x63\x52\x55\xe0\xaf\xcb\ +\xad\x78\x7a\x20\x6e\xcc\xa9\xc7\xdf\x7b\x8a\x47\x48\xcb\xb0\x06\ +\xca\xc6\x6c\x41\xde\xac\x91\x00\xba\xa3\xda\x42\xa7\xcd\x80\x73\ +\xc2\x48\x20\x91\xc9\x4e\x47\xe4\x10\xc2\x87\x8b\xae\x08\x66\xff\ +\x35\x9e\x7a\xc7\x06\x95\xfa\xfa\x9a\x7f\xf8\x32\x8f\x87\x8b\xca\ +\x84\xaa\x34\xa9\x70\x77\x52\x78\x72\x50\x11\xc0\xc6\xc3\x74\x51\ +\xdf\x6e\x0b\x93\x83\x0e\x0c\x6b\x9f\xad\xd8\xb9\x76\x8d\x38\x40\ +\xf5\xf8\x5b\x32\x93\x5d\x12\xb2\xe0\x36\x60\xf9\x9e\x80\x61\x36\ +\xfc\xcc\xc3\x57\xca\xbd\xe6\xff\xf7\x2d\x82\x9b\x88\x94\xe3\x0a\ +\x4c\x4a\x94\xf0\x6b\x93\x6d\xb3\x1f\x13\xdb\xef\x6e\xee\x0a\xf6\ +\x0e\xf2\x20\xe7\xc4\x62\x89\xe3\x87\xb5\xcf\x59\x56\xe2\x01\x5d\ +\x01\xd5\x4f\x16\x2a\x2a\xf7\x3c\x3d\x00\xe6\x7f\x49\x02\x98\xdb\ +\x29\x17\x95\xf1\xf7\x89\x12\x67\x7e\x98\xc1\xd1\x48\x62\x28\xe2\ +\x63\x81\xdf\x94\x38\x3e\xba\x1c\x02\x50\xd0\xc9\x8f\xbf\xac\x00\ +\x7e\x62\xcf\xbb\xd5\x06\x23\xf2\x70\x97\x31\x26\xb4\xdb\x52\x22\ +\x93\x7d\x4e\x45\x77\xf1\x2d\x3f\x5d\x01\xde\xc1\x3c\x94\x6f\x81\ +\x9c\x1d\x3b\xf7\xd5\x50\xf7\xc9\x18\xf3\x4d\xad\xad\x8c\x7c\x29\ +\xbd\x5b\xbd\x0f\x3c\x01\x74\x6a\x59\xf9\xfc\xa1\x81\x40\x94\x77\ +\x20\xb4\x3d\x52\x03\xa5\x71\x9f\xe7\x39\xa5\xcc\xff\xeb\xaa\x3c\ +\x6f\x2d\x0b\x75\x6d\xec\x3c\xc2\x50\x12\x68\x6a\xeb\xf8\x1f\xe8\ +\x01\xd8\x89\xc6\xcb\x86\xe8\xb3\xf7\x80\x07\x44\xd8\x25\xd9\x9e\ +\xfd\x6b\x32\x33\x4b\x4b\x08\xff\xb7\xb0\x13\x97\x6b\xc9\xfe\xbc\ +\x97\x01\x0c\x98\xd7\x1a\x03\x88\x6d\xb3\xaa\x13\x0b\x36\x0b\xb3\ +\x1e\x76\x28\x87\x36\xe4\xb9\x22\x2c\x46\x39\xad\x69\xda\xac\x40\ +\x02\x9e\x60\xcd\xff\x6a\x47\x76\x57\xeb\x06\x94\x4d\x02\xc9\x4c\ +\xc7\x12\x91\xe4\xa9\xbe\x4b\x30\x73\x88\xbd\xff\x79\xc0\x89\x0e\ +\xc5\x03\x12\x6d\xd9\xd7\x4a\x7d\xd0\x17\xfe\x1b\x6a\x14\xfe\x3c\ +\x90\x71\x5d\x57\x87\x2a\x01\xc4\x9f\xf9\xc8\x3b\xbd\x84\x6f\xf4\ +\x0f\x06\x76\xb0\xc4\x50\xc4\x75\x05\x2d\xbe\x12\xea\x2f\xc2\xcf\ +\xca\x31\xff\x01\xe2\xb2\xbc\xf0\xf9\x6e\xc0\xaf\x6b\xbc\xbe\x2e\ +\x12\xf8\x5c\xa8\x3b\xd0\xf6\x8a\x26\x32\xd9\x87\x29\xb2\x23\xb6\ +\x6b\xcf\x5b\x83\x9c\x08\x16\x03\x17\x21\xce\x56\x82\xfc\x3a\x9e\ +\x99\x9b\xeb\x43\xf8\xbf\x1d\x81\xf0\x23\x70\x57\xac\x1f\xbb\xff\ +\xd4\x83\x00\x00\x7e\x19\xea\x06\xd8\x7e\x72\xf7\x36\x64\xba\x6c\ +\xcc\x42\x9d\xd3\x47\x4c\x9b\xeb\x85\x2c\xbc\x0d\x80\x1f\x95\x7b\ +\xb2\x03\x46\x7a\xbf\x0f\xf8\xf5\xcd\x11\x08\xe3\xda\xc0\xf4\x6d\ +\xc7\x9b\xd1\xa5\x5a\x8f\x25\xcf\xcb\x2e\x4d\x66\xb2\x97\x39\x2a\ +\x5b\x62\xa7\x4c\xbd\xc6\xe0\x09\x0c\x2b\x76\xfb\xf2\x7c\x90\x71\ +\x38\x9c\x94\x6c\x9b\xf5\x6e\x22\x33\xbb\x94\xc9\x3f\x62\x6b\x63\ +\xda\xb0\x35\x16\xb5\x16\x7d\xe5\x1d\xc8\x3c\x3b\x80\xda\x3f\x2a\ +\x02\x88\x43\x70\xda\x70\xa2\xad\xc3\xc3\x96\x37\x36\x1a\x49\x94\ +\x67\xfa\x7f\x3f\xd9\x3e\x6b\x41\xa9\x98\x0b\xb0\x7a\xb9\x27\xbc\ +\x6d\x9e\x13\x94\x47\xf0\x82\xff\x53\x2b\x46\x0d\x13\x5e\xbd\xeb\ +\xed\xd8\x39\x7d\x8d\x28\x8f\xb7\xcf\xfe\x30\x99\xc9\xfe\x0a\xd4\ +\x08\xfa\x35\x9f\x84\x3e\x1a\x00\x32\x50\x6c\x60\xfa\x5f\xc0\xd1\ +\x8e\x7a\x1b\x83\x4e\x4c\x66\x66\x77\x24\xa7\x66\x4b\x0a\xe2\x78\ +\x3b\xd8\xf3\x11\x8f\xaa\xf7\xfa\x97\xf7\x9d\x85\xbb\x86\xc9\xc0\ +\x6a\xff\x2e\xe1\x0d\xc3\x7f\x7c\xc1\x8d\x95\x41\x22\x5f\x84\xe0\ +\xc6\x91\x8e\x72\xbf\x27\xbc\x8c\x6d\x34\xd1\x40\xa8\x65\xcf\xa9\ +\x31\x2f\xf7\x48\x09\xed\x53\x91\xf6\x27\x24\xfd\xd7\x75\x5d\xdd\ +\x61\xbc\xf9\x52\xa7\x32\x43\xab\x8f\x25\x00\xf0\xb1\x47\xd3\xc7\ +\x39\x26\x01\x71\x63\xcc\xe9\xae\xeb\x96\x2c\x66\x49\x66\x3a\x96\ +\x01\x7f\xcb\x4d\x6a\xfe\x87\x53\x88\xad\xa6\x31\x6f\x6f\xb5\xca\ +\xe3\x0b\xd8\x22\xac\xae\x46\xb4\x51\x55\x57\x7a\xfe\xcf\xdb\xc0\ +\xe3\x02\xf7\x78\xaa\xf7\x0a\xbc\x9d\x6c\xef\x28\x5b\x29\x8d\xb7\ +\x19\x7e\x93\x80\xad\x23\xba\xae\x7c\x0c\xda\x1e\x9f\xe1\x0e\xb8\ +\x5b\x24\x25\x16\x5c\x02\x58\x44\x79\xcd\x1f\x72\xc0\x1a\xae\xeb\ +\x06\xee\x5b\xe7\xd2\xa9\x23\x81\x6b\x19\x84\x83\x11\x06\x09\x3e\ +\x00\xdd\x3c\x99\xe9\x78\xb7\xc4\xfb\x38\xdf\x37\xa3\xcb\xc5\x0c\ +\x60\x42\x58\x80\xc9\x18\x33\x05\x38\x23\x42\xcd\x3a\x6f\xb5\x98\ +\xee\xb0\xb0\x28\x73\x2b\x09\x6a\xe5\x26\x35\x0b\x9e\x13\x43\x75\ +\x14\x82\x41\x30\x3e\x19\x8c\xf6\x7f\xd6\xc6\x96\x6e\x87\xa5\x3e\ +\x77\x11\xdd\x42\x6c\x80\x73\x1e\x76\x9a\xd1\xeb\xc0\xab\x28\xd3\ +\x71\x12\x73\xd1\x7c\x31\x59\x61\xf3\x97\xf1\xc6\xac\xa4\x36\xd7\ +\x65\x62\xc4\x6b\xf7\xef\x2b\x39\x1c\xf6\xf8\x73\x03\x4f\x00\xf1\ +\x12\x01\x24\x3c\x2d\xdb\x46\x4b\x02\x67\xfa\x0f\xaa\x37\x0d\x8b\ +\xfc\xc5\x51\x9d\x84\xed\xbd\xdf\x40\x6f\xac\x21\xc8\xe1\xc0\xff\ +\x95\xf8\x4c\xa5\x99\x7f\x85\x3e\x04\xf1\x72\xe0\x38\xca\x6f\x50\ +\xda\x97\x22\x59\x7b\x61\x51\x5e\x07\x7e\xb7\xcd\x78\xd3\x76\xf4\ +\xe8\xe2\xdc\x9e\xfd\x04\x02\x17\x4e\x5b\x47\x57\x9a\xf2\xdb\xfe\ +\xcf\x3d\xb9\xd6\x31\x02\x31\x51\x51\xc7\x01\x81\x61\x8e\x6a\xe7\ +\xaa\x38\xde\x08\xfb\x2b\x2f\x06\x82\xa7\xce\x52\x47\x75\x09\x71\ +\x96\x6a\x51\x8b\x22\xa8\x27\xa8\x78\xe2\x25\xdb\x67\x57\x2d\x5c\ +\xc6\x98\xb8\xc0\x61\xbe\xd6\x1f\x1f\xf1\xbb\xce\x03\x6d\x83\x41\ +\xf8\x4b\x5a\x00\x97\x1e\xb4\xa5\xf3\xc4\x7c\xe7\x1f\x33\x17\xcb\ +\x41\xe5\x92\x79\x29\x2b\x20\x9f\x4e\x1d\xae\xf0\x07\xfa\xb7\x5d\ +\xf3\x50\xc2\x4c\xd0\x1d\x7c\x33\x39\x68\x51\xbe\x46\x65\xbd\x0a\ +\xb6\x70\x5d\xf7\xa5\x3e\x16\xfa\x99\x3e\x71\x47\xda\xd0\x64\x65\ +\x87\xce\xb5\x12\x5c\xf4\x51\xa7\x9e\xf1\x90\x3b\x33\x3f\x54\x5e\ +\x80\x31\x26\xce\xa7\x82\xbf\x55\x9d\x2c\xd6\xbf\x02\x5f\x77\xdd\ +\xc1\x41\x00\xa1\x37\x78\xe2\x1d\x2f\x78\x33\x17\x4b\x25\x89\x3c\ +\x49\x20\x7c\x4b\xb0\xe8\xdd\x8a\x4d\x0f\x6e\x20\x18\x1b\x89\xc4\ +\x4a\x75\xd9\xf9\x5e\x85\x26\x79\x39\xc9\x25\x67\x13\xd8\x00\xb4\ +\x36\x2c\xf2\x68\x9a\xd3\xc9\xc4\x0f\x91\xb7\xb6\x36\xe6\x2c\x63\ +\x4c\xaa\xaf\x40\xe1\x00\x0b\x7e\xc2\x18\x73\x38\x30\x1d\xdb\xe7\ +\x60\x7c\x9d\x84\xff\xe3\x0d\x55\xbf\x35\x58\x84\x9f\xbe\x6e\x32\ +\x06\x8f\x50\x41\x04\xdf\x81\x5d\xb7\x19\x6f\x02\x63\x06\xf1\xf3\ +\xe6\xe4\x81\x5f\x32\x74\x33\xc4\xea\x8d\xe1\x8a\xb7\x7f\x89\xe3\ +\x95\xec\xa4\x3c\xe3\xfb\xc0\x25\xe1\xba\xae\x26\x84\x2f\x89\xf5\ +\x9b\x23\xb7\x2e\x15\x46\x7a\xd6\xc2\x78\x15\xb8\xca\x18\xf3\x0b\ +\x5f\xd8\x64\x10\x08\x7d\xdc\x18\xb3\xaa\x31\x26\x83\x8d\x75\xfd\ +\x11\x30\x75\x8c\x53\xe5\x57\x72\xd8\xf7\xae\x99\x83\xcb\x22\x8a\ +\xf7\xa1\x46\xca\xdd\x09\xb0\xbe\x3e\xec\xea\x29\xe7\x02\xa7\x06\ +\x9f\x50\xa6\x23\x7a\x11\xd0\x4a\xfd\xfa\xe8\x0d\x5d\x28\x07\x12\ +\xde\x15\xb6\x2b\xd8\xd5\xe7\x73\x1b\x95\x90\x23\x1f\x98\x3e\xa3\ +\x2c\x2d\x33\x7d\x86\xfb\xb6\x31\xe6\x32\xe0\xac\x3a\xbe\x93\x38\ +\x70\xb4\x7f\xfd\xd3\xf8\xff\xf6\xce\x36\x38\xaa\xab\x8c\xe3\xbf\ +\x73\xef\xee\x26\x4d\x2d\x6d\xc5\x3a\xd2\x56\x31\x69\x75\xea\x14\ +\x2e\xe1\xa5\x82\xb4\xe0\xd4\x41\xab\xd2\x82\x1f\x18\xb4\x30\x75\ +\xaa\xf2\x89\x16\x68\x8b\xa0\xc1\x99\xfa\x05\x92\x30\xbc\x59\x6b\ +\xa7\x8e\x63\x1d\xa7\x05\x1d\x5b\xc0\x52\x3b\x75\xac\x74\x98\x56\ +\x1c\xe5\xb5\xe4\x40\xd1\x3a\x6d\x36\x20\x6d\x3a\x22\x32\x0d\x09\ +\x90\x64\xf7\x1e\x3f\x9c\x1b\xd8\x84\x6c\x92\x7d\xc9\xee\xdd\xdd\ +\xe7\x37\x93\x2f\xd9\x4d\xf6\xee\xbd\xe7\xff\x3f\xcf\x79\x7b\x1e\ +\x78\xd6\xf3\xbc\xe3\xc0\x93\x81\x11\xf5\x16\x42\xf0\xc1\xf7\x5b\ +\x10\xcc\x45\xf5\xd5\x83\x2c\x54\x19\xbb\x5d\x35\xae\xda\x17\xb6\ +\x26\xa7\x86\xb9\x69\x2e\x76\x79\x6f\x4e\x06\xff\x73\xc8\xb9\x80\ +\xee\x35\x75\xd7\x28\x63\xf6\x90\x41\xb5\x94\x0a\xe2\x08\x46\x4d\ +\x8f\x35\xb7\x5e\xb1\x13\x6d\xf1\x84\x09\xea\xa8\xe3\xb4\x32\x7c\ +\x16\xa0\x76\x60\xa2\xd6\xfa\x4c\x06\xe2\x50\xc0\x91\xa0\x07\x2c\ +\x9c\xdd\xd9\x61\x4a\x07\xf0\x33\x80\x2a\xd8\xda\x0d\x27\x52\xde\ +\x93\xc8\x34\x5c\x0e\x56\xaf\xfa\xa8\xe1\x72\xd9\xf5\x05\xd8\xb2\ +\xe0\x2e\x85\x5f\x8d\xea\xc0\xa6\x68\x4f\x94\x94\x01\x04\x37\xf4\ +\x71\xd2\xa4\x86\x1a\x82\x8d\x5a\xeb\x55\x69\x1d\xa2\xa1\xb6\x1e\ +\x7b\x6a\x6c\x8c\x68\xbe\x1f\xad\x0a\xea\xa3\x4d\xf1\x73\x69\x9e\ +\x45\x7c\x04\x06\x70\x40\x6b\x9d\x71\xf1\x0e\xcf\xf3\x3e\x11\x8c\ +\x81\x6f\x2c\xe2\xf7\x4f\xd0\x7f\xef\xc2\x36\xfa\x1b\xc2\x70\xd4\ +\x60\x37\x4b\xa5\xb6\xeb\x68\x91\x9f\xe9\x05\xe0\x6e\xad\xf5\xbe\ +\x30\x36\xb8\x91\x64\x75\xcd\x68\x18\x10\xf0\x88\xe7\x79\x7b\xb4\ +\xd6\xaf\x0c\x3a\x57\x60\x4c\x8b\xaf\xd4\x72\x6c\x9d\xbc\x88\xe8\ +\x3e\x75\x1a\xc5\xe4\x12\x86\x1b\xb2\x3c\xf1\xa7\xb5\xfe\x60\xea\ +\x24\x6f\x6a\xc2\x6e\x10\xfa\x78\x48\xda\xe3\x83\x65\xf0\x4c\x1f\ +\x09\xab\xf8\x19\x61\x28\xb4\x97\xcc\xd3\x15\x47\x80\x35\xe9\xce\ +\x08\x44\x9a\xdb\x0c\x8a\xe7\x82\x31\xa0\x24\x0d\xe9\x37\x8d\xa2\ +\xb2\xbe\x1f\x0a\x5a\xbe\x72\x43\x22\xeb\x54\x6c\x87\x5a\xf4\x07\ +\xc6\x86\xe3\x72\x82\x33\x3f\xbc\x00\x3c\x13\xf2\x1e\x67\xd8\x9e\ +\xa1\x17\x5b\xdf\x3c\x53\xee\x04\x1a\xd3\xbd\x18\x6b\x8c\xfb\x6e\ +\xc2\x6d\xc0\xae\x8b\x4a\x83\xb3\x9c\x45\x91\x6e\x1f\xc0\x4d\x0c\ +\x73\x0e\xc0\xc0\xf3\x1b\x5f\x3b\x9e\xeb\xbd\x5c\x8b\x9d\xb9\x97\ +\x67\x92\xbb\xf8\xef\x2f\x74\xa1\x8f\xd1\x88\x00\x00\xde\x20\xbb\ +\x03\x3d\xcb\x3d\xcf\x9b\x9b\xee\x45\x77\xc3\x3b\xdd\x28\xb5\x04\ +\x78\x4d\xda\x0b\x00\x47\xa2\x8d\xf1\x74\x13\x45\x37\x0f\x63\x00\ +\xed\xe4\xa1\xae\x5c\x30\xe9\x26\x26\x90\x1b\xcf\x97\x82\xf8\x47\ +\x6c\x00\x2e\xbc\xae\x60\x4f\x96\x63\xba\xb4\x43\x01\x1b\x09\xb4\ +\x76\x28\xa3\x16\x62\x4f\x68\x55\x32\x49\x85\x4a\x5b\x18\xe2\x6a\ +\x67\xd8\x39\x98\x76\xad\xf5\xff\xf2\x71\x21\x29\x26\xf0\xb8\x98\ +\x40\x56\xe2\x5f\x54\x0a\xe2\x1f\xb1\x01\xbc\xa9\xb5\x6f\xb2\x33\ +\x00\x80\x99\xc0\xee\x25\x77\x4e\x4c\x6b\x02\xd1\xe6\xd6\xb3\xca\ +\xa8\x05\xc0\x2b\x15\xdc\xe0\xfe\xe9\x0f\xb1\x2b\xef\xb6\x1a\xf3\ +\x54\x21\x2f\x26\x30\x81\x75\x62\x02\x19\xf1\xbb\x52\x12\x7f\x26\ +\x43\x00\xb0\x79\xde\x2e\x64\xf3\x21\x0a\xee\x8a\x3a\x43\x9f\x3c\ +\x8b\x36\xb7\x9e\x35\x8e\x5a\x88\xcd\xb1\x56\x69\x13\x83\x09\x14\ +\xab\xaa\x9a\x5a\xd3\x65\x01\x56\x87\x3a\xd5\xf5\x85\xbe\xa8\x14\ +\x13\x78\x08\x78\x4f\xf4\x3d\xac\xf8\x17\x97\x92\xf8\x33\x32\x00\ +\xad\x75\x77\xd0\x18\x32\xc6\x00\x7f\xfd\x50\xad\xf6\x3c\x6f\xde\ +\x50\xef\xab\x5a\xd7\xda\xe5\xb8\xc9\xef\x05\xbd\x4e\x25\x65\x14\ +\xde\xea\x2b\x76\x0f\xf1\xfa\x17\xb0\x47\x63\x29\x86\x09\x68\xad\ +\x9f\x06\xa6\x28\x38\x25\xdb\x37\xaf\xe0\x42\xc4\x16\x5e\x2d\x39\ +\xf1\x67\x1a\x01\x00\x6c\xcc\x36\x0a\x08\xe6\x03\x76\x78\x9e\x37\ +\x7f\xc8\x37\xad\x3d\xd9\xab\x8c\x69\xc4\x6e\xd5\x6c\xaf\x80\x06\ +\xb4\x17\x58\x56\xbd\x2e\xde\x9b\xae\xf7\xc7\xe6\x9f\x2b\xaa\xf6\ +\xb4\xd6\xff\xf9\xd6\x38\xbf\xfe\xc6\x18\x5b\x64\x48\x70\x89\xf3\ +\xc0\xac\xc3\x5a\xaf\x2b\x45\xf1\x67\x6c\x00\xb9\x44\x01\x29\x26\ +\xb0\x7d\x38\x13\x88\x36\xb7\x99\x58\x53\xfc\x65\xe5\x9b\x19\xc0\ +\x1f\xcb\x78\x48\xf0\x86\x42\xcd\x8f\x35\xc5\x3b\x87\xe9\xfd\x6b\ +\xc3\x70\xb1\x0d\x7f\x3a\x76\xe6\xbd\x1e\x56\x02\x4b\x81\x7f\x57\ +\xb8\xf8\x77\x00\xb3\xb4\xd6\x87\x4a\xf9\x4b\x64\xb3\x27\x3a\x97\ +\x28\x20\xd5\x04\xbe\x31\xdc\x1b\xa3\xeb\xdb\x4e\x3a\x7e\x72\x3e\ +\x36\x71\xc5\xe9\x32\x6a\x3c\x06\xd8\xa1\x7c\xee\x8b\x36\xb5\xa6\ +\x9d\xb9\x0f\x4b\xef\x3f\xc8\x90\xe0\xe7\xd8\xb3\x1c\x4f\x50\x84\ +\x9a\xf6\x45\xa6\x13\x9b\x25\xe8\x9b\x5a\xeb\xc3\xa5\xfe\x65\x9c\ +\x2c\x1a\x40\x37\x76\x89\x88\x1c\x4d\xe0\x85\x91\x98\x40\x64\xfd\ +\xc9\xde\x58\x53\xfc\x17\x0a\x35\x05\xbb\xab\xaa\xd4\xe7\x06\x2e\ +\x02\x6b\x8d\x32\x8b\xa3\xeb\xe3\x1d\xc3\x88\x7f\x43\x58\x7a\xff\ +\x41\xda\xc1\x69\xe0\x51\xec\xfe\x84\x4a\x30\x82\xa4\x82\x6d\x33\ +\xae\x73\xc6\x6a\xad\x1b\x4b\x35\xe4\x1f\x48\x56\x3d\x4b\xb0\xae\ +\x7f\x16\xb8\x2a\xc7\xcf\x4f\x00\x0b\x81\x97\x46\x72\x43\x7b\x1a\ +\x6a\x1d\x6c\x8d\xba\x1f\x63\x0b\x4f\x44\x4b\xe8\x5e\x1b\x6c\x5a\ +\xec\xa5\x4a\xb1\x27\xda\x38\x74\x7e\xba\x99\x93\xbc\x59\x9d\x86\ +\xd7\x33\x78\x46\x87\xb5\xd6\x45\x39\x61\xe9\x79\x9e\xba\xd9\x31\ +\x37\x9c\xf2\x55\x03\x76\xc5\x20\x5a\x4e\xc2\x07\x76\x02\x4d\x40\ +\x8b\xd6\xba\xac\x86\xa3\x59\x87\x96\x93\x3d\xef\xee\x24\xfc\x99\ +\xdc\x53\x7c\xf9\xc0\x3e\xe0\x4b\x5a\xeb\x11\x25\x0b\xe9\x69\xa8\ +\x73\xc1\xdc\x01\xac\x04\xee\x05\xaa\x43\x2e\xfc\x0e\x60\x8b\x52\ +\x66\x73\xb4\xb1\xed\xdc\x08\x04\x55\x8b\x2d\xab\x36\x36\x83\xcf\ +\x29\x9a\x01\x0c\x88\x5a\x3e\x86\x4d\xa9\xf5\x50\x10\xe9\xa9\x12\ +\x17\x7e\xa3\x0d\x78\x74\x59\xce\x43\x65\xfd\x70\xa6\x78\x9e\x93\ +\xb0\x1b\x77\xee\xc9\xd3\xb5\xfc\x0d\x98\x93\x2e\x8f\xc0\xa0\x46\ +\xb0\xa6\xce\x01\x53\x6b\x0c\x4b\x14\xdc\x1f\x84\xa3\x61\xc9\x39\ +\xe8\x63\xb3\xd4\xfe\xd2\x60\x9e\x32\x4a\xb5\x57\x37\xc6\xcd\x08\ +\xc5\x7f\x10\xf8\x68\x86\x9f\x57\x74\x03\x18\x60\x04\x11\xe0\xc1\ +\x2a\xc5\xed\xdd\x86\xa5\x25\x14\x15\x9c\x03\x36\x03\xbf\x07\x8e\ +\x96\xab\xf0\x73\x36\x80\xe0\x41\xcf\xc6\x46\x01\xb1\x3c\x5d\x4f\ +\x8f\x03\x8b\x5c\x78\xf1\x50\x06\x63\xac\xee\xd5\x75\xca\x75\xa9\ +\xf2\x31\x5f\xc4\x26\x7e\xb8\x07\x7b\xae\xdd\x2d\x82\xe8\x3b\xb1\ +\xdb\x9a\xb7\x2b\xc7\xdf\x95\xf4\x23\x5d\xd5\x43\x14\x98\x4c\x15\ +\xcd\xb8\x98\xaa\x6b\xef\x31\xfb\xb3\x10\x3f\x40\x83\xd6\xba\x39\ +\x6c\x0d\x6c\xd1\xcc\x7a\x75\xac\xd3\x1f\x1b\x44\x04\xd7\x02\x0f\ +\x87\x2c\x32\xe8\xcb\x41\xb0\x13\x9b\x14\x65\x53\x21\x32\x14\x95\ +\x85\x01\x00\xd4\x7b\xde\x0f\x7d\x3b\x3e\xca\xa7\x88\xf6\x4f\x19\ +\xe3\xcc\xf9\xf5\xde\x23\x5d\x99\xfe\x71\xf7\x9a\x5a\xe5\x2a\x15\ +\xf3\x7d\x33\x05\x5b\x70\x62\x26\x30\x1d\x5b\x4f\xcf\x21\x3f\xd9\ +\x60\x0c\x97\x8b\x4e\x9c\x02\x74\xd0\x6b\xef\x77\x8c\xf9\xbb\xef\ +\x38\x9d\xb1\xc6\xd6\x11\xf7\x1c\x41\x8f\xb9\x09\x58\x46\xf6\xf9\ +\x11\x3e\xa3\xb5\x7e\x27\xcc\x8d\x2d\x25\x32\xf8\x36\xf0\x49\x6c\ +\xbe\xff\x65\x29\x6f\x89\x16\x4a\xf0\x0e\xec\xf0\xe1\xed\x1a\x87\ +\x9d\xe7\x7d\xfe\x01\x24\xcb\xbd\xb7\x1f\x15\x03\x98\x3c\xc9\x8b\ +\x18\xc3\xcb\x7e\xfe\x86\x02\x97\xa2\x01\xec\x2c\x78\x93\xd6\xba\ +\x2b\xdb\x7f\xd2\xbd\xa6\xce\x71\xfc\x44\xd4\x28\x77\x3c\x8a\x89\ +\x18\x3e\x87\xcd\xaa\xf3\x29\xe0\xa6\xc0\x18\xae\x4b\x23\x3c\x13\ +\xf4\xe8\xa7\x83\x9f\x76\x20\x8e\x22\x8e\x51\xef\x82\x7f\x0c\x9c\ +\xd3\xe0\xfb\xb1\xa6\x36\x3f\x4b\x51\xd4\x05\x63\xe6\xef\xe6\xf0\ +\x3c\x4c\x60\x00\xef\x96\x52\xe3\x4b\x31\x04\xc6\xbb\x66\xcc\x89\ +\xa4\x7a\x38\xe5\x1e\x2c\x06\xc6\xe7\xe1\x63\xce\xbb\xf0\x93\x94\ +\x70\x72\x3b\xf0\x76\x0c\x92\x07\x2b\x50\xf0\x79\x37\x80\xe0\x41\ +\xce\x02\x76\xe7\x71\x28\x90\x77\x23\xe8\x67\x0a\x0d\x9f\x56\x0a\ +\xc7\x01\x5f\x81\x42\xf9\xc6\x75\x94\xa9\xee\x35\x6e\x44\xb9\x26\ +\x62\x8c\x32\x91\x24\x17\x8d\x49\x74\x9b\x88\xeb\xdb\x04\xb7\xc6\ +\xa0\x30\xb1\xc6\xb8\x9f\xc7\xc6\xff\x28\x76\x1b\x69\xae\xfb\xfc\ +\x0f\x03\xd3\xc2\x94\x6e\x3a\x0f\xf7\x27\x2f\xc3\x04\x05\xb4\x54\ +\x50\x48\x5f\x14\x03\x08\x1e\xd8\x6a\x6c\xc6\xd7\xd1\x22\xef\x46\ +\x50\xc4\x5e\xef\x16\x6c\x91\xc9\xef\xe4\xe9\x19\x84\x72\xfc\x2f\ +\x54\x96\x01\x44\x80\x17\x81\xb9\xa3\x7c\xcd\x3d\xd8\xdd\x88\x07\ +\x19\xe1\xfe\x81\x30\xf0\xd5\x69\x93\xd4\xfb\x3d\xe6\x96\x20\xdc\ +\x7f\x80\xfc\xe5\x42\x34\xc0\xad\x5a\xeb\x56\x69\xce\x42\xd1\x0c\ +\x20\xc5\x04\x5e\x02\xbe\x56\x80\x6b\xf7\x81\x37\x81\x3f\x10\x1c\ +\x55\x0e\x9b\x19\xa4\x8c\x71\x97\xc7\x14\x13\x7b\x0c\x8b\xc9\x7f\ +\x12\xd4\xfd\xc0\x8c\x72\x0a\xff\x85\x12\x35\x80\xa0\xd1\xdf\x15\ +\xcc\x07\x54\x15\xf0\x7b\xf4\x02\x47\x03\xf3\xd9\x54\xa5\xb8\x70\ +\xa0\xa5\x38\x66\x90\x92\x97\x7e\x05\x30\x01\x58\xc4\xe8\x2d\x7b\ +\x19\xe0\x36\xad\xf5\xbf\xa4\x29\x0b\xa1\x30\x80\x40\x04\xb3\x81\ +\x57\x0b\x6c\x02\x97\xcc\xc0\x55\xbc\xe5\x1b\x76\x19\x2b\x90\x4d\ +\x40\x37\xe0\xe7\x3b\x42\xe8\x13\xfb\xe4\x6b\x1c\x75\xe4\x9c\xbf\ +\xc2\xc0\xd5\xc0\x72\xe0\x23\x14\x66\xad\x5b\x7a\x7f\x21\x7c\x06\ +\x10\x02\x13\x18\x18\x1d\x00\x1c\x0f\xe6\x28\xfa\x7a\xce\xcd\x64\ +\x56\xa7\x50\x61\xab\xcc\xd4\xa4\xfc\x6e\x05\x56\xf4\x50\xf8\x9d\ +\x6e\xef\x03\xf5\xc1\xa1\x1c\x41\x08\x97\x01\x04\x26\x30\x0b\xbb\ +\x53\xb0\x2a\x84\xdf\x3d\x9b\xa5\xa1\xb0\x6c\x67\x35\xc0\x0f\xb4\ +\xd6\x1b\xa4\x09\x0b\xb9\x30\xaa\x35\xd2\xb4\xd6\x7f\x01\xbe\x1c\ +\x84\xe0\x61\x23\x9a\xc5\x4f\x28\x88\x28\x1a\xb0\x2b\x21\x82\x10\ +\xde\x08\x20\x25\x12\x28\xc6\xc4\x60\xb9\x72\x52\x41\x7d\x8b\xd6\ +\x67\xe5\x56\x08\xa1\x8e\x00\x52\x22\x81\xbd\xd8\x0a\xc3\x17\xe4\ +\x96\xe7\xc4\x09\x60\xaa\x88\x5f\x28\x29\x03\xe8\x33\x81\x5b\xaf\ +\x32\xd7\x03\xbb\xe4\xb6\x67\x2d\xfe\x69\x5a\xeb\xff\xca\xad\x10\ +\x4a\x6a\x08\x30\x60\x38\x10\xc1\x1e\xc8\xb8\x97\xf0\x9c\xdd\x17\ +\xf1\x0b\x62\x00\x05\x34\x01\x07\x9b\xed\xf6\x55\xfa\x2f\xab\x09\ +\x57\xd2\x16\x88\xff\x8c\xdc\x0a\xa1\x2c\x0c\xa0\x8f\x07\xa6\x4e\ +\xa8\x6a\xe9\x75\x7e\x0b\xcc\x93\x68\xe0\x0a\x4c\x20\xfe\x3b\x44\ +\xfc\x42\xc9\xcf\x01\x0c\xc6\x73\x87\x8e\x75\x47\x14\x0b\xaa\x1c\ +\x66\x63\x8b\x2c\x08\x81\xf8\x5d\xc5\xaa\xda\x6a\xf5\x59\x11\xbf\ +\x50\xb6\x11\xc0\x80\x61\x41\xcc\x55\xac\x4e\x1a\x7e\x44\xb8\x93\ +\x7c\x8e\xf6\x03\x89\x03\x8d\x06\x9e\x91\x2d\xbe\x42\xc5\x18\x40\ +\x8a\x11\x4c\xc7\x1e\x99\x9d\x5b\x61\xc3\x02\x03\x6c\x01\xd6\xe5\ +\xab\xcc\xb7\x20\x94\x9c\x01\x04\x26\xe0\x00\xd3\x02\x23\xf8\x3a\ +\xe5\x95\x67\x7e\x30\xe1\xc7\xb1\xe9\xa7\x7f\x25\xbd\xbe\x50\xf1\ +\x06\x30\xc0\x08\x22\xc0\x2a\x6c\x06\x9d\xea\x32\x8a\x0a\x0c\xd0\ +\x1a\x08\xff\x59\xad\x75\x42\x9a\xa3\x20\x06\x90\x86\x49\x9e\x17\ +\xad\x72\xa8\xbf\xe8\xd3\x80\xdd\x43\x50\xaa\x51\x41\x2f\x76\x5d\ +\xbf\x49\x84\x2f\x88\x01\x64\x1f\x15\x7c\x1f\x5b\xa0\xf2\xbe\x12\ +\x30\x83\x3e\xd1\x6f\x05\x9e\x04\x3a\x44\xf8\x82\x18\x40\xfe\xcc\ +\x60\x65\x60\x06\xf3\x82\x97\x8a\x6d\x08\x06\x9b\x7f\xbe\x0d\xd8\ +\x06\xfc\x54\xc1\xb9\x16\x11\xbd\x20\x06\x30\xaa\x66\xe0\x7e\xfe\ +\x5a\xc7\x39\xf0\xa1\xff\x98\xb1\x27\x0f\x57\x72\xf9\x04\xe2\x68\ +\x99\x42\x6a\x5e\x81\x27\x80\x2e\xec\xa4\xde\x6f\x00\x23\x3d\xbd\ +\x20\x06\x50\x3c\x53\x88\x02\x4c\x88\xf8\xea\xad\x84\xf3\x98\x09\ +\xf6\x17\x44\xe0\xf6\x84\x2d\x21\x96\x11\xe3\xa2\xf4\x74\xf9\x6c\ +\xe9\x48\x5e\xca\x24\x64\x52\x44\x8f\x96\xfc\xf3\x82\x20\x08\x82\ +\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\ +\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\ +\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\ +\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\ +\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\x82\x20\x08\ +\x82\x20\x08\x82\x20\x08\x65\xce\xff\x01\x75\xbc\x99\xa9\x24\xca\ +\x69\x37\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x28\x8a\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x02\x00\x00\x00\x02\x00\x08\x04\x00\x00\x00\x5e\x71\x1c\x71\ +\x00\x00\x02\xe6\x7a\x54\x58\x74\x52\x61\x77\x20\x70\x72\x6f\x66\ +\x69\x6c\x65\x20\x74\x79\x70\x65\x20\x65\x78\x69\x66\x00\x00\x78\ +\xda\xed\x97\x5b\xb2\xe3\x28\x0c\x86\xdf\x59\xc5\x2c\x01\x49\x08\ +\x89\xe5\x60\x2e\x55\xbd\x83\x59\xfe\xfc\x60\x3b\x27\xe7\xd2\xb7\ +\xea\x7e\x9a\x8a\x29\x83\x23\x40\x92\xff\x0f\x48\x12\xc6\xbf\xdf\ +\x66\xf8\x07\x17\x95\xcc\x21\xa9\x79\x2e\x39\x47\x5c\xa9\xa4\xc2\ +\x15\x0f\x1e\xcf\xab\xec\x9a\x62\xda\xf5\x7d\xf1\x65\x7d\x67\x0f\ +\x8f\x0e\x86\x49\xd0\xca\xd9\x61\xf5\x6c\xa9\xc2\xae\x6f\x13\xee\ +\x18\x74\xbc\xb7\x07\xbf\x7a\xd8\x2f\x47\x77\xe4\xcb\xa1\xac\xc8\ +\x2b\x54\x7f\x4e\x12\x76\x3e\xed\x94\x2e\x47\x65\x9c\x0f\xb9\xb8\ +\x3d\xa7\x7a\x9c\x79\xc6\x76\x0d\xdc\xa9\x5c\xf7\x71\x39\xdd\x19\ +\xc5\xf3\x73\x78\x36\x24\x83\x4a\x5d\x11\x48\x98\x87\x90\xc4\x5d\ +\xfb\x99\x81\x9c\x77\xc5\x2d\xa8\x59\x0c\xe3\x90\x31\x9e\x93\x50\ +\xd8\x1d\xf7\xbb\x42\x90\x77\xaf\xf7\x26\xf0\xb3\x40\x1f\xc5\xa7\ +\x53\x6c\xfa\xba\xe3\xa3\xf8\x5c\x2f\xbb\x7c\xd0\x32\xdf\xd4\xf2\ +\xd7\x1d\xa4\x5f\x8b\xbf\x25\x7e\x0a\x2c\x8f\x8c\xf8\x7d\x87\xc9\ +\xed\xea\xb3\xc8\x73\x76\x9f\x73\x9c\x6f\x57\x53\x86\xa2\xf9\x5a\ +\x51\x5b\x6c\xba\xdd\x60\xe0\x01\xc9\x65\x4f\xcb\x28\x86\x5b\xf1\ +\x6c\xbb\x14\x14\x8f\x35\x36\x20\xef\xb1\xc5\x03\xa5\x51\x21\x06\ +\x95\x19\x28\x51\xa7\x4a\x93\xc6\x6e\x1b\x35\xa4\x98\x78\x30\x98\ +\x30\x73\x63\xd9\x36\x07\xa3\xc2\x6d\x53\x4a\xab\xd0\x64\x93\x22\ +\x5d\x1c\xfc\x1a\x8f\x00\x94\x49\xf8\x91\x0b\xed\xb8\x65\xc7\x6b\ +\xe4\x88\xdc\x09\x43\x99\xe0\x8c\x30\xe5\xbb\x25\xfc\xa8\xf3\x77\ +\x4a\x98\xb3\x2d\x89\x28\xfa\x43\x2b\xe4\xc5\x6b\x5d\x23\x8d\x45\ +\x6e\xd5\x18\x05\x20\x34\x2f\x6e\xba\x05\xbe\xcb\x63\xd3\xc6\x27\ +\xb0\x02\x82\xba\x65\x76\xbc\x60\x8d\xc7\xe9\xe2\x50\x7a\x5b\x5b\ +\xb2\x39\x0b\xc6\x29\xda\x73\x0b\x51\xb0\x7e\x39\x80\x44\x88\xad\ +\x48\x86\x04\x04\x62\x26\x51\xca\x14\x8d\xd9\x88\xa0\xa3\x03\x50\ +\x45\xe6\x2c\x89\x0f\x10\x20\x55\xee\x48\x92\x93\x08\xce\x23\x63\ +\xe7\x15\x1b\x73\x8c\xf6\x58\x56\xce\xbc\xcc\x38\x9b\x00\x42\x25\ +\x8b\x81\x4d\x91\x0a\x58\x29\x29\xd6\x8f\x25\xc7\x1a\xaa\x2a\x9a\ +\x54\x35\xab\xa9\x07\x2d\x5a\xb3\xe4\x94\x35\xe7\x6c\x79\x1d\x72\ +\xd5\xc4\x92\xa9\x65\x33\x73\x2b\x56\x5d\x3c\xb9\x7a\x76\x73\xf7\ +\xe2\xb5\x70\x11\x9c\x81\x5a\x72\xb1\xe2\xa5\x94\x5a\x39\x54\x04\ +\xaa\xf0\x55\x31\xbe\xc2\x72\xf0\x21\x47\x3a\xf4\xc8\x87\x1d\x7e\ +\x94\xa3\x36\x2c\x9f\x96\x9a\xb6\xdc\xac\x79\x2b\xad\x76\xee\xd2\ +\x71\x4c\xf4\xdc\xad\x7b\x2f\xbd\x0e\x0a\x03\x27\xc5\x48\x43\x47\ +\x1e\x36\x7c\x94\x51\x27\xd6\xda\x94\x99\xa6\xce\x3c\x6d\xfa\x2c\ +\xb3\x3e\xa8\x5d\x54\x3f\x95\xdf\xa0\x46\x17\x35\xde\xa4\xd6\x38\ +\x7b\x50\x83\x35\x98\xdd\x2e\x68\x1d\x27\xba\x98\x81\x18\x27\x02\ +\x71\x5b\x04\xb0\xa0\x79\x31\x8b\x4e\x29\xf1\x22\xb7\x98\xc5\xc2\ +\xd8\x14\xca\x48\x52\x17\x9b\xd0\x69\x11\x03\xc2\x34\x88\x75\xd2\ +\x83\xdd\x1b\xb9\x5f\xe2\x16\xd4\x7f\x89\x1b\xff\x8c\x5c\x58\xe8\ +\xfe\x06\xb9\x00\x74\x9f\xb9\x7d\x41\xad\xaf\xef\xb9\xb6\x89\x9d\ +\xbb\x70\x69\x1a\x05\xbb\x0f\xfd\xc3\x6b\x60\xaf\xeb\x4b\xad\xfe\ +\x69\xfb\x72\xf4\x72\xf4\x72\xf4\x72\xf4\x72\xf4\x72\xf4\x72\xf4\ +\x3f\x70\x34\xf1\xe3\x01\x7f\x62\xc3\x7f\x34\x20\x9e\x2a\xa9\x0d\ +\xb7\xf9\x00\x00\x01\x23\x69\x43\x43\x50\x49\x43\x43\x20\x70\x72\ +\x6f\x66\x69\x6c\x65\x00\x00\x28\x91\x9d\x90\xbf\x4a\xc3\x50\x14\ +\xc6\x7f\x69\x8b\x16\xd1\x49\x71\xf0\x0f\x64\x70\x2d\xb8\xd8\xc9\ +\xa5\x2a\x04\x41\x21\xc6\x0a\x46\xa7\x34\x49\xb1\x98\xc4\x90\xa4\ +\x14\xdf\xc0\x37\xd1\x87\xe9\x20\x08\x3e\x81\x4f\xa0\xe0\xec\x77\ +\xa3\x83\x83\x59\xbc\x70\xf8\x7e\x1c\xce\xf9\xbe\x7b\x2f\xb4\xec\ +\x24\x4c\xcb\xce\x2e\xa4\x59\x55\x38\xde\xc0\xbf\xf4\xaf\xec\xc5\ +\x37\xda\x6c\xd2\x61\x9b\x6e\x10\x96\xf9\xc0\x75\x4f\x68\x3c\x9f\ +\xaf\x58\x46\x5f\x7a\xc6\xab\x79\xee\xcf\xb3\x10\xc5\x65\x28\x9d\ +\xab\xb2\x30\x2f\x2a\xb0\xf6\xc5\xfd\x59\x95\x1b\x56\xb1\x76\x3b\ +\xf4\x0e\xc5\x0f\x62\x3b\x4a\xb3\x48\xfc\x24\xde\x89\xd2\xc8\xb0\ +\xd9\xf5\xd2\x64\x1a\xfe\x78\x9a\xdb\x2c\xc7\xd9\xc5\xb9\xe9\xab\ +\xb6\x70\x38\xe6\x14\x17\x9b\x11\x53\x26\x24\x54\xf4\xa4\x99\x3a\ +\x47\xf4\xd9\x93\x3a\x14\x04\xdc\x53\x12\x4a\x13\x62\xf5\x66\x9a\ +\xa9\xb8\x11\x95\x72\x72\x38\x10\x0d\x45\xba\x4d\x43\xde\x46\x9d\ +\xe7\x2a\x65\x24\x8f\x89\xbc\x4c\xc2\x1d\xa9\x3c\x4d\x1e\xe6\x7f\ +\xbf\xd7\x3e\xce\xea\x4d\x6b\x7d\x9e\x07\x45\x50\xb7\xda\xaa\xd6\ +\x78\x0c\xef\x8f\xb0\xe2\xc3\xea\x33\x2c\x5d\x37\x64\x75\x7f\xbf\ +\xad\x61\xa6\x5f\xcf\xfc\xf3\x8d\x5f\xb4\xf4\x50\x4b\xa7\xc8\x6e\ +\xd4\x00\x00\x00\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\ +\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\ +\x95\x2b\x0e\x1b\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1b\ +\x04\x29\x07\x5c\x92\xfb\xee\x00\x00\x20\x00\x49\x44\x41\x54\x78\ +\xda\xed\x9d\x77\x98\x96\xd5\x99\xc6\xef\x19\xa6\x32\x0c\xd5\x81\ +\xa1\x68\x90\x22\x45\x10\x63\x01\x15\x41\xb1\xc5\x60\x8c\x97\xb1\ +\x60\xac\x89\x1a\x8d\xc1\x18\x93\xdd\x64\xd7\xb8\xbb\xd9\x75\xb3\ +\x31\x97\x3d\xab\x59\x8d\x25\x31\x36\x34\xb1\x22\x18\x13\x1b\x11\ +\xb1\x63\xa3\x8a\x74\x84\x19\x90\x36\xb4\xe9\xf3\xed\x1f\x80\x41\ +\x1c\x86\x99\xef\x39\xe7\xad\xbf\xdf\xfb\xcf\x5e\x59\xdf\x8f\x79\ +\xef\xf3\x9c\xfb\x3c\xa7\x4b\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x61\x3f\x5d\xa9\xc9\ +\x5a\xa6\x5a\x65\xbe\xf4\x34\x6a\xa3\xde\xd1\x4d\x1a\x85\x4c\x00\ +\xc9\xa3\xa7\x1e\xd6\xb6\x66\x2a\xfe\x97\x9f\xe9\x1a\x86\x5c\x00\ +\x49\xe2\x74\x6d\x6c\x55\xe5\xdf\xfe\xd4\xe9\x5c\x24\x03\x48\x0a\ +\x57\xab\xb1\x0d\xd5\x7f\x7b\x87\xe0\x02\x64\x03\x48\x02\x67\xb5\ +\xb1\xf2\x6f\x7f\xaa\x35\x08\xe9\x00\xe2\x4e\x7f\x6d\xcd\xca\x00\ +\x32\x7a\x03\xf1\x00\xe2\xce\xa4\x2c\xab\x7f\x46\x4d\x3a\x0e\xf9\ +\x00\xe2\x4c\x6f\x35\x64\x6d\x00\x19\xdd\x97\x04\x09\x72\x89\x02\ +\x48\x2d\x67\xa8\x9d\xe1\xed\xa3\x55\x80\x01\x00\xc4\x97\x71\xa6\ +\xb7\x0f\x50\x21\x06\x00\x10\x5f\x06\x18\xdf\xef\x82\x01\x00\xc4\ +\x97\x12\xe3\xfb\xed\x31\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\ +\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\ +\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\ +\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\ +\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\ +\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\ +\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\ +\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\ +\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\x00\ +\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\ +\x00\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\ +\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\ +\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\ +\x00\xc0\x00\x00\x00\x03\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\ +\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\ +\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\ +\x00\x00\x03\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\ +\x00\x00\x0c\x00\x00\x30\x00\x00\xc0\x00\x00\x00\x03\x00\x00\x0c\ +\x00\x00\x30\x00\x00\xf0\x44\x1e\x12\x40\xc4\x28\x56\x99\x72\xb4\ +\x41\x9b\x90\x42\x52\x57\x75\x54\xa3\x2a\x55\x8f\x14\x90\x74\x7a\ +\xeb\xbf\x35\x57\xd5\xaa\x55\x9d\x6a\xb4\x58\xb7\xa8\xbf\x72\x3c\ +\xfe\x7b\x8b\x95\x31\x3d\x83\xbd\x36\xcd\xc7\xeb\x11\xad\x56\x8d\ +\xea\x54\xab\x6d\x7a\x5f\xd7\xa8\x8c\x10\x81\xa4\xd2\x51\xff\xab\ +\x86\x66\x2a\xd9\x03\x1e\xc3\x3e\xba\x06\x70\x9a\x66\x35\xf3\xef\ +\xd5\xe8\x66\xb5\x27\x54\x20\x79\x0c\xd4\xd2\x3d\x56\xb3\x0a\x8d\ +\x48\x95\x01\xe4\xe9\x1e\x35\xed\xf1\xdf\xfc\x58\xfb\x13\x2e\x90\ +\x2c\x0e\xd4\x9a\x16\x2b\x5a\x95\x8e\x4c\x8d\x01\xe4\xe9\xf1\xbd\ +\xfc\xab\xab\xbc\x76\x3d\x00\x02\x66\xa8\xd6\xef\xb5\xaa\x6d\xd1\ +\xc8\x54\x18\x40\x81\x26\xb7\xe2\xdf\x5d\xab\x41\x84\x0d\x24\xa5\ +\xfa\xaf\x6e\x55\x65\xab\xd2\x11\x89\x37\x80\x3c\x3d\xd9\xca\x7f\ +\xb9\x82\x2c\x00\x92\xc0\x10\xad\x6b\x75\x75\xdb\xac\x43\x12\x6d\ +\x00\x05\x9a\xd2\x86\x7f\xfb\x33\x1d\x40\xf8\x40\xbc\x19\xac\xca\ +\x36\x55\xb8\x8d\x3a\x3c\xb1\x06\x90\xaf\x67\xda\xf8\xaf\x93\x05\ +\x40\xac\x39\x40\x9f\xb5\xb9\xca\x55\xe9\xab\x89\x34\x80\x42\x3d\ +\x9f\xc5\xbf\x4f\x16\x00\xb1\x65\x90\x56\x65\x55\xe9\x36\xe8\xd0\ +\xc4\x19\x40\x81\x9e\xcd\xf2\x2f\xa8\x60\x38\x10\xe2\x48\x2f\x55\ +\x64\x5d\xed\x36\x68\x78\xa2\x0c\xa0\x48\x7f\x35\xfc\x0d\x15\x2a\ +\x27\x9c\x20\x5e\xe4\xeb\x55\x53\xc5\x5b\xa7\x83\x13\x63\x00\x85\ +\x9a\x6a\xfc\x2b\xa6\xb1\xa3\x07\xe2\xc5\xf9\xc6\x90\xcf\x68\x83\ +\x86\x26\xc2\x00\x8a\xf4\x92\x59\x8b\x8c\xce\x25\xa4\x20\x4e\x2c\ +\x76\x10\xf4\x6b\x1d\x74\x04\xc2\x36\x80\xa2\xac\x86\xfe\xbe\xfc\ +\x7c\x42\x48\x41\x7c\x18\xed\x24\xe8\x33\x5a\x6b\xae\x80\xe1\x1a\ +\x40\xb1\x5e\x76\xa4\x44\x26\xfb\x45\x52\x1c\x08\x02\x41\x73\xa2\ +\xa3\xdf\xe9\xa6\xe9\x1a\x16\x5b\x15\x8a\xf4\x8c\xc6\x39\xfb\xb5\ +\x93\x30\x00\x88\x0b\x43\x9d\xfd\xd2\x3e\x9a\x16\xd3\x99\xf0\xf6\ +\xfa\xab\x33\x23\x34\x69\xca\xf8\xa1\x4f\x72\x55\xa8\x02\x15\x28\ +\x5f\x9d\x34\x40\xbd\xd5\x5d\x3d\xd4\x43\xe5\xea\xac\x0e\xca\x51\ +\x67\xe5\xa8\x83\xa4\x2d\xca\x68\xa3\x32\xda\xa2\x0d\xaa\xd4\x1a\ +\x55\xea\x33\xad\xd4\x42\x55\xa9\x5e\x75\xaa\x53\xad\x9a\x12\xa5\ +\x4a\x17\x87\xbf\xd5\x4d\xaf\xe9\x18\xcd\x8b\x5d\xf5\x9f\xac\xb1\ +\x4e\x7f\xb1\x2b\x06\x10\x25\x8a\xb5\xbf\x06\x68\x7f\x0d\xd2\x7e\ +\x2a\x57\xb9\x7a\xb5\x78\xae\x4d\xa9\xa4\x8e\xcd\xfc\xef\x4d\xaa\ +\x50\xa5\x2a\xb4\x5c\x4b\xb4\x42\x0b\xb5\x40\x9b\x13\xa1\x8e\xdb\ +\xc3\xad\xca\x34\x4d\x47\x69\x51\x8c\xbe\xbf\x44\xcf\xeb\x68\xc7\ +\xbf\xd9\x80\x01\x44\x83\x51\xfa\x86\x4e\x56\x6f\x15\xa9\x48\x45\ +\xc6\xe3\xac\x72\xd5\x5b\xbd\x77\xfc\xdf\x75\xaa\x56\xb5\xd6\xe9\ +\x15\x4d\xd1\x8b\x6a\x8c\xb5\x46\x2b\x1d\xff\x5e\x77\xcd\xd0\x31\ +\xfa\x38\x36\xd5\x7f\xb2\xf3\xea\xef\x5e\x53\x68\x03\x05\xea\xaa\ +\x83\xf5\x0b\xbd\xed\x6c\x4c\x77\x6f\xcf\x5b\xfa\x0f\x8d\x50\x99\ +\x8a\x62\xa9\xd7\xe5\x1e\x14\xa9\x54\xff\x2c\xfe\x92\xe0\x67\x01\ +\x4a\xf4\x9a\x97\x88\xb8\x9c\x6a\x18\x06\xed\x34\x5a\x57\xeb\x21\ +\x2d\x0a\xac\xea\x7f\xf1\x64\x98\x27\xf4\x63\x8d\x8e\xdd\x40\x6e\ +\x4f\x2f\x6a\xac\xd4\xc0\xc8\x1b\x40\x07\xfd\xdd\x53\x2c\xb0\x1c\ +\x38\x84\x30\xbe\x4e\x4b\xb4\xa9\x85\xd3\xdb\x82\x78\x9a\xb4\x49\ +\xcb\x74\x47\xcc\xce\x89\x7b\xc2\x93\x21\xf6\x8d\xb4\x01\x74\xd0\ +\x9b\x9e\xa2\xe0\x29\xaa\x63\x90\x09\x7f\x2f\x5d\xa8\x77\x42\xad\ +\xf6\xcd\x3d\x0b\x75\x85\xf6\x8d\x49\xa7\x60\xb8\x6a\xbd\x68\xf0\ +\xa9\x06\x44\xd6\x00\x4a\x35\xdd\x53\xc9\xd7\xe9\x20\xaa\x65\x30\ +\x94\xea\x5c\x3d\xa2\x8d\x91\xab\xfc\x3b\x9f\x5a\x3d\xa5\xef\x66\ +\x3f\x25\x14\x20\xff\xed\x49\x81\x95\xda\x37\x92\x06\x50\xaa\xb7\ +\xbc\x95\xfa\x2f\xa9\x98\x41\xd0\x47\xf7\x6a\x8d\xa7\x96\xcb\xe5\ +\x53\xaf\xb5\xba\xe7\xf3\xd9\x83\xe8\xf2\x80\xa7\xef\x5f\xde\x86\ +\xee\x50\x50\x06\xd0\xd1\xd3\xd0\x5f\x46\x19\x4d\xa2\x6a\xfa\xa6\ +\x50\xfd\xf4\xbb\xc8\x57\xfc\xdd\x9f\x47\x35\x30\xd2\x1d\x82\x7c\ +\x3d\xe6\xcd\x02\xfa\x44\xca\x00\x3a\xea\x5d\x6f\xa5\xfc\x84\x0a\ +\xa8\xa0\x7e\xc3\xf4\xbc\x36\x9f\xd5\x16\x9d\xe7\x39\x9d\x1b\xe1\ +\x95\x1e\xb9\x9a\xe4\xe9\xbb\x97\xb5\x32\x0b\x08\xc2\x00\x3a\xea\ +\x75\x6f\xe5\xfb\x27\xb5\xa3\x8a\xfa\xe4\x74\x2d\x8c\x41\xd2\xdf\ +\xf2\xa8\xc0\x22\x7d\x3b\x85\x59\xc0\x32\xf5\x8c\x84\x01\x74\xd4\ +\x4c\x5a\xff\x78\xa6\xfd\x87\x68\x5a\xac\xab\xfe\xae\xcf\x7b\x1a\ +\xa9\xc2\x94\x65\x01\x4b\xb4\x5f\xe8\x06\xd0\xd9\xdb\xc4\x1f\xad\ +\xbf\x57\x4e\xd4\x73\x89\xa9\xfc\x3b\x9f\xa9\xfa\x66\xca\xb2\x80\ +\xc5\x7b\x5d\x1e\xe3\xd7\x00\x3a\xeb\x03\x5a\xff\xf8\xd1\x43\x7f\ +\xd3\xd6\xc4\x55\xff\x8c\x32\xaa\xd6\x0b\xad\x1e\x1e\x4b\x46\x16\ +\xb0\x78\x2f\x93\x82\x3e\x0d\xa0\xb3\xc7\xb5\x22\x7f\xa6\xf5\xf7\ +\x43\xa9\x2e\x53\x5d\x22\x2b\xff\xce\xa7\x41\x57\xaa\x34\x45\x59\ +\xc0\x22\x75\x0f\xc5\x00\xba\xe8\x43\x5a\xff\xf8\x0d\xf9\x7d\x98\ +\xe8\xca\xbf\xf3\xf9\x40\x67\xa6\x28\x0b\xf8\xa4\x85\x35\x11\xbe\ +\x0c\xa0\xab\xc7\x89\x3f\x5a\x7f\x4f\x83\x7e\x93\x55\x9f\x8a\xea\ +\xbf\x7d\xb1\xd0\x5f\xd4\x3e\x35\x59\xc0\x02\x95\x05\x6a\x00\x5d\ +\x35\xdb\x5b\xc9\x3d\x4e\xeb\xef\x23\xf4\x4e\xd1\x96\xd4\x54\xfe\ +\x7f\x8c\x08\x9c\x19\xb1\x35\x02\xfe\xb2\x80\x05\xea\x15\x98\x01\ +\x74\xd3\xfb\xb4\xfe\x71\xa2\xa7\xb7\x45\xa9\xd1\x7f\x1e\x50\xbf\ +\x94\x64\x01\xf3\x9b\xdd\x21\xe1\xde\x00\xba\x69\x0e\xad\x7f\x9c\ +\x18\xaf\x15\xa9\xad\xfe\xdb\xb7\xd0\x9e\x96\x92\x2c\x60\x5e\x33\ +\x93\x82\xae\x0d\xa0\x9b\xc7\x89\x3f\x5a\x7f\xe7\x14\xeb\x67\xa9\ +\xae\xfc\x3b\x9f\x9f\x47\x6a\xcf\x80\xbf\x2c\x60\xee\x97\xb2\x00\ +\xb7\x06\x50\xa6\xf9\xb4\xfe\xf1\xe1\x80\x04\xad\xf4\xb3\x3e\x2f\ +\x6b\x48\x2a\xb2\x80\xb9\xbb\x4d\x0a\xba\x34\x80\xee\x9a\x45\xeb\ +\x1f\x1f\x8e\xd1\x5a\x2a\xfe\x2e\xcf\x5a\x8d\x49\x45\x16\x30\x4b\ +\x9d\xbc\x18\x40\x77\x7d\x4c\xeb\x1f\x17\x72\xf5\x03\xaa\x7c\x33\ +\xcf\x35\xc6\x53\x8c\xe3\x91\x05\xcc\xda\x65\x52\xd0\x95\x01\x74\ +\xf7\x38\xf1\x47\xeb\xef\x98\xa2\x14\x8f\xfa\xef\x7d\x56\xa0\x7d\ +\x0a\xb2\x80\x8f\x3e\xcf\x02\xdc\x18\x40\x0f\x2d\xa0\xf5\x8f\x0b\ +\x85\x1e\xc7\x69\x93\xf0\xcc\x56\x49\x84\xb2\x80\x47\x3d\x7d\xe5\ +\x87\xea\xe6\xcc\x00\xca\x35\x8f\xd6\x3f\x2e\x0c\xd0\x4a\x2a\xf9\ +\x5e\x9e\x4a\x0d\x8a\x50\x16\xe0\xcb\x02\xde\x57\xa9\x13\x03\xe8\ +\xe9\xf1\x48\x78\x5a\x7f\x87\xe4\x48\x3a\x31\xe5\x73\xfe\xad\x7d\ +\x56\xea\x84\x1d\x8a\x25\x39\x0b\x78\x4f\x25\x66\x03\x38\xce\xe3\ +\xc4\x1f\xad\xbf\x63\xce\x4a\xf8\x3e\x3f\x97\x4f\xad\xce\x4b\xc1\ +\x58\xc0\x0c\x55\x18\x7f\x61\x63\xfc\x5b\xff\x9c\x94\x54\xff\x73\ +\xf4\x70\x68\x37\xe8\xd4\xab\x46\x35\xaa\xd5\x3a\xcd\xd3\xc7\x5a\ +\xae\x4a\xad\x56\x85\xaa\xb5\x61\x97\xff\xa6\x8b\x8a\xd5\x53\xe5\ +\x2a\xd7\xbe\x1a\xac\xc1\xea\xa6\x42\x15\xa9\x48\xf9\x21\xfd\xcd\ +\x8d\x3a\x4f\x8f\x45\x26\x7b\x9b\xa4\x09\xa9\x6a\xac\x1e\xd7\x39\ +\x41\xdd\xff\x98\x0e\x03\xb8\x54\xb7\x87\xb0\xda\xad\x41\x73\x34\ +\x5b\x0b\xb5\x48\x8b\xb5\x58\x15\x6d\x7c\xbb\xa7\xfa\xa9\xbf\xfa\ +\x69\xa0\x86\xe9\xc0\x10\xd2\xc1\x1a\x4d\xd4\xef\x23\x93\x05\x3c\ +\x98\x22\x0b\x78\x42\xe7\xaa\x8e\x94\xdd\x1d\xff\xae\xc6\x80\x53\ +\xe8\x0d\xba\x53\x5f\x53\x6f\x75\x76\x90\xc8\x15\xa8\xb3\xfa\xe8\ +\x64\xdd\xa5\xaa\x80\xbf\xa2\x51\xd7\x46\x68\x0c\x67\x52\x4a\xba\ +\x5f\xf4\xfd\x9d\xb7\xfe\xc1\x55\xff\xcd\x5a\xa2\x1b\x35\xdc\xdb\ +\xb7\x8c\xd0\x4d\x5a\x1c\xa0\x11\x34\xea\x92\x14\xcc\x08\x44\xe9\ +\x61\xe4\xdf\x71\xf7\xe6\x1c\x6d\x0b\xa4\xe0\x1a\xf4\x82\xae\x0d\ +\x68\x39\xed\x21\xfa\x57\xbd\x18\x90\xad\xd5\xe8\xec\xc8\x74\x14\ +\x73\x12\x6f\x01\xb4\xfe\x8e\xf9\x76\x20\xd5\xa4\x51\xb7\x68\x40\ +\xc0\x0b\x68\x4a\xd4\x4f\xbf\x08\xe4\xce\x82\x06\x9d\x45\x16\x40\ +\xeb\x1f\x47\x4e\xf2\x3e\xf1\x57\xa3\x45\x9a\x18\xea\x37\x5e\xa9\ +\x45\xaa\xf6\x3e\x29\x78\x7c\x84\xb2\xba\x47\x13\x5b\xfd\x69\xfd\ +\x9d\x32\x48\x9f\x7a\x4f\xd8\xce\x8e\x40\xa1\xe5\xe9\x74\x3d\xee\ +\x7d\x69\xd0\x40\xb2\x00\x5a\xff\x38\x51\xa0\x55\x5e\x0b\xec\x79\ +\x0d\x0e\x6d\x8e\xbe\xb9\xaf\x1d\xe8\xf9\x06\xc3\x55\x2a\x26\x0b\ +\xa0\xf5\x8f\x0b\xc5\x1e\x0f\xf8\xae\xd5\x2c\x1d\x1b\xc9\xaf\x3e\ +\x5c\xef\x7b\xec\xf4\xcc\x8e\x90\x05\x24\x2b\x0b\xf8\x33\xad\xbf\ +\xeb\x16\xe2\x41\x6f\x85\xf5\x9e\xce\x8f\xf4\xb7\x5f\xa0\xb7\x3d\ +\x6e\x16\xce\x8d\x50\x19\x3f\x4a\xeb\x0f\xcd\xf3\x23\x6f\x85\x75\ +\x79\xe4\x4e\xd3\x6f\x2e\xfb\xb9\xc2\xdb\xf7\x4f\x8c\xd0\x77\xe6\ +\x27\x62\x69\x10\xad\xbf\x73\xc6\x78\x9a\x0c\x9b\xd1\xe2\x25\x53\ +\xd1\xa2\x8b\x5e\x54\x83\x17\x1d\xa2\xd4\xf9\x89\x7f\x16\x40\xeb\ +\xef\x9c\x21\x5e\xce\xfa\x5b\xac\x4b\x63\xd7\x0d\xfa\x9e\x96\x78\ +\x50\x62\xdd\x5e\xaf\xc4\x26\x0b\xa0\xf5\x0f\x31\xfd\xf5\x71\xd2\ +\xef\xd3\xea\x19\x4b\x35\x7a\x69\xb2\x07\x35\x5e\x8a\xd4\x21\xe2\ +\xf1\xcd\x02\x68\xfd\x3d\xf0\x2f\xce\x8b\x69\xab\xae\x8f\xb5\x22\ +\xd7\x79\x58\x0a\x7d\x4d\xa4\xbe\x30\x9e\x59\x00\xad\xbf\x07\x4e\ +\x71\x5e\x4c\xb3\x34\x3a\x01\x63\x22\xee\xa7\x44\x4f\x8e\x58\x77\ +\x27\x6e\x59\xc0\x13\xb4\xfe\xee\xe9\xeb\xfc\xbc\xbf\x97\xd5\x25\ +\x11\xca\x74\xd2\xdf\x1c\x2b\xf3\xa9\xf6\x25\x0b\x60\xd5\x5f\xb4\ +\x42\xc0\xf5\xdc\xff\x4d\x89\xd2\xe7\x56\xc7\xea\xfc\x31\x62\x37\ +\x0b\xc7\x27\x0b\xa0\xf5\xf7\xc2\x69\x8e\x27\xfd\x2e\x4b\x9c\x42\ +\x97\x3b\x5e\x25\x78\x6a\xe4\x9a\x80\x49\xb4\xfe\xe9\x1d\xfd\x77\ +\x3b\xd4\x75\x54\x22\x55\x1a\xe9\x74\x73\xf4\x56\x15\x46\xec\xfb\ +\xa2\x9f\x05\xd0\xfa\x7b\x62\x8a\xc3\x42\x5a\x13\xa1\x9d\x6f\xae\ +\x19\xa4\x35\x0e\x95\x9a\x1c\xc1\x8e\xe0\x24\x5a\xff\xf4\x71\xa6\ +\xea\x1d\x0e\x6f\x1d\x9a\x68\xad\x0e\xd6\x52\x67\x5a\xd5\xeb\xf4\ +\xc8\x7d\x5f\x74\xb3\x00\x5a\x7f\x4f\x94\xea\x23\x67\x85\x54\xa9\ +\xf2\xc4\xeb\x55\xae\xd5\xce\xf4\x7a\x5f\x1d\xc8\x02\x98\xf7\x0f\ +\x17\x77\x5b\x5f\x56\xc6\x74\xc5\x5f\x5b\xe9\xe1\xf0\x86\xa4\x89\ +\x11\xfc\xbe\x1c\x6f\x57\x89\xd0\xfa\x47\x8e\x5e\xce\xd2\xff\x35\ +\x3a\x2c\x35\xaa\x1d\xaa\xcf\x9c\xcd\x97\x44\x31\x67\x8a\x56\x16\ +\x40\xeb\xef\x91\x17\x1d\x15\x52\x53\x82\x87\xfe\x9a\x63\xb0\xb3\ +\xf0\xfe\x6b\x24\xbf\x2f\x3a\x59\x00\xad\xbf\x47\x4e\x71\x74\x20\ +\x66\xbd\x8e\x48\x9d\x76\x47\x39\xda\x30\xbc\x2d\x62\x0b\x83\xa3\ +\x95\x05\xd0\xfa\x7b\xa4\x50\xcf\x39\x3b\xe8\x23\x8d\x5c\xae\x26\ +\x27\xea\x4d\x89\xdc\x8a\x80\xa8\x64\x01\xb4\xfe\x5e\x19\xc9\xa2\ +\x5f\x23\xb7\x39\x52\x30\xaa\xa3\x27\xe1\x66\x01\xb4\xfe\x9e\x79\ +\xd7\x49\x31\xbd\x90\x6a\x0d\x5f\x72\xa2\xe1\x1b\x91\xfd\xbe\xf0\ +\xb2\x00\x5a\x7f\xcf\x9c\xe9\xa4\x98\xe6\xa8\x6b\xaa\x55\xec\xa2\ +\xd9\x4e\x74\x3c\x3d\xb2\x5f\x98\xaf\x47\x68\xfd\x93\x47\xbe\x16\ +\x39\x59\xcf\x3e\x3a\xf5\x4a\x8e\x75\xb2\x8f\xe2\x93\x88\xed\x0e\ +\xdc\x95\xdc\xc0\x57\x07\xd2\xfa\x7b\xe7\x7c\x27\xf7\xe2\xfd\x0a\ +\x21\x25\xfd\xd2\xc9\x6d\x09\xdf\x8e\x74\x73\xf1\x08\xad\x7f\x92\ +\x28\x74\x72\xda\xdd\xb3\x08\xb9\x03\x17\xb7\x0a\x3d\x13\xd1\xb9\ +\x80\x9d\x59\xc0\x63\xb4\xfe\xc9\x61\xa0\x93\x93\x7e\x7b\x21\xe4\ +\x0e\x7a\x39\xd9\x22\xd4\x3f\xe2\x9d\xc6\x47\x68\xfd\x93\xc2\x43\ +\x0e\x96\xb0\x7e\x0f\x19\x77\xe1\x12\x07\xcb\x82\xfe\x18\xf1\x6f\ +\xf4\x9f\x05\x3c\x49\xeb\x1f\x04\x7d\x1c\x14\xd5\x34\xe5\x20\xe4\ +\x2e\xe4\x68\xba\x03\x55\xa3\x9e\x53\xf9\xcd\x02\x68\xfd\x03\xe2\ +\x3e\x07\x85\x55\x86\x8c\xbb\x51\xe6\x40\xd5\xbb\x23\xff\x95\xfe\ +\xb2\x00\x5a\xff\x80\xe8\xe4\x60\x27\xdb\x65\xc8\xd8\x0c\x13\xcd\ +\xba\x7e\xa6\x4e\x91\xff\x4a\x3f\x59\x00\xad\x7f\x60\x5c\x68\x3e\ +\xdc\xf2\xbd\x18\x5c\xf1\x19\x06\xc5\x9a\x69\x54\xb6\x2e\xe2\x77\ +\x27\xfb\xca\x02\x68\xfd\x03\xa3\xd0\x5c\x78\xb5\x3a\x0f\x19\xf7\ +\xc0\x05\x66\x73\x9d\x14\xe9\xc9\x40\x3f\x59\x00\xad\x7f\x80\xf4\ +\xd6\x66\x63\x71\x7d\x80\x88\x2d\x60\x3d\x5e\xad\x2a\x26\x93\xab\ +\xee\xb2\x00\x5a\xff\x40\xb9\x94\x23\xbf\xbd\x62\xbf\x5c\xfd\x3b\ +\x31\xf9\x52\x37\x59\x00\xad\x7f\xc0\xcc\x31\xaf\x57\x83\x96\x99\ +\x9a\x9a\x0c\xcb\x9e\x05\xd0\xfa\x07\x4c\x5f\xb3\x63\x0f\x42\xc4\ +\xbd\x60\x5f\x65\xb9\x6f\x6c\xbe\xd5\x96\x05\xd0\xfa\x07\xce\x6d\ +\x24\x6c\xde\x29\xd0\x13\x29\x3a\x60\x25\xfb\x2c\x80\xd6\x3f\x70\ +\x72\x8c\x2b\xd6\xab\x75\x26\x22\xb6\x82\xb3\x55\x63\xd2\x79\xa9\ +\x72\x63\xf4\xb5\xf9\x7a\x98\xa6\x24\x1e\x1c\xad\x4d\xc6\xed\x3f\ +\x79\x88\xd8\x0a\xda\x19\xcf\x5a\xa8\xd2\x91\xb1\xfa\xde\xb6\x67\ +\x01\xb4\xfe\xa1\xf0\x63\xe3\x21\x96\x3f\x40\xc2\x56\xf2\x43\xe3\ +\x11\xeb\x57\xc7\xec\x7b\xdb\x96\x05\xd0\xfa\x87\x42\xa1\xf1\x5c\ +\x97\x7a\x24\x6c\x03\xb6\xbd\x81\x8f\xc4\xae\x8a\xb4\x3e\x0b\xa0\ +\xf5\x0f\x89\x6e\x5a\x62\x0a\xca\xeb\x91\xb0\x0d\xdc\x68\xd2\x7a\ +\x61\x0c\x4f\x5a\x6c\x5d\x16\xf0\x27\x5a\xff\xb0\x38\xd8\xb8\xff\ +\xbf\x3f\x12\xb6\x81\x01\x6a\x34\xe9\x7d\x50\x0c\xbf\x79\xef\x67\ +\x07\x72\xda\x4f\x88\xfc\x87\xf1\xf0\xef\x12\x24\x6c\x03\x25\xc6\ +\x03\xc3\xaf\x8d\xe5\x57\xb7\xd3\x0d\x2d\x7c\xd3\xad\x0c\x22\x87\ +\xc9\xdb\x29\x0c\xc8\x30\xf9\xb9\x49\xef\xd7\x63\xfb\xdd\x27\xe9\ +\x83\x66\xbe\xe7\x23\x8d\x27\x24\xc2\xc5\x12\x8e\x9b\x34\x0a\x01\ +\xdb\xc8\x51\xc6\x49\xd7\xf8\x92\xa7\xb1\xba\x4d\xef\x6a\x83\x1a\ +\xb5\x51\xef\xe9\x7f\x75\xac\xf2\x09\x88\x70\x39\xd6\x14\x8c\x8b\ +\x10\x30\x0b\x6c\x83\xae\x63\x10\x30\x1e\x03\x1f\xf1\xe0\x14\xd3\ +\xdb\x4f\x52\xd0\x59\xf0\xb4\xe9\x6d\x52\x66\x70\xc8\xfb\xa6\xd6\ +\x68\x38\x02\x66\xc1\x57\x8d\xe7\x2e\x01\x38\xa2\x54\x15\x86\x50\ +\xdc\x80\x80\x59\x62\x19\x05\xa8\x60\xde\x85\x2e\x80\x2b\xfa\xa9\ +\xc8\xf0\xf6\x24\x8a\x39\x4b\x1e\x33\xbc\x5b\xa4\x7e\x08\x88\x01\ +\x44\xc1\x00\x38\x04\x24\x5b\x9e\xc2\x00\x30\x80\xb8\x1b\x40\x83\ +\x66\x53\xcc\x59\xf2\x91\x9a\x30\x00\x0c\x20\xfc\xbf\xd1\xb2\x8c\ +\x77\x8e\xb6\x52\xcc\x59\xb2\xc5\x64\x9e\xfd\x63\x75\x2e\x00\x06\ +\x10\x59\x0a\xb5\x9f\xc9\x00\xb6\x51\xcc\x59\xb2\x4d\x73\x0c\x6f\ +\xef\x17\x8b\x23\xc2\x31\x80\xc8\x53\xa0\x72\xc3\xdb\x0b\x54\x47\ +\x31\x67\x49\x9d\x16\x18\xde\x2e\x67\xe7\x1c\x06\xe0\xc6\x00\x7a\ +\x66\xfd\x6e\x3d\xab\x00\x4d\x2c\x31\x9c\xa3\x50\xce\x02\x5a\x0c\ +\xc0\x05\xf9\x06\x03\xa8\xd1\x62\x0a\xd9\xc0\x62\xd5\x64\xfd\x6e\ +\x2f\x0c\x00\x03\x70\x41\x67\xc3\x55\xde\x18\x40\x78\x06\xd0\x2e\ +\x06\x57\x85\x62\x00\x31\xf8\x1b\x0f\x30\xbc\x5b\xab\x4a\x0a\xd9\ +\xc0\x4a\xd3\x08\xca\x01\x08\x88\x01\xd8\xb1\x5c\x34\xb1\x8e\x22\ +\x36\x62\x59\x48\xdd\x07\xf9\x30\x00\x3b\x3d\x0d\xef\xce\xa3\x88\ +\x8d\xcc\x0b\xa9\xe4\x00\x03\xd8\x81\x65\x12\x70\x3e\x45\x6c\x64\ +\x7e\x48\x25\x07\x18\x80\x83\x30\x5a\x41\x11\x1b\x59\x81\x01\x60\ +\x00\xf1\xed\x02\x30\x04\x68\xa5\x82\x2e\x00\x06\x10\x2e\xdd\x0c\ +\xef\xae\xa6\x88\x8d\x58\x2c\xb4\x0c\xf9\x30\x00\x3b\xc5\x21\xb5\ +\x5f\x60\x35\x80\x22\xe4\xc3\x00\xec\x58\xb6\x94\x6c\xa6\x88\x8d\ +\x6c\xc2\x00\x30\x80\xf8\x66\x00\xd5\x14\xb1\x91\x1a\x0c\x20\xc9\ +\xe4\xc4\xe0\x6f\xcc\x24\xfc\xfb\xd0\x1f\xc8\x00\x00\x00\x03\x00\ +\x00\x0c\x00\x52\xdf\x79\x00\x0c\x60\x07\x96\x81\x3c\xee\x73\xb5\ +\x62\x99\x83\xe1\x30\x36\x0c\xc0\x01\x96\x30\x2a\xa6\x88\x31\x00\ +\x48\xaf\x01\x94\x52\xc4\x46\x3a\x61\x00\x18\x40\x7c\xbb\x00\xac\ +\x46\xb7\x52\x8e\x01\x60\x00\xe1\xb2\x35\xa4\xf0\x05\xab\x85\x62\ +\x00\x18\x80\x03\xd6\x63\x00\x21\xd2\xc3\xf0\x2e\xe7\x31\x61\x00\ +\x0e\x58\x6a\x78\x97\x43\xa9\xac\x58\x2e\x65\x59\x86\x7c\x18\x80\ +\x1d\xcb\x91\x14\x43\x28\x62\x23\x83\x0c\xef\x2e\x47\x3e\x0c\xc0\ +\x8e\x25\x8c\x06\x53\xc4\x46\x86\x60\x00\x18\x40\xb8\x58\x12\xc9\ +\x6e\x14\xb1\x89\x1c\x75\xa1\x0b\x80\x01\xc4\xb7\x0b\x50\xc8\x44\ +\xa0\x89\x5e\xa6\xfb\xfd\x56\x22\x20\x06\x60\xc7\x32\x99\xc4\x2d\ +\xf5\x36\xfa\x99\xf6\xf4\x73\x1a\x03\x06\xe0\x80\x7a\xc3\xc9\x7e\ +\x18\x80\x8d\xfe\x06\x03\xa8\xe0\x5e\x66\x0c\xc0\x05\x35\x86\xc1\ +\xa4\x7c\xf5\xa7\x90\x0d\xec\x6f\xb8\xe0\x73\xb9\x6a\x11\x10\x03\ +\xb0\x53\x6b\x1a\x4c\x1a\xc8\x2d\xf5\x59\x53\xa0\x81\x86\xb7\x31\ +\x00\x0c\x20\x02\x06\x30\x4c\xed\x29\xe6\x2c\x69\xaf\x61\x18\x00\ +\x06\x10\x3e\x2b\x0c\x47\x4b\x1c\xa8\x0e\x14\x73\x96\x74\x30\x18\ +\x40\x86\x49\x40\x0c\xc0\x15\x96\xb6\xa4\x9d\xa9\x15\x4b\x37\x23\ +\x0c\x87\x7a\xd6\xb0\x0c\x08\x03\x70\xc5\x32\x53\x32\x79\x3a\xc5\ +\x1c\x82\x72\xb5\x64\x00\xe0\x2e\x15\x5d\xa5\x4c\xd6\xcf\x46\x04\ +\xcc\x92\x2d\x06\xd5\x57\x31\xf6\x42\x06\xe0\x2e\x10\x97\x1a\xde\ +\xee\xa4\x11\x14\x74\x16\x1c\xa2\x12\xc3\xdb\x8b\x39\x0d\x00\x03\ +\x70\xc7\xdf\x4c\x6f\x5f\x40\x41\x67\xc1\x85\xa6\xb7\x9f\x47\x40\ +\x70\xc7\x11\x86\x64\x34\xa3\xc5\x08\x98\x05\x4b\x4c\x9a\x8f\x44\ +\x40\x70\x89\x25\x18\x37\xe9\x48\x04\x6c\x23\xa3\xb5\xd9\xa4\x39\ +\xd0\x05\x70\xca\x9b\x86\x77\x4b\x75\x1c\x45\xdd\x46\x8e\x33\xad\ +\x9f\x98\x81\x80\x18\x80\x5b\x9e\x33\xbd\x3d\xce\x34\xa0\x95\x3e\ +\x4a\x34\xce\xf4\xfe\x5f\x90\x10\xdc\x32\xdc\x94\x90\x36\x6a\x00\ +\x12\xb6\x81\x01\x6a\x32\xe9\xcd\xe2\x2b\x32\x00\xc7\xac\xd4\x42\ +\xd3\x77\x7e\x8f\xc2\x6e\x03\xdf\x37\x5d\xec\xbd\x40\xab\x90\x10\ +\xdc\x52\xa0\x87\x4c\x6d\x52\x03\x12\xb6\x81\x46\x93\xd6\x0f\x18\ +\x36\x11\x03\x19\x40\xb3\xd4\xe9\x2d\x35\x19\xde\x6f\xa7\x89\x14\ +\x77\x2b\xb9\xca\x14\x17\x4d\x7a\x4b\xf5\x88\x08\xae\x19\xa9\x2a\ +\xe3\x6a\x00\xda\xa5\xd6\x90\xa7\xc5\x26\x9d\x37\xea\x50\x44\x04\ +\xf7\xe4\x68\x99\x29\x30\x6b\x74\x16\x22\xb6\x82\x73\x54\x63\xd2\ +\x79\x29\x12\xd2\x05\xf0\x41\x46\x93\x4d\xef\x17\x6a\x02\xa7\x03\ +\xb5\x62\xac\x65\x82\xe9\x4a\x70\xe9\x49\x44\x04\x3f\xec\x6b\x6a\ +\x99\x32\xca\x98\xee\xb9\x49\x07\x07\x98\x35\xee\x85\x88\xe0\x8b\ +\x77\x8d\xc1\xf9\x2c\x12\xee\x85\xe7\x8d\x0a\xbf\x81\x84\xe0\x8f\ +\x09\xe6\xf6\x69\x0c\x22\xb6\xc0\x31\x66\x7d\xcf\x40\x44\xf0\x47\ +\xb9\xd6\x1a\x03\xf4\x23\x44\x6c\x81\x59\x46\x75\xd7\x98\xae\x13\ +\x87\xc0\xc9\x8d\xd9\xdf\xbb\x41\x2f\x19\x7f\x61\x10\xa7\x03\xec\ +\x91\xef\x98\xc7\x48\x5e\xd4\x06\x64\x04\x9f\x7c\x57\xf5\xc6\x56\ +\xea\x7d\x36\x06\x35\x4b\x89\x3e\x34\x2a\x5b\xa7\x8b\x90\x11\xfc\ +\xd2\x51\x6b\xcc\xfd\xd4\x2b\x90\xb1\x19\x7e\x68\xd6\x75\x8d\x4a\ +\x91\x11\x7c\x73\xab\x39\x50\x33\xea\x8e\x8c\xbb\x51\xe6\x40\xd5\ +\x1b\x91\x11\xfc\xd3\xd9\x41\xa8\xfe\x3d\x76\xa3\x1f\x7e\xc9\xd1\ +\x6b\x0e\x54\xed\x88\x90\x10\x04\x77\x9b\x43\xb5\x41\x97\x21\xe3\ +\x2e\x5c\xa6\x06\xb3\xa6\x77\x22\x23\x04\xc3\x00\x07\xad\xd5\x12\ +\x56\xac\x7d\x4e\x6f\xe3\x2e\x8b\xed\x0f\xf7\x30\x43\x40\x14\x69\ +\x8a\x83\x80\x65\x55\xe0\x4e\xa6\x3a\x51\xb3\x08\x21\x21\x28\xce\ +\x36\xee\x58\xdb\xfe\x5c\x8f\x90\x92\xfe\xc7\x81\x92\x35\x3a\x13\ +\x21\x21\x48\xe6\x39\x08\xdb\x6d\x1a\x9b\x7a\x1d\xc7\xa9\xda\x81\ +\x92\x73\x08\x48\x08\x96\xaf\x3b\x08\xdb\x8c\xe6\xaa\x5b\xaa\x55\ +\xec\xaa\x39\x4e\x74\x3c\x99\x80\x84\xa0\x99\xe1\x24\x74\x5f\x4d\ +\xb5\x86\xaf\x38\xd1\x70\x3a\xc1\x08\xc1\x73\x98\x93\xe0\xcd\xe8\ +\xd6\xd4\x2a\x78\x87\x23\x05\x39\x02\x0c\x42\xa0\x50\xcf\x3a\x0a\ +\xe0\x74\x2e\x0d\xfe\x81\x23\xf5\xa6\x18\x4f\x10\x02\xc8\x92\x13\ +\xb5\xd5\x49\x08\x37\xea\x98\xd4\x69\x77\xb4\x83\xa5\x3f\x19\x65\ +\xb4\x55\x27\x10\x88\x10\x16\x53\x1d\xb5\x62\x69\x3b\x2c\x6c\x88\ +\x33\xdd\x58\x4d\x01\x21\xb2\x8f\x93\x49\xac\x8c\x32\x5a\xaf\xc3\ +\x53\xa3\xda\xa1\xe6\x63\x55\x76\x3e\xb5\x29\x9f\x45\x81\xd0\xb9\ +\xc8\x59\x5b\xb6\x32\x25\x8b\x83\x7b\x68\x85\x33\xcd\x2e\x26\x00\ +\x21\x5c\x3a\xe8\x1d\x67\xe1\x5c\xa9\x9e\x89\xd7\xab\xa7\x56\x3b\ +\xd3\xeb\x6d\xd3\x15\xe2\x00\x4e\x38\xc3\x7c\x46\xd0\x3f\x9e\x4f\ +\x75\x58\xa2\xb5\x3a\x44\x2b\x9d\x69\x55\xaf\xd3\x09\x3e\x88\x02\ +\x4f\x39\x0b\xea\x8c\x3e\xd3\xe0\xc4\xea\x34\xd4\x59\xdf\x3f\xa3\ +\x8c\x9e\x20\xf0\x20\x1a\xe4\x68\x83\xc3\xc0\xce\xe8\xe8\x44\xaa\ +\x34\xd6\xa9\x46\x1b\xd5\x8e\xc0\x83\xa8\x70\x82\x9a\x1c\x06\x77\ +\x43\x02\x97\x06\x4d\x34\x5e\xf9\xbd\xfb\x73\x1c\x41\x07\xd1\xa1\ +\x9d\xee\x71\x1a\xde\x19\xdd\x96\x28\x7d\xee\x70\xac\xce\xdd\xb4\ +\xff\x10\x2d\xfa\xea\x53\xc7\x41\xfe\x4a\x42\xe6\xb8\xbb\xe9\x55\ +\xc7\xca\xac\xd4\x57\x08\x38\x88\x1a\x5f\x77\x1c\xe6\x19\xcd\x4f\ +\xc0\x12\xe1\x71\x9a\xeb\x5c\x97\xf1\x04\x1b\x44\x91\x2b\x9c\x87\ +\xfa\x36\xdd\x10\x6b\x45\xfe\xc7\xd9\x4a\xc9\x7f\x3c\x57\x12\x68\ +\x10\x4d\x0a\x35\xd9\x79\xb8\x67\x34\x55\xbd\x63\xa9\x46\x2f\x3d\ +\xed\x41\x8d\xa7\x55\x40\xa0\x41\x54\xe9\xaf\x4a\x0f\x41\xbf\x54\ +\xdf\x8f\xd9\xa0\x57\x8e\x2e\x73\x72\xd2\xef\xee\xcf\x6a\xf5\x23\ +\xc8\x20\xca\x8c\xf6\x10\xf6\x19\x35\x6a\x46\x8c\xee\xbd\xed\xa1\ +\x69\x8e\x36\xfb\xee\xfe\x70\x86\x22\x44\x9e\x1f\x79\x09\xfd\xed\ +\xbd\xdf\xe8\xaf\x7d\x2f\x71\x70\xc7\xdf\x9e\x9e\x89\x04\x17\xc4\ +\x81\xbb\xbc\x55\x81\xd9\xba\x30\xd2\x5f\x7e\x91\x3e\xf0\xf6\xed\ +\xff\x47\x60\x41\x3c\xc8\xd3\x5b\xde\xaa\x41\x9d\x66\x6b\x5c\x24\ +\xbf\x7a\xa4\x3e\x50\x9d\xb7\xef\x7e\x57\xf9\x04\x16\xc4\xc7\x02\ +\x56\x78\xab\x0a\x19\x65\x34\x4d\x43\x22\x34\x1a\x5e\xa8\x03\xf4\ +\x8c\xd7\xef\x5d\xc1\xca\x3f\x88\x17\x03\x1c\x6e\x7c\x6d\xfe\x79\ +\x52\x67\x2b\x2f\xf4\xef\xcc\xd7\x04\xa7\xbb\x21\x9b\xdf\x24\xcd\ +\xbd\x7f\x10\x3b\xc6\xaa\xd6\x73\xc5\xa8\xd1\x62\xfd\x30\xd4\x6f\ +\xbc\x4a\x4b\x9c\x5c\x92\xd6\xf2\x57\x8e\x21\x98\x20\x8e\x9c\xe4\ +\x69\x32\xec\x8b\x4f\x93\x6e\xd1\x40\x95\x04\xfa\x65\x25\xea\xaf\ +\x5f\x78\xec\xf1\xef\x7a\xec\xc7\x37\x08\x24\x88\x2b\xa7\x69\x73\ +\x00\x95\x24\xa3\x46\xbd\xa8\x7f\x0b\xa8\xa5\x3c\x44\xd7\xe8\x65\ +\xa7\xdb\x9f\x5b\x3a\xf4\xfb\x54\x82\x08\xe2\xcc\xf9\x81\x64\x01\ +\xdb\x9f\xcd\x5a\xa2\x5b\x74\xb0\xc7\xaa\x7f\x9b\x96\x68\x63\x60\ +\xdf\xd3\xa0\x73\x09\x20\x88\x3b\x3f\x75\x7c\x14\xc6\xde\x9f\x4d\ +\xba\x47\xe3\xd5\x47\x9d\x1d\xcc\x14\x14\xa8\xb3\xfa\xe8\x14\xdd\ +\x17\x50\x2e\xb3\x6b\x56\xf3\x13\x82\x27\xe9\xe4\xa4\xe2\x2b\x2f\ +\xd6\x1d\x2a\x0e\xfc\x5f\x6d\xd4\x5c\xcd\xd6\x27\x5a\xa4\x25\x5a\ +\xa4\x55\x6d\x2c\x97\x5e\xea\xa7\x7e\xea\xaf\x01\x1a\xa6\x03\x95\ +\x1b\xf8\x5f\xbf\x4d\x3f\xd0\x1f\xa9\x20\x18\x40\x52\xc6\x02\x9e\ +\x08\x6d\x26\xbb\x5e\x35\xaa\x51\x9d\xd6\x6b\xbe\xe6\x6b\xb9\x2a\ +\xb5\x5a\x15\xda\xa6\x2a\x35\xee\xf8\x2f\xda\xa9\x93\xda\xab\xa7\ +\x7a\xa8\x5c\xfb\x69\xb0\x86\xa8\x8b\x0a\x54\xa4\xa2\xd0\x16\xdf\ +\x34\xea\x5b\x9a\x4c\xf5\x80\xe4\x70\xbc\xb6\x05\x9c\x42\xc7\xf7\ +\xa9\xd6\xd7\x09\x18\x48\x1a\x23\xbd\x6c\x90\x4d\xde\xb3\x42\x47\ +\x11\x2c\x74\x01\x92\xc8\x20\x4d\x53\x39\x85\xde\x22\x15\x1a\xa7\ +\x8f\x91\x01\x03\x48\x26\x05\x9a\x91\xf0\xbb\x7f\x6c\x7c\xa0\xd1\ +\xda\x86\x0c\xe9\x21\x37\x65\xdf\x5b\xa7\x23\x74\x07\xc5\xbe\x07\ +\xee\xd2\x48\xaa\x3f\x24\x9f\x0b\xe9\xe9\x37\xf3\x5c\x9a\xba\x7c\ +\x10\x52\xcb\x31\x4e\x6f\xc9\x8b\xff\xb3\x96\xc3\xbe\x20\x5d\x0c\ +\xd0\x93\x54\xfc\x1d\xcf\xf3\x1a\x44\x40\x40\xda\x28\xd0\x65\x54\ +\x7e\x65\x74\xa5\x8a\x08\x06\x48\x27\xc7\xa6\x7c\x6d\xc0\x32\x9d\ +\x44\x10\xa4\x99\xb4\x1f\xf4\xb4\x42\x4d\x3a\x39\xc5\xdf\x7f\x83\ +\x1e\x53\x03\xd5\x00\xd2\xc8\x70\xdd\xa9\xca\x94\x2f\x10\xae\xd5\ +\x6a\x3d\xa2\xd1\x04\x03\xa4\x87\x3c\x75\xd3\x45\xfa\x84\xde\xff\ +\x2e\xcf\x02\x5d\xa8\x32\x2e\xfd\x4a\x1f\xe9\x5b\x09\x38\x5e\xa7\ +\xea\xb4\x84\x5c\xfc\xed\x96\x2a\x4d\xd1\x54\x3d\xa7\x2a\xa4\x80\ +\x24\x52\xa8\x5f\x69\xb9\x87\xdb\x72\x93\xd5\x25\xf8\x54\xb7\x30\ +\x2b\x00\xc9\xa2\x48\x03\x75\x0b\xd5\xbb\x0d\xcf\x7d\x1a\x84\x0d\ +\xd0\x05\x48\x46\xe5\x3f\x53\x13\x34\x3e\x75\xbb\x1e\xac\x34\xea\ +\x39\xfd\x59\x4f\x69\x0b\x52\x40\x7c\x99\xa8\x25\x81\x1c\x9e\x9d\ +\xcc\xa7\x5e\xcb\x75\x2d\x41\x04\x71\xa4\x9b\x2e\x0d\xfc\x18\xcd\ +\x64\x3e\x9b\x74\xa9\xca\x08\x28\xba\x00\xf1\xa1\xa7\x2e\xd6\xf9\ +\x1a\x4c\xf1\x3a\x63\x99\xfe\xa0\xfb\xb5\x0c\x21\x20\xea\xe4\xe9\ +\x46\x76\xfa\x79\x79\xd6\xeb\x6e\x15\x12\x60\x10\x5d\x3a\xeb\x12\ +\xef\xf7\x01\xa6\xfb\x69\xd4\x55\xac\xa1\xa0\x0b\x10\x45\xda\xeb\ +\x62\x5d\xe2\xf1\x56\x1e\xd8\xc9\xc7\xba\x4b\xf7\x6b\x23\x42\x40\ +\x74\x38\x5f\x4b\x03\xbf\xff\x27\xbd\x4f\x93\x96\xe9\x3c\x82\x0e\ +\xa2\x40\xae\x0e\xd4\x0c\x2a\x65\x08\xcf\x3b\x1a\x91\xfa\xdd\xa4\ +\x10\x32\x23\x74\x97\x6a\xa8\x8c\xa1\x65\x02\xf7\xea\x20\x82\x10\ +\xc2\xe2\x3a\xad\xa7\x1a\x86\x3e\x37\x70\x33\x81\xc8\x20\x60\xd0\ +\xb4\xd3\x08\x3d\xa6\x01\x14\x60\x24\x58\xae\xb3\x35\x93\x83\x45\ +\xe2\xd9\x83\x8e\x23\x83\x74\x8f\x66\x52\xfd\x23\xc3\x7e\x7a\x53\ +\xf7\x73\xb0\x68\x3c\x5b\xd2\xf8\xf1\xcf\x7a\x58\xa3\x28\xba\x88\ +\x71\x90\x2e\x52\xae\xa6\x23\x04\x5d\x00\x9f\xec\xaf\xfb\x34\x2e\ +\x06\x7f\x67\x83\xea\x54\xa7\x7a\xd5\xa9\x41\xab\x55\xa9\x4a\x55\ +\xaa\x42\x15\x5a\xad\x6d\xda\xa2\x26\x6d\x56\x46\xeb\x77\xf9\xef\ +\xbb\x2a\x47\xa5\xca\x55\x07\xb5\x57\x0f\xf5\x52\xb9\xca\x55\xae\ +\x9e\xea\xae\x3c\x15\x28\x5f\x05\x2a\x50\x5e\x0c\xbe\xfb\x15\x5d\ +\xac\xa5\x54\x2b\x0c\xc0\x07\x25\xba\x5a\x3f\x53\xc7\x08\xff\x85\ +\x2b\xb4\x54\x15\xaa\x54\xc5\x8e\x2a\x5f\xa9\xd5\xca\x98\x4b\xa8\ +\xc7\x0e\x33\xd8\x6e\x0a\xbd\xd4\x57\x7d\x22\xac\x41\x95\x6e\xd0\ +\x6d\x5c\x30\x86\x01\xb8\xa6\xaf\x9e\x89\xe8\x94\x53\xa3\xa6\xeb\ +\x5d\xbd\xa6\x99\xaa\x51\xad\xea\x54\x67\xae\xf4\x7b\x2e\xad\x02\ +\x15\xa8\x50\x85\xea\xaf\x31\x3a\x4c\xc7\xa8\x4b\x24\x15\xf9\x50\ +\xa7\xb1\x71\x08\xdc\x91\x17\xb9\x2b\x3c\xb6\x6a\xad\x66\xeb\x36\ +\x8d\x57\x69\xa8\xca\x74\xd4\x78\xfd\x46\xb3\xb5\x46\x9b\x22\xb6\ +\x12\xf2\x12\xe5\x13\xb8\x64\x00\x2e\x18\xaa\x1b\x74\x4a\x44\xfe\ +\x96\x1a\x7d\xa4\x59\x9a\xa7\xd9\x9a\x1f\xa9\x36\xae\xbb\x06\xe9\ +\x60\x1d\xac\x61\x1a\x11\x99\x1d\x7b\xcf\xeb\x27\x9a\x47\x15\x03\ +\x1b\x97\x68\x4b\x44\xda\xb4\x67\x75\x86\xca\xd5\x31\xc2\x83\x71\ +\x79\xea\xa8\x72\x9d\xa8\x07\x23\xb2\x27\x72\xb3\xfe\x89\x00\x86\ +\xec\xe9\xa2\x3b\x42\xdf\xfe\x5a\xa5\x15\x7a\x48\x27\xc6\x4e\xbb\ +\x31\xba\x57\xcb\x54\xa5\xa6\x90\x15\xfc\x5d\x44\xc7\x29\x20\xf2\ +\x8c\xd5\xdb\xa1\x86\x6e\xb5\xfe\xa6\xab\x75\x78\xac\x3b\x78\x87\ +\xeb\x6a\x3d\xad\x4d\xa1\xea\xf8\xa6\x8e\x23\x98\xa1\xad\x5c\xad\ +\xaa\x10\x83\xf6\x7d\x5d\xa2\xee\x6a\x9f\x08\x25\x8b\xd4\x5d\xdf\ +\xd2\xf4\x50\xbb\x02\x3f\x25\xa0\xa1\xf5\xb4\xd7\xcd\x21\x85\xea\ +\x16\x7d\xa2\xeb\xb5\x5f\x22\x55\xed\xac\x7f\xd5\xfc\xd0\x46\x54\ +\xee\x50\x31\x81\x0d\xad\xe1\xa0\x90\x52\xff\xa5\xba\x35\x16\xab\ +\x0c\x6d\x1c\xa7\x9b\x34\x3f\xa4\xae\x00\xe7\x35\xc1\x5e\x39\x35\ +\x94\xd4\x7f\x9a\x4e\x52\xe7\xd4\xdc\x94\xd8\x51\x5f\xd3\xdf\x43\ +\x50\xb9\x2a\x32\xd3\xb9\x10\xd1\x41\xab\x9f\x04\x7e\xa4\xc5\x3a\ +\x3d\xa4\x21\xa9\x54\xbb\x9f\xee\xd5\x5a\x35\x04\xac\xf8\x35\xa9\ +\xbb\x90\x16\x5a\x49\x89\x1e\x08\x38\x18\x3f\xd0\x4f\xd5\x2b\xd5\ +\x9a\x97\xe9\x2a\xbd\x11\xb0\xea\x8f\x84\xbc\x7a\x12\x22\x49\xd7\ +\x80\x7b\xa6\x33\x35\x46\x9d\x90\x5d\x52\xa9\x46\xe9\xd5\x40\xb5\ +\xff\x44\xdd\x91\x1d\x76\x65\x64\x80\xd7\x78\x6d\xd5\x0c\x8d\x45\ +\xf2\xdd\x38\x46\x6f\x68\x5b\x80\xa3\x01\x87\x21\x39\xec\x64\x42\ +\x60\x67\xfb\x35\xea\x41\x8d\x41\xf0\x3d\x30\x56\x77\xab\x3e\xa0\ +\x92\x58\xa7\x73\x11\x1c\x24\xe9\xba\xc0\x82\xee\x59\x0d\x52\x01\ +\x82\xb7\x40\x9e\x06\xe8\xa9\xc0\xee\x1e\xbe\x1e\xc1\xd3\x4e\x81\ +\x6e\x08\x24\xd8\x6a\x35\x53\x47\x20\x77\x2b\x39\x52\x33\x03\x3a\ +\x6c\xfd\xd7\x18\x72\xba\xdb\x9b\x7b\x02\x09\xb3\xa9\x3a\x0d\xb1\ +\xdb\xc8\xc9\x9a\x1a\x48\xd9\xdc\xcb\xe5\x22\x69\xa5\x38\x90\xf5\ +\xe9\x95\x3a\x81\x45\xa8\x59\x96\xcf\x58\x2d\x09\xa0\x84\x5e\x57\ +\x09\x62\xa7\x8f\x7d\xf4\x42\x00\x63\xcd\xbf\xe3\x5c\x1a\x13\xed\ +\x74\x7b\x00\x7b\x09\x5f\xd6\x3e\x48\x9d\x2e\xf6\xd5\x9b\xde\xc3\ +\xea\xf7\x3a\x10\xa1\x1d\x30\x5c\xf7\x7a\x2f\xab\x37\xd4\x17\xa1\ +\xd3\xc3\xfe\x5a\xe8\x39\xa0\x3e\xd4\xd0\x58\x1c\xa3\x1d\x97\x3c\ +\x60\xa8\x66\x79\x2e\xb1\x85\x58\x40\x5a\xe8\xe3\x39\x98\x36\xe9\ +\xd7\x88\xec\x81\xeb\xb4\xc1\x6b\xb9\xcd\xd5\x57\x10\x39\xf9\x0c\ +\xf1\x5c\xfd\xa7\xe8\x68\x44\xf6\xc4\x61\x7a\xc6\xf3\x12\xe1\x61\ +\x88\x9c\x6c\x86\x6a\xb9\xd7\x75\x7e\xdf\x65\xc4\xdf\x2b\x45\x9a\ +\xa0\x3a\x8f\x25\xf8\x29\xd7\x8d\x27\x7b\xe8\xcf\xdf\x86\x9f\x26\ +\xbd\xca\xf1\x93\x81\xd0\x55\xaf\x7a\xbc\x83\x60\x5e\x42\xcf\x63\ +\x02\xed\xaf\x8f\x3c\x9e\x3a\x77\x35\x02\x07\xc8\x15\xda\xe8\x71\ +\x2c\x60\x20\x02\x27\xb1\xfa\x2f\xf6\x16\x32\x6f\x47\xfa\xb6\xbc\ +\x64\xd2\x5b\xef\x7a\x2b\xcf\xc5\x0c\x07\x26\x8d\x7d\xf4\xba\xa7\ +\x60\xa9\xd1\xed\xc8\x1b\x12\x37\xab\xda\xdb\xf9\x81\x65\xc8\x9b\ +\x1c\x4a\xf4\xa2\xa7\x40\x99\xad\xd3\x91\x37\x44\x4e\xd5\x6c\x6f\ +\xab\x03\xdb\x23\x6f\x32\x28\xd0\x0c\x4f\x41\xf2\x14\x27\xcb\x84\ +\x4e\x77\xfd\xc9\xdb\xea\xc0\x22\xe4\x4d\x42\xf5\xbf\xdf\x4b\x78\ +\xd4\xe9\x0e\xc4\x8d\x08\xbf\xf5\xb4\x79\xf8\xf7\x6c\x16\x8e\x3f\ +\xbf\xf2\x12\x1a\x15\x9a\x80\xb4\x11\xe2\x9b\x5a\xe9\xe9\xbc\x00\ +\xa0\xfa\x37\x33\x4a\xdc\x1f\x69\x23\x46\x5f\x2d\xf0\x52\xd6\xbf\ +\x44\xda\xf8\x72\xbe\x97\x53\xe7\x9f\x43\xd8\x88\xe2\x63\xa1\x70\ +\xbd\xce\x43\xd8\x78\x72\xb8\x97\xcd\x23\xff\x86\xb0\x11\xe6\xe7\ +\x1e\x4a\x7c\x83\x46\x22\x6c\xfc\xe8\xea\x65\xad\x18\x93\x7e\x51\ +\xe7\x14\x2f\xfb\x3b\x99\xed\x89\x19\x1d\xf4\xb1\xf3\x30\x58\xaf\ +\x51\x08\x1b\x03\x0e\xd1\x3a\x0f\xa7\x05\x70\x9b\x50\x8c\xc8\xd5\ +\xa3\xce\x43\xe0\x63\x8d\x40\xd8\x98\x70\xb0\xe6\x38\x2f\xff\xc7\ +\x94\x8b\xb0\x71\xe1\x5a\xe7\xc5\xff\x9a\x3a\x22\x6b\xac\x32\xc0\ +\x57\x9c\xc7\xc0\xcf\x90\x35\x1e\x7c\xc3\x79\xd1\x3f\x83\xfb\xc7\ +\x8e\x76\x9a\xec\x3c\x0e\x4e\x46\xd6\xe8\x73\x98\xf3\x5b\xfe\x1e\ +\xe6\xd8\xe8\x58\x52\xac\x47\x9c\x0f\x06\x7e\x15\x59\xa3\x4d\x7b\ +\xe7\x9b\x44\x6f\xe3\xe2\x88\xd8\x92\xa7\x1b\x1d\x47\xc3\x7b\x6c\ +\x11\x8a\x36\x77\x39\x2e\xf0\xdf\x2a\x07\x51\x63\xcd\x6f\x1c\x47\ +\x04\x9b\xbf\x23\xcc\x8f\x1c\x17\xf6\x4d\xb4\xfe\xb1\x27\x57\xbf\ +\x76\x1c\x15\x9c\xfd\x14\x51\x8e\x77\xdc\xfb\xbf\x8a\xd6\x3f\x21\ +\x5c\xe5\x78\x24\x60\x1c\x92\x46\x8f\xae\x9a\xe9\xb4\x98\x6f\xa1\ +\xfa\x27\x08\xb7\x77\x40\xbf\xad\xae\x48\x1a\x35\xee\x74\x5a\xc4\ +\x77\x32\xf1\x97\x28\x72\x74\xbb\xd3\xf8\xb8\x1b\x49\xa3\xc5\x44\ +\xc7\x03\x3d\xb4\xfe\xc9\xe3\x16\xa7\x31\x72\x39\x82\x46\x87\x61\ +\xda\xe2\x74\xde\x9f\xa1\xbf\x24\xd2\xce\xe9\xba\x80\x2d\x1a\x8a\ +\xa4\xd1\x20\x5f\xcf\x3b\x2c\xd8\xc9\xea\x80\xa4\x09\xa5\xbd\x9e\ +\x75\x18\x29\x7f\xe1\xfa\xd7\xe4\xa5\xff\xd3\xe9\xfb\x27\x3c\x0b\ +\x78\x85\x6e\x40\xb2\xe8\xef\xf4\x5a\x28\x36\x7d\x26\x9d\x4e\x4e\ +\xaf\x87\xe3\x0e\xa1\x90\x29\x71\x78\xdd\xd7\x3a\x56\x7a\xa7\x82\ +\x83\xb5\xde\xe1\xbd\x10\xec\x13\x09\x15\x97\x1b\x7f\x0f\x47\xce\ +\x94\x30\xd2\x61\xd4\xfc\x3b\x72\x86\x99\xfe\x57\x39\x2b\xc8\x33\ +\x90\x33\x45\x9c\xe6\xf0\x5a\x58\x4e\x88\x0e\x8d\xd7\x38\xea\x13\ +\x42\xcf\x1d\xff\x8e\x98\xe1\xf0\x9f\x0e\x0f\xfc\x80\xf4\x31\x95\ +\x6e\x40\x9c\x19\xe8\x2c\xfd\x5f\x88\x98\x29\x65\x81\xb3\xed\x41\ +\x83\x10\x33\x58\xf2\xf4\x80\xa3\xc2\x5b\xa5\x7e\xc8\x99\x52\xfa\ +\x3a\xbb\x50\xec\x01\x56\x8f\x06\xcb\x11\xce\xae\xf8\x3c\x0b\x31\ +\x53\xcc\x04\xd5\x39\x8a\xa4\xa3\x10\x33\x48\x5c\x39\x37\x37\xfc\ +\xa6\x1d\x57\xbb\x48\x97\x23\x65\x70\xdc\xe4\xa8\xd0\xfe\x8c\x94\ +\xa0\x27\xb9\x49\x38\x5e\xb8\x5a\xc9\x35\x4b\x65\x88\x09\xea\xa1\ +\xb9\x8e\x56\x92\x0e\x47\x4c\xff\xb4\xd3\xef\x9d\x14\x57\xb5\x4e\ +\x43\x4c\x90\x24\x9d\xae\x6a\x27\x31\xf5\x07\xf6\x07\xfa\xe7\xab\ +\x6a\x74\x52\x58\xbf\x41\x4a\xf8\x9c\x3b\x9c\xc4\x54\x83\x0e\x41\ +\x4a\xdf\xb8\x39\xf9\xef\x6d\x84\x84\x2f\xe0\xe6\x3e\x89\xf7\x11\ +\xd2\x2f\x17\x38\x5a\xbf\xbd\x1f\x52\xc2\x17\xd8\x57\x9b\x9c\xc4\ +\xd6\x39\x48\xe9\x8f\xce\x5a\xe6\xa0\x88\x1a\x75\x25\x52\xc2\x97\ +\xb8\xd2\x49\xe7\x72\x29\xd7\xc8\xfa\xe3\x9f\xd5\xc4\xe6\x0d\xf0\ +\xc6\x74\x07\xd1\xd5\xa4\x9f\x20\xa4\x1f\xba\x39\x39\xcb\xa5\x41\ +\xdd\x90\x12\xf6\x90\x61\x36\x38\x88\xb0\xb9\x44\x98\xaf\x14\xcd\ +\x45\x1f\xed\x5c\x84\x04\xcf\x63\x4c\xdf\x47\x48\xf7\xe4\x3b\x71\ +\xe7\x29\x2a\x42\x4a\xd8\x23\x45\x9a\xec\x64\x87\x09\xeb\x01\x9c\ +\xe3\x62\xcd\xf6\x46\x1d\x81\x90\xd0\x22\x47\x39\xd9\x66\xce\x1d\ +\xc2\x8e\xe9\xa7\x8d\x0e\x8a\xe5\x57\x08\x09\x7b\xc5\xc5\x6d\xc2\ +\x1b\xd4\x17\x21\x5d\xf2\x5f\x0e\x0a\xe5\x03\x64\x84\x56\x31\xcb\ +\x41\xb4\xfd\x02\x19\xdd\x51\xa6\xc5\x0e\x8a\x84\xab\x9c\xa0\x75\ +\x0c\x73\x10\x6d\x9f\x68\x1f\x84\x74\xc5\x15\x0e\x0a\xe4\x3e\xce\ +\x6c\x81\x56\x92\xa7\x3f\x70\x6f\x50\x94\xb0\xef\xd5\xaa\xd2\x81\ +\xc8\x08\xad\xe6\x20\x07\x0b\x83\xb7\x21\xa3\x1b\x7e\xe6\xc0\x8d\ +\x7f\x8b\x8c\xd0\x26\x6e\x77\x10\x75\x3f\x46\x46\x3b\x25\x0e\xd6\ +\xff\x7f\x4a\xfa\x0f\x6d\x24\x57\xab\x1c\xec\x0b\x28\x46\x48\x2b\ +\x17\xaa\xde\x5c\x10\x27\x20\x23\xb4\x99\xe3\xcd\x71\x57\xaf\x8b\ +\x90\xd1\x46\xb1\x9e\x63\xf5\x1f\xc4\x36\xf6\x9e\x25\xf6\x6c\x0c\ +\x36\xef\xff\xab\xd1\x29\xc8\x08\x59\xf1\x4d\xd5\x9a\xb7\x9e\x73\ +\x65\x88\x89\x7b\xcc\x1e\xfc\x2e\x22\x42\xd6\xd8\xcf\x9f\xba\x13\ +\x11\xb3\xa7\xc4\xc1\x48\x2c\xab\xff\x21\x7b\x46\x3b\x88\xc0\xf6\ +\xc8\x98\x2d\xb7\x9a\xc5\x7f\x1a\x11\xc1\x84\x7d\x77\xe0\xcd\x88\ +\x98\x1d\x1d\xf5\xa9\xb9\x07\x36\x10\x19\xc1\xc4\x40\xf3\x36\xf4\ +\x15\xea\x80\x8c\x7b\x22\xb7\x85\xff\xdf\x78\xf3\x6a\xea\x07\xb5\ +\x14\x89\xc1\xc4\x32\x4d\x32\xfe\x42\x99\xc6\x23\x63\xdb\xc9\xd7\ +\x43\x46\xe7\xdd\xaa\x31\xc8\x08\x66\x8e\xd1\x36\x63\x24\x3e\xa4\ +\x7c\x64\x6c\x2b\xdd\xb5\xd9\x28\xfb\x0c\x44\x04\x27\xbc\x69\xde\ +\x89\xc2\x25\x74\x6d\xe6\x7b\xe6\xc1\x17\xda\x7f\x70\xc3\x38\x73\ +\x2c\x7e\x07\x11\xdb\xca\x22\xa3\xe4\xef\x20\x21\x38\xe3\x5d\xf3\ +\xe9\x00\xd0\x26\x8e\x30\x7b\xee\xd1\x88\x08\xce\xb0\xaf\x07\x38\ +\x0c\x11\xdb\xc2\xe3\x46\xb9\xdf\x54\x29\x22\x82\x33\x4a\xf5\xb6\ +\x31\x22\x1f\x45\xc4\xd6\xd3\x5d\x6b\x8d\xf3\xff\xec\xc4\x06\xb7\ +\xfc\x93\xf1\xe2\xb0\xcf\xb8\x2e\xa4\xf5\x9c\x6b\xdc\x84\xb1\x4e\ +\x3d\x11\x11\x9c\xd2\x4b\xeb\x4c\x31\x59\xab\xb3\x11\xf1\xcb\x34\ +\xb7\x10\xa8\x9d\x8e\x55\x81\xe9\x57\xa7\xaa\x02\x69\xc1\x29\x49\ +\x97\x69\x80\x00\x00\x03\xee\x49\x44\x41\x54\xab\xf4\x17\xd3\xfb\ +\x05\x3a\x9e\x63\x69\x5a\x47\x17\x2d\x31\xf6\xb7\xfa\x23\x22\x38\ +\x67\xa0\x79\x26\xa0\x33\x22\xb6\x06\xeb\x88\xeb\x34\x24\x04\x2f\ +\x58\x6f\x0f\x1e\x85\x84\xad\xe1\x7e\xa3\xcc\x27\x22\x21\x78\xe1\ +\xeb\xc6\xc8\xfc\x1d\x12\xb6\x86\x5a\x63\xa2\xd5\x09\x09\xc1\x0b\ +\x9d\x8c\xcb\xd3\xaa\x91\x70\x77\xbe\x3c\x08\x78\x8a\x71\x00\x70\ +\xb2\x36\x21\x2b\x78\x61\x93\x26\x9b\xde\x2f\xd2\xd7\x10\x71\x6f\ +\x06\x70\xa9\xe9\xf7\xb6\x6a\x8a\x32\xc8\x0a\x5e\xc8\x68\xaa\xb6\ +\x9a\x7e\xe1\x52\x44\x6c\x99\x0e\x5a\xcd\x9a\x6b\x88\x30\x9f\x98\ +\xe2\xb3\x92\x03\xc2\x5a\xce\x00\x8e\x50\x89\xe9\xf7\x1e\x46\x52\ +\xf0\xca\x9f\x8c\x0d\x1c\x67\x54\xb6\x68\x00\xa3\x8c\x06\xc0\x19\ +\xac\xe0\x97\xff\x33\xbd\x5d\xc2\x54\x60\x4b\x06\x50\xac\x23\x4d\ +\xbf\xf6\x96\x56\x23\x29\x78\x65\xa5\xf1\xa0\xf9\x51\x5c\x15\xb2\ +\x67\xca\xf4\x99\xa9\x87\xc5\x6a\x6b\xf0\xcf\x79\xc6\x51\x00\x4e\ +\x07\xda\x23\x87\x9b\xa4\xdd\x8c\xb4\x10\x00\xdd\xb5\xd5\x14\xa7\ +\x87\x22\xe1\x9e\xba\x00\xb6\x16\xfc\x25\x6d\x46\x50\xf0\xce\x66\ +\xbd\x64\x7a\xff\x5b\x48\xb8\x27\x03\x38\xd3\xf0\x4b\x19\xbd\xa2\ +\x1a\x04\x05\xef\x54\xeb\x15\xd3\x5a\x13\x3a\xaa\x7b\xa0\x9b\x29\ +\xb1\xda\xc8\xa1\x4b\x10\x10\xa3\x54\x65\x8a\x55\x76\x05\x36\x9b\ +\x01\x9c\x61\xfa\xa5\x4d\x9a\x89\x9c\x10\x08\xef\xa8\x8a\x4e\x80\ +\x7b\x03\xf8\x86\xe9\x97\x5e\x60\x09\x30\x04\x44\x93\x71\xcb\x39\ +\x17\xd6\x37\xcb\x4a\x53\x5a\x75\x1c\x02\x42\x60\x7c\xcd\x14\xab\ +\x2b\x11\xf0\xcb\x8c\xd0\x06\x83\xa4\xf5\x08\x08\x01\x67\x01\xd9\ +\x47\xeb\x7a\x0d\x43\xc0\xdd\xbb\x00\xc3\x4d\x8b\x80\x9f\x42\x4a\ +\x08\x94\x67\x0c\xef\x96\x68\x38\x02\xee\x6e\x00\x43\x4c\x17\x28\ +\xb2\x09\x08\x82\xc5\x12\x71\x05\x1a\x8a\x80\x5f\x34\x80\x22\x0d\ +\x36\xfc\x4a\x8d\xde\x42\x4a\x08\x94\x19\xaa\x33\xbc\x3d\x58\x85\ +\x48\xb8\xab\x01\x14\x9b\x0c\xe0\x43\x6d\x43\x4a\x08\x94\x6d\xfa\ +\xc8\x64\x00\x9c\x0b\xf0\x05\x03\x28\x31\x19\xc0\x6c\x0c\x00\x02\ +\x66\xab\x66\x63\x00\xee\x0c\x60\x60\xb3\x57\x84\xb4\x8e\x8c\x66\ +\xab\x01\x29\x21\x50\x1a\x4c\x06\x90\xa7\x01\x48\xb8\xab\x01\x8c\ +\x35\x25\x63\xb3\x11\x12\x02\x67\x96\x29\xef\x1c\x83\x80\xbb\x1a\ +\xc0\x51\x26\x03\x98\x85\x90\x10\x82\x01\x58\x0e\x08\x3d\x0a\x01\ +\xbf\xd8\xa3\xca\x7e\x59\x05\xd5\x1f\xc2\x61\x8e\x21\x6a\x39\xbc\ +\x7e\x97\x0c\x60\x1f\xd3\x90\xc8\x4b\xc8\x08\xa1\xf0\xb2\xe1\xdd\ +\x52\xf6\x04\xfe\xc3\x00\x0e\x37\xfd\xc6\x5f\x91\x11\x42\xe1\x79\ +\xd3\xdb\x23\x11\x70\xa7\x01\xd8\x76\xf2\xbf\x86\x8c\x10\x0a\xb6\ +\xc8\xe3\x68\x30\x27\x06\xb0\x89\x83\xc0\x20\x24\xaa\x4c\xc3\x80\ +\x18\xc0\xe7\x06\x60\xd9\x1b\xf5\x06\x22\x42\x68\xbc\x69\x78\x77\ +\x04\xf2\x6d\x37\x80\x4e\xa6\x7d\x80\x6f\x22\x22\x84\x86\xa5\xf9\ +\x29\x55\x29\x02\xe6\x4a\xea\x63\xba\x0f\x78\x06\x22\x42\x68\xbc\ +\x6e\x78\x37\x5f\xbd\x11\x30\x57\x52\x4f\x93\x01\xcc\x45\x44\x08\ +\x0d\xcb\x1a\x94\x02\xf5\x42\xc0\x5c\x49\xbd\x0d\x06\xb0\x5c\xb5\ +\x88\x08\xa1\x51\x6b\x38\xde\xab\x50\x3d\x11\x70\x7b\x06\x90\xfd\ +\x51\x20\xcb\x30\x00\x08\x91\x1a\x2d\xa5\x0b\x60\x33\x80\x1c\x93\ +\x0f\x56\x98\x8e\x65\x00\xb0\x51\xa7\x0a\xc3\xdb\xe5\x08\x98\xab\ +\x7c\xd3\x8d\x7e\x95\x18\x00\x84\x6a\x00\x95\x86\xb7\xcb\x4c\xc7\ +\xe0\x61\x00\x92\x2a\xb8\x0d\x00\x42\x24\x63\xca\x00\xba\x63\x00\ +\x36\x03\x68\x30\xf9\x2f\x80\x9d\xd5\x86\xc3\x68\xc8\x00\x8c\x06\ +\x50\x87\x01\x40\xc8\x58\x46\xa1\x30\x00\xe5\x2a\x4f\x5d\x31\x00\ +\x88\x2d\x95\x86\x4b\x69\xba\x62\x00\xb9\xca\x53\x91\xc1\x00\x2a\ +\x88\x40\x88\x6d\x06\xd0\x5e\x79\x18\x80\xe5\x58\x84\x7a\xad\x26\ +\x02\x21\xe4\x31\x00\xcb\xb5\x74\xa5\x18\x40\x6f\x93\x01\x00\x84\ +\x4b\x93\x29\x0a\x7b\x62\x00\x3d\x0c\x6f\x7f\x46\xfc\x41\xe8\xac\ +\xc5\x00\x2c\x06\x60\x59\x05\xb0\x8a\xe8\x83\x08\x8c\x02\x64\xcf\ +\x3e\x18\x40\x57\xc3\xdb\xcc\x01\x40\xf8\x58\xa2\xb0\x2b\x06\x80\ +\x01\x40\x7a\x33\x00\x0c\x00\x03\x80\x98\x63\x99\x89\xea\x86\x01\ +\x74\xc6\x00\x80\x0c\x20\xbd\x06\x50\x82\x01\x40\x6a\xc7\x00\x4a\ +\x30\x80\x0e\x86\xb7\xb7\x10\x7d\x10\x3a\x5b\x31\x80\xb0\x32\x00\ +\x4e\x03\x82\xf0\xa9\xc1\x00\xc2\x32\x80\x6a\xa2\x0f\x62\x6d\x00\ +\xed\x31\x80\xe2\x90\xa4\x07\x70\x43\x35\x06\x60\x31\x80\x3c\x0c\ +\x00\x52\x9b\x01\xb4\xc3\x00\x2c\x06\xc0\x18\x00\xc4\xdb\x00\xd8\ +\x0e\x6c\xf2\x40\x76\x03\x42\xf8\xd4\x61\x00\x61\x19\x00\x40\xbc\ +\x49\xbd\x01\xfc\x3f\x75\xb2\x6e\x24\x77\xdf\x76\x9d\x00\x00\x00\ +\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x04\xe7\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1b\x04\x3a\x1f\x2e\x11\ +\x22\x2a\x00\x00\x03\xb5\x49\x44\x41\x54\x78\xda\xed\x9d\x4f\x48\ +\x14\x51\x1c\xc7\x3f\xed\xaa\x74\xe8\x8f\x16\x86\x96\x44\x44\x1d\ +\xab\x4b\x86\x47\xc9\xb2\x6e\x1d\x0a\x8c\x0e\xdd\xea\xd2\x25\x83\ +\x08\x12\x3a\x15\xd4\xa9\x43\x54\x4a\x42\x10\x11\x51\x87\xa8\x43\ +\x07\x31\xe8\x96\x25\x28\x94\x84\x48\xb7\xd2\x42\x33\x8d\xd0\x72\ +\xad\xb6\x43\x20\xba\x33\xea\x9b\x3f\xbb\xdf\xe7\xcc\xfb\xcc\x69\ +\xdf\xf8\x1e\xdf\xdf\x87\x79\x6f\x76\x67\x67\x5c\x80\x32\x5a\x19\ +\x60\x86\xfc\x8a\xdb\x86\x69\x67\x33\x11\x29\xe3\x99\xbc\x90\x28\ +\xdb\x67\x76\x45\x13\xd0\x2a\x2f\x21\xea\x36\x1a\x4d\xc1\x80\xbc\ +\x00\xa9\x82\x55\xcc\x50\x11\x75\x16\x59\xc0\x18\x4d\xbc\x0b\x27\ +\x20\xaf\xce\x1e\x9b\x82\x03\xbc\x0d\xde\x2d\xa3\xce\x1d\x1b\xd5\ +\x74\xb3\x3b\xcd\x02\x42\x2a\x48\x92\x80\x50\x0a\xfc\xd6\x80\xbb\ +\xf4\x18\xf5\xdd\xcb\x29\x4f\xdb\x19\x7e\x17\xb9\xc8\x32\x6e\x2e\ +\xb9\x3f\xf0\x72\xe8\x3d\xa9\x9c\x34\xec\xd9\xe2\xd3\xb7\xf8\x67\ +\x94\x0a\x83\x93\x62\x80\xa3\x20\x59\x53\xe0\x3f\x81\x26\x42\x12\ +\x05\x04\x52\x60\xaf\x80\x9d\x34\x50\x53\x7c\x05\x76\x0a\x68\x64\ +\x80\x21\x5e\x31\xc2\x73\xb6\x16\x5b\x81\x7d\x8b\x60\x13\xb9\x79\ +\x23\x7e\xa2\x76\xc1\xde\xe5\x17\xc1\x40\xcb\xa1\x7d\x47\x40\x86\ +\x76\xca\xe7\xbd\xde\xc2\xe5\x65\xfb\x5c\xe3\x9b\x6f\xbb\xc1\x51\ +\x60\x9f\x80\x3d\xec\x28\x68\x39\xba\x6c\x9f\x7e\x0e\x86\x55\x60\ +\x9f\x00\xef\x15\x9e\xf5\xac\x5d\xb6\x57\x5f\x58\x05\xf6\x09\xc8\ +\x1a\xb6\xc5\xa4\xc0\x3e\x01\xe1\x09\xa5\x20\x49\x02\x42\x29\x48\ +\x96\x80\x10\x0a\x92\x26\x20\xb0\x82\xe4\x09\x08\xa8\x20\x89\x02\ +\x02\x29\x48\xa6\x80\x00\x0a\x92\x2a\xc0\x58\x81\x7d\x02\x7e\x78\ +\x5a\xfe\x32\x55\x3c\x05\xf6\x09\xe8\x27\x57\xd0\xd2\xcb\x6c\xc8\ +\xb1\x0c\x14\xd8\x27\x60\x92\x1b\x0b\x5e\xe7\x0d\x3e\x0d\x46\x52\ +\x60\xdf\xf5\x80\x72\x1e\xce\x8d\x37\x4b\x6b\xc1\x5e\xbf\xeb\x01\ +\x2d\x4b\x8e\x57\xcf\xc4\x22\xd7\x0b\x46\xa8\x2d\x8b\x21\x70\xdc\ +\xcc\x72\x9c\x3b\x1c\x61\x13\x1f\xb8\xcf\x60\xe4\xf1\x7a\x69\xa6\ +\x8b\x4a\x9f\x3d\xb5\x5c\xb2\xf1\x08\x58\x9a\xe0\x47\x00\x2c\x7e\ +\x14\x0c\xdb\xb7\x06\x14\x87\x5e\x9a\x99\xf4\x69\xaf\x49\x8b\x00\ +\xe8\xa5\xc9\x67\x39\xcc\xa4\x47\x00\xf4\xd1\xe6\x6d\x8c\x7b\x11\ +\xfc\xa2\xae\x72\x49\xa6\x8b\x2f\xa0\x4a\x5d\x63\x50\xd2\x34\x05\ +\x9c\x80\xb8\x05\xd8\x73\x77\x51\x84\x24\x51\x04\x7c\x55\xd7\x3d\ +\xc7\xa8\x46\x40\x0f\xdf\xd5\x95\x03\x30\xc1\x6b\x8d\x80\x69\x2e\ +\xaa\x6b\x07\xa0\x8d\x9f\x1a\x01\x70\x8b\x73\x7e\xe7\xd6\x12\x32\ +\xc5\x59\x6e\x47\x19\x20\xea\xfb\x80\xeb\xdc\xa3\x89\xba\x05\xdf\ +\xe7\x96\x8a\x59\x3e\xf2\x62\x91\x4f\xfb\x25\x13\x00\xe3\x3c\x12\ +\x14\x1f\x1b\xee\x7d\x80\x3a\x80\x1a\x27\x40\x1d\x40\x8d\x13\xa0\ +\x0e\xa0\xc6\x09\x50\x07\x50\xe3\x04\xa8\x03\xa8\x71\x02\xd4\x01\ +\xd4\x38\x01\xea\x00\x6a\x9c\x00\x75\x00\x35\x4e\x80\x3a\x80\x1a\ +\x27\x40\x1d\x40\x4d\xea\x05\x04\xbb\x2a\x5c\xc9\x06\x75\x60\x03\ +\xc6\x83\x7c\x63\x65\x2e\x60\x3f\x57\xa9\x57\xd7\x66\xc8\x1b\x2e\ +\xf0\xd2\xec\x4f\x4d\xa7\xc0\x31\xba\x56\x4c\xf9\xb0\x8f\x6e\x83\ +\x67\xcd\x02\x08\x58\x47\x87\xd1\x83\x4b\xf6\x90\xa5\xc3\xe0\x59\ +\x33\x63\x01\x87\x57\xc4\xdc\x5f\xc8\x46\x0e\xc5\x27\x60\x9b\xba\ +\x9a\x50\x6c\x8f\x4f\x80\x8d\x37\xd4\xc6\x94\x3a\xf5\xef\x03\x9c\ +\x00\x75\x00\x35\x61\x67\xf7\x18\x27\xd4\xd1\x0b\x78\x40\x75\x29\ +\x05\xfc\xa2\x5b\x5d\xb1\x27\x51\x28\x52\x3f\x05\x9c\x00\x75\x00\ +\x35\x4e\x80\x3a\x80\x1a\x27\x40\x1d\x40\x8d\x13\xa0\x0e\xa0\xc6\ +\x09\x50\x07\x50\xe3\x04\xa8\x03\xa8\x71\x02\xd4\x01\xd4\x38\x01\ +\xea\x00\x6a\x9c\x00\x75\x00\x35\x4e\x80\x3a\x80\x1a\x27\x40\x1d\ +\x40\x8d\x13\xa0\x0e\xa0\xc6\x09\x50\x07\x50\xe3\x04\xa8\x03\xa8\ +\x71\x02\xd4\x01\xd4\x38\x01\xea\x00\x6a\x9c\x00\x75\x00\x35\x4e\ +\x80\x3a\x80\x1a\xbf\xbb\xc4\x1a\x59\x5d\xd0\xe2\xbd\x51\x7e\x0d\ +\xa7\xd5\xd1\x3d\x89\x0a\xa9\xf7\x64\x6c\xf0\x76\x4b\xce\x2f\x4e\ +\x86\x24\xf5\x53\xc0\x09\x50\x07\x50\x93\xf1\xfc\x2f\xf7\x74\x91\ +\xcb\x30\xa4\xce\x20\x65\x30\x43\xa7\x3a\x83\x94\x4e\xc8\xf2\x54\ +\xfe\xd3\xd9\xaa\xed\x09\xd9\x2c\x79\x1e\x33\x4e\x1d\x55\x2b\xec\ +\xc1\xb8\x68\xe4\x78\xcf\x15\xce\xf3\xe7\x1f\x45\xc0\x39\x68\xd6\ +\x1f\xd2\xc2\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\ +\x63\x72\x65\x61\x74\x65\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\ +\x37\x54\x30\x34\x3a\x35\x38\x3a\x33\x31\x2b\x30\x30\x3a\x30\x30\ +\x0c\xce\x02\x9e\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\ +\x3a\x6d\x6f\x64\x69\x66\x79\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\ +\x32\x37\x54\x30\x34\x3a\x35\x38\x3a\x33\x31\x2b\x30\x30\x3a\x30\ +\x30\x7d\x93\xba\x22\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\ +\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\ +\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\ +\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x0c\x9c\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x04\x06\x05\x0e\x0d\x77\x13\ +\x99\xf0\x00\x00\x0b\x6a\x49\x44\x41\x54\x78\xda\xe5\x9d\x7b\x70\ +\x57\xc5\x15\xc7\x3f\xbf\x07\x11\x08\x08\x04\x04\x09\xa2\x84\x02\ +\x21\x58\xed\x43\xb1\x45\x7c\x00\x8a\x42\x99\xf8\x9a\x81\xa9\xc2\ +\xd8\x69\xad\x76\x46\x2b\x5a\xac\xa5\x1d\xad\x20\x01\x05\xa7\x3a\ +\xd3\x4e\xa7\xb5\xe2\xa3\xc5\xd2\xd2\x1a\xd4\x8a\xce\x20\x83\x48\ +\xc1\x80\x0a\x55\x34\x48\x00\x11\xc2\x53\x14\xc2\x43\x48\x80\x98\ +\xe4\xd7\x3f\x62\x30\x24\xe7\xdc\x7b\x77\xef\xde\xdf\x8d\xe3\x77\ +\xff\xc8\xcc\xfd\xed\x7e\xcf\x39\x9b\x7b\xef\xee\x9e\x3d\x7b\x6e\ +\x82\x6c\x21\x41\x01\x45\xf4\xa7\x1f\x7d\xe9\x49\x77\xba\xd3\x9e\ +\x1c\x72\x81\x6a\x6a\x39\x4e\x15\x55\x7c\xca\x0e\x2a\xd9\xc6\x06\ +\x2a\xc9\x64\x4b\xad\xa8\x91\xcf\x70\x86\x33\x94\xf3\xe8\x6c\xd0\ +\xea\x33\xca\x59\x43\x19\x65\x7c\x9c\x9d\x8e\x70\x8f\x1c\x46\xf3\ +\x18\x1b\xc9\x84\x2c\x1b\x78\x94\xd1\xe4\xc4\x6d\x8e\x09\xd2\x8c\ +\xe1\x19\x0e\x86\x36\xbd\x79\x39\xc0\xd3\x5c\x4d\x3a\x6e\xd3\xfc\ +\xd1\x87\xa9\x6c\x77\x6a\x7a\xf3\xb2\x87\xd9\xf4\x8f\xdb\x44\x1d\ +\x97\xf2\x22\xf5\x91\x19\xdf\x54\xea\x79\x81\x4b\xe2\x36\xb5\x35\ +\xc6\xf1\x56\xe4\xa6\x37\x2f\xab\x19\x13\xb7\xc9\x5f\x62\x24\xab\ +\xb2\x6a\x7c\x53\x29\xe3\xf2\xb8\x4d\x87\xde\xcc\xa3\x21\x16\xf3\ +\x1b\xcb\x22\xfa\xc6\x67\x7c\x9a\xbb\x38\x1c\xa3\xf1\x8d\xe5\x28\ +\x53\xc3\x8c\x0e\xf6\x13\xa1\x8b\xf9\x13\xdf\x0a\xd1\x7d\x07\xa9\ +\xa6\x9a\xa3\x40\x27\x72\xc9\xa5\x5b\x08\xae\x75\xdc\xce\xea\x6c\ +\x76\x40\x8a\x69\xdc\x47\xd2\xa8\x4d\x3d\x1b\xa8\x60\x33\x15\x6c\ +\xe2\x43\x3e\x13\x6a\x74\x61\x20\x85\x0c\xa6\x90\xc1\x0c\x21\x65\ +\xc4\xde\x40\x09\x25\xd4\xdb\x75\x82\x29\x7a\xb2\xc4\xe8\x26\xfd\ +\x88\xbf\x30\x9e\x3c\x23\x19\x9d\xb8\x92\xd9\xac\x35\x7a\xbf\x2c\ +\x27\x3f\x1b\xe6\x5f\xc5\x27\x06\x83\xd5\xed\xf4\x0c\x25\xad\x17\ +\x77\xf0\x66\x60\x79\x7b\xb9\x32\x5a\xe3\x13\xcc\x08\x38\xd5\xa9\ +\xa4\x84\x41\xce\xe4\x16\x32\x33\xe0\xfc\xb2\x9e\xe9\xd1\x2d\xf0\ +\x52\x3c\x19\x48\x89\x72\x6e\x36\x7c\x82\x83\x20\x49\x31\x6b\x03\ +\xc9\x9f\x47\xbb\x28\xcc\xef\xc8\xcb\x01\x84\xbf\xcb\xf8\x08\x97\ +\xd8\x09\x8a\x03\x3d\x10\x8b\xe8\xe8\x5a\x74\x5e\x80\xd9\xde\x16\ +\xc6\x45\x66\x7a\x73\x14\xf3\x91\xaf\x2e\x65\xa1\x86\xd5\x56\xe8\ +\xc3\x7a\x1f\x81\xc7\x98\x4e\xfb\xac\x98\x0f\xd0\x81\x19\x1c\xf7\ +\xd1\x68\xbd\xbb\x31\x21\xcf\xd7\xfc\xc5\x0c\xc8\x9a\xf1\x4d\x18\ +\xc8\xab\xbe\x5d\xe0\xe4\x2e\xe8\xc0\x4a\x4f\x31\x9f\x33\x35\x0b\ +\x8e\x35\x19\xb7\x71\xc2\x53\xb7\x37\xc9\x0d\x2b\xa2\x1d\xaf\xf8\ +\x0c\x77\xdf\x8f\xc9\xf8\x46\x5c\xc0\x16\x4f\xfd\x5e\x0e\xe7\x45\ +\x4a\x30\xcf\x93\xbe\x94\x2e\xb1\x9a\x0f\xd0\x95\xe7\x3d\x75\x7c\ +\x3a\xcc\xfd\x39\xc3\x93\xfa\xe1\xd8\x6e\xfd\x53\x91\xe0\x11\x4f\ +\x3d\x1f\xb0\x25\x1e\x49\x9d\x4a\xda\xc0\xbd\xd6\x0a\x77\x62\x2c\ +\xb3\x58\x48\x39\xbb\x38\xc8\x41\x76\x51\x4e\x29\x33\x19\x43\x27\ +\x6b\xce\xc9\x1e\x33\xd4\x7a\x46\xdb\x50\xf6\xe2\x63\x8f\x17\xdf\ +\x4f\xac\xd4\xcc\x61\x02\x8b\xa8\x55\x79\x6b\x79\x89\xf1\x96\x2e\ +\xf0\x49\x1e\xbc\x7b\xe9\x6d\x4a\x97\xe2\x75\x0f\x35\x6d\x26\x3c\ +\xed\x99\xcc\x4e\x9f\x81\xab\xb1\xec\xe0\x4e\xab\x39\x45\xb1\x47\ +\x17\x2c\x35\x5c\xbc\x7b\x3c\xfd\x0d\xfc\xc8\x42\xb9\x51\x54\x04\ +\x32\xbe\xa9\x6c\x61\xac\x85\x94\x9b\x3c\x1e\x04\xa3\x37\xc1\xc5\ +\x1e\x44\x77\x1b\xab\xd5\x9e\xdf\x1b\x19\xdf\x54\xe6\x59\xcc\xe8\ +\xa7\xa8\x6c\x75\x0c\x0b\x4a\x92\xe2\x5d\x95\xe6\x21\x63\x95\xf2\ +\x59\x67\x65\x7e\x86\x0c\xef\x98\x3f\xbb\xcc\x51\xd9\x2a\x38\x2d\ +\x18\xc5\xdd\x2a\xc5\x73\xc6\x03\xdf\x40\xb6\x59\x9b\x9f\x21\xc3\ +\x56\x06\x1a\x4a\x4c\x78\xcc\x0b\xa6\x05\x21\x38\x93\x43\x4a\xf3\ +\x4a\x43\xb7\x16\xf4\xa1\x32\x94\xf9\x19\x32\xec\xe4\x6c\x43\xa9\ +\x5d\xd9\xaa\x70\x1d\x0f\xb2\x66\x59\xa0\x34\xae\x35\x9e\xf4\x76\ +\xe6\x83\xd0\xe6\x67\xc8\x50\x6e\x3c\x3f\x18\xaa\xae\x11\x9e\xf3\ +\x6b\x3a\x52\x55\x63\xb2\xa1\x12\xf0\xac\x13\xf3\x33\x64\x58\x60\ +\x2c\xfb\x17\x2a\xd7\xa5\xde\x0d\x35\xb7\xc7\x62\xe3\xa7\x7f\x52\ +\x00\xc3\x8e\x50\x41\x05\x47\x02\xd4\xbc\xd1\x50\x7a\x42\xf5\x5c\ +\xaf\xf0\x6a\x36\x4e\x7d\x76\x4c\x1d\x9c\x5d\xd9\xeb\x61\x4e\x2d\ +\xcf\x31\xb1\xd9\x1b\xa5\x3b\x93\x28\xe5\x73\x8f\x16\x7b\x38\xdd\ +\x50\x83\x01\x1c\x53\xb8\x3c\x26\xc6\xda\x0e\xef\x74\x43\xe1\x30\ +\xdb\xc3\x98\x52\xa5\x3b\x0b\x59\xe8\xd1\x6a\xa6\xb1\x0e\x25\x0a\ +\xd3\x1b\x5a\x83\xcb\x94\x06\x5b\x8c\x27\xa6\x5d\xd4\x91\xe4\x84\ +\xcf\x2a\x62\xa2\xfa\x7f\x3b\x4c\x57\x43\x2d\x3a\xa8\xbe\xc3\x8b\ +\xe5\x06\x2f\x2a\xd5\xcd\x67\xfe\xbf\x54\x98\x6a\xb8\xcc\xb7\xed\ +\xe5\x6a\x17\x4c\x31\xd6\xe3\x1a\x85\x69\xa1\x54\xb9\x40\x99\xfe\ +\xbe\x69\x2c\x16\xde\x17\x99\x1a\x98\x18\xa8\xf5\x78\x65\x43\xec\ +\x5d\x0b\x4d\xd6\x88\x4c\xf5\x9c\xd3\xba\xea\x4c\xa5\xb7\x8a\x8d\ +\x85\x9e\xab\x30\x3d\x1e\x98\x61\xae\xc2\x30\xd8\x58\x97\xeb\x83\ +\xbe\xd5\xd2\xec\x16\x2b\xae\xb3\xf0\xfa\x4c\x16\x99\x8e\x70\x66\ +\x60\x86\x9e\x7c\x26\x72\xdc\x61\xac\x4b\x42\xb9\x1b\x77\xb6\xdc\ +\xbb\x1a\xab\xf4\xd4\x04\x63\x91\x50\x2a\x32\x3d\x6a\xc4\xf1\x98\ +\xc8\xf1\x6f\x0b\x6d\x6e\x52\x2c\x6b\xb1\x8d\xfa\x8c\x58\xa9\xd2\ +\x6a\x8f\x4f\x5e\xf9\x07\x5e\x8c\x02\x30\x5c\xe4\x58\x6f\xa1\x4d\ +\x4a\xd9\x56\x9d\xdb\xbc\x52\x0e\x07\xc4\x4a\x33\x2c\x04\xa6\xc5\ +\x79\xf8\xa7\x86\x1e\x99\x24\xfb\x04\x96\xe3\x56\xff\x90\x87\x44\ +\xdb\x0e\x34\x77\xbd\x5d\xa5\xdc\x26\x85\x16\xe2\x7a\x88\x4c\x65\ +\xc6\x3c\xab\x45\x1e\x9b\xbd\x9e\x41\x8a\x75\x23\x1b\x7b\x1a\x50\ +\xdc\x4f\xab\xd9\x64\x21\x4e\x5e\xb9\xed\x31\xe6\xd9\x2d\x5e\x35\ +\x9d\x10\x03\x6c\x66\x8d\x78\x7d\x8c\x5f\x07\x3c\x6b\x21\x4c\x8b\ +\x3a\x32\x8f\xf9\x3e\x26\x5e\xb5\xf3\x18\xcb\x96\x9c\xb4\x3a\x5f\ +\xbc\x41\x1a\x2c\x83\x5b\x72\xc4\xd5\xdd\xed\xc6\x3c\xaf\x89\x5a\ +\x99\xbb\xc8\x00\x7a\x8b\x53\xab\x93\x16\x4e\x10\x45\xbd\x6f\x25\ +\x0a\xe0\xa9\x56\x5c\x47\x0d\xe6\x00\x4d\xd8\x2c\xaa\x6c\xbb\x05\ +\x2f\xbb\x66\x6e\x68\x7c\x04\xe4\xa5\xc1\x32\xeb\x0e\xb8\xaf\xd5\ +\xf3\x3b\x95\xbd\x86\x1c\x67\x8b\xce\xab\x8f\x39\x6e\xa9\x93\x6c\ +\xcd\xf0\xc6\x3f\x65\x62\xef\x5c\x6b\xdd\x01\xf0\x8d\x66\x73\xf0\ +\x23\x16\xb7\x3f\xdc\x29\xea\xb4\xd4\x5a\x23\x79\x4a\xbc\x12\x20\ +\x21\x86\xbb\xd6\x85\x0c\x2e\x48\x72\x05\xd3\x98\xc3\x2d\x9c\x61\ +\xd1\x3a\xcd\x06\x51\xe1\x59\xd6\xfa\xe4\x89\x4b\xbd\x43\x24\xa0\ +\xbf\xe3\x37\x80\x0b\xdc\xa6\x8c\xdc\x61\xa2\x00\xe5\x28\x97\xb3\ +\x93\x0c\x11\xab\x57\xc4\x68\x7e\x81\xf2\x9f\x3e\xc4\x7f\x43\xb0\ +\xca\x16\x0d\x49\x52\x20\xfe\xb0\x31\x36\xf3\x3b\xf3\x12\x3d\xc4\ +\x5f\xfe\xc5\xe7\x21\x78\xe5\x49\x5d\x41\x92\x7e\x06\xd5\xa3\x47\ +\x5f\x96\xf3\x4d\xe5\xb7\x3f\x87\x62\x96\x2d\xea\x97\x54\xf6\x5d\ +\xe2\xe9\x80\xeb\x79\x9b\xef\x2a\xbf\xbd\xc2\x7b\xa1\xb8\xe5\x7b\ +\xba\x1f\x4a\x1c\x40\xb6\x63\x7f\xda\x53\xcc\x1b\xca\xab\xaf\x71\ +\x54\xfa\x4e\x48\x09\xdd\x44\xde\xd7\xd2\xe2\x30\x55\x2f\xc6\xf3\ +\xbb\xc7\xb7\x39\x9f\x7c\xf2\xe9\xcf\x08\x9f\x80\xb6\xc7\xad\x3c\ +\x82\xcd\x71\x98\x06\x61\x49\xde\x03\xf6\x88\xe3\x63\xf4\x28\x32\ +\x08\x83\xdf\x62\xb5\x0a\x6c\x09\xc9\xcd\xb6\x0b\xd1\x83\xbf\x2b\ +\x72\xf3\x07\x2b\x2e\x18\xa9\x1c\x63\xa8\x13\x99\xd2\xbf\xfa\x00\ +\xd4\x08\x97\xa3\x9f\x05\xbc\x11\xd8\xfc\x7a\xc6\x3b\x92\xb9\x49\ +\x60\xaf\x46\x0c\x85\x7b\x3b\x62\xf3\x8b\x0c\xcc\xbf\xd5\x99\x54\ +\x69\x8f\xa0\x2e\x9e\xe3\xc8\xe7\x05\xac\x77\x82\x5b\x98\x1f\xa9\ +\x26\x99\x34\xb5\x74\x68\x75\xd9\xe4\xa4\xbf\x0d\x82\x05\x3c\xec\ +\x66\x02\xab\x1c\x4a\x95\x5e\xa4\x27\x92\xd4\x0a\x97\xa3\xee\x80\ +\x20\x58\xc0\xf9\x4e\xcd\x97\xad\x3a\x91\xa4\xba\x0d\x76\xc0\x3b\ +\x8c\xe2\x46\x0e\x38\x66\x95\xee\xbb\x9a\xa4\x28\x26\x37\xb6\x30\ +\xe8\x7a\x5e\xe1\x07\x5c\xc0\xeb\xce\x99\x93\xe2\x54\xab\x2a\xcd\ +\x7e\xe1\x72\x8a\xd3\x39\x9c\x75\xe3\x57\xf1\x57\x5e\x8e\x2c\x67\ +\x48\x17\x71\x6b\xa6\x2a\xc9\x3e\xb1\xba\x69\x74\x9e\x0b\x3c\xc5\ +\xdc\x08\x53\xa6\xc8\x16\xed\x4b\xb2\x43\xfc\xc1\x66\x4f\xa8\x6d\ +\x43\xde\x5a\xdf\x9e\x64\xfb\xd7\xa4\x03\x64\x8b\xb6\x25\xd9\x2a\ +\xfe\x60\x1e\x8a\xd0\xd6\x21\x77\x40\x65\x92\x0d\x5f\x93\x0e\x90\ +\x2d\xda\x90\xa4\x52\x5c\xfb\x0f\x71\x7b\xf2\x32\x76\xe4\x51\x24\ +\x5c\x3d\xc4\xce\x24\x19\xca\x85\x9f\x52\x6d\x21\x41\x89\x43\x8c\ +\x10\x07\xc1\x72\x32\x49\x50\x36\x8f\x47\x44\xa8\x4e\x8d\xc1\x55\ +\x37\x18\x25\x5e\x7d\xab\x71\x6f\xb0\xcc\xa0\x89\x1b\xc8\xfe\x86\ +\x0f\xb2\xde\x01\x5f\x58\xde\x5b\x5c\x89\x37\xd0\x2b\x32\x75\x12\ +\xc2\x99\x94\xb5\x11\x9a\x9f\xef\xbd\x3d\xae\x85\x35\x99\x07\xa5\ +\x05\xc7\xd0\x16\x9e\xa8\x1a\xd5\x1d\xee\x02\x72\xe0\x5e\x33\x37\ +\xbb\x1c\x94\x66\x13\x23\x1a\x1c\xc3\xf8\xf0\xa4\xa4\x4d\x11\x9f\ +\x40\x96\xe3\x45\x67\x7f\x59\x61\xb4\xe2\x90\x8a\x76\x36\xd0\x8e\ +\x31\xfc\x86\x5f\x73\x75\x34\x29\x2f\x4e\x42\x73\xc0\x8d\x68\xae\ +\x4a\x95\x58\xa5\x24\x52\xc5\xb2\x85\x87\x45\xdb\xf6\x9d\x7a\xae\ +\xfc\x69\xb1\x92\x5d\xa0\x64\xdb\x42\x8a\x1d\xa2\x6d\x4f\x9c\x5a\ +\xed\x6a\xe5\x36\xf9\x61\xdc\xfa\x87\xc6\x44\xc5\xb2\x16\x03\x63\ +\x9a\x5d\x62\xb5\x0f\x4c\xcf\xdc\xb6\x31\x24\x28\x17\xed\xda\xd9\ +\xfa\xde\xd6\x0e\x98\x84\x89\x15\xf2\x47\x6e\xf8\x24\x17\x9e\xb8\ +\x41\xb1\x6a\x7a\xeb\xaa\xfd\x94\x03\x13\x51\x6d\x92\x5c\xc4\xfc\ +\x2f\x5e\xbd\xfb\xf9\xbb\xa3\xcd\xaf\xd6\x90\x53\x2f\xd5\xc9\x41\ +\x01\x2f\x28\xbd\x65\x7e\x64\xc2\x0f\x69\xfe\xd8\x62\x6e\xd6\xc0\ +\x1f\x22\xc8\x19\x7b\x9d\x62\x51\xa9\x5c\xfd\x12\xa5\x7a\xa5\xe3\ +\xbc\x4c\x09\xfe\x29\xca\x99\xef\xd8\x17\xdd\x41\x3d\x42\xab\x86\ +\xee\xaf\x56\x1a\xd8\x84\xcd\xeb\xf8\x99\x22\x25\xc3\x4f\x9d\xca\ +\x99\xa5\x48\xf1\x38\x3a\xa9\x9d\x1b\x39\xe1\xd0\x47\x98\x16\xb7\ +\xa9\x1b\xcb\x6e\x87\x8f\xc1\x40\x35\xd7\xd4\x15\x5e\xcd\xb4\x6d\ +\xeb\x57\x9d\xdd\x9e\xa3\x54\xf3\x5b\x4c\x4f\x43\x21\xc1\x52\x45\ +\xc2\x72\xef\x86\x97\xab\xaa\xdd\xed\x48\xb5\x29\x9e\x1d\xe0\x4a\ +\xca\x3d\x0a\x7f\x43\x53\x7c\xb0\x8e\x7f\x28\x4d\x6b\x0d\x4f\xfd\ +\x68\xf0\xce\x4d\xe4\xe6\x6d\x73\x91\x7a\x7c\x3e\xc0\x39\xf4\x33\ +\xd5\xb4\xe8\xdb\x8d\x13\x28\x48\x98\xec\xd9\x01\x3f\x77\x20\xa1\ +\x9b\x9a\xb5\xa2\x46\x89\x8a\x0c\xac\xe2\xf3\x0e\xde\x04\xdf\xf3\ +\xec\x80\x0b\x43\xf3\x27\xf8\x8f\xca\x7e\x7f\x30\x8a\x14\xef\xa8\ +\x14\x8f\x84\x56\x30\x29\x1e\x85\x68\x2c\x1b\x1d\x74\xf0\xef\x54\ +\xf6\xf2\xe0\x07\x6e\x86\x79\xa4\xd1\xb9\x27\xb4\x8a\xd7\xab\xdc\ +\xd7\x84\xe6\xbe\x57\xe5\xae\x33\xf3\x3a\x3d\xa8\x12\x35\xf0\xe3\ +\xd0\x6a\xca\xc9\xcf\x66\x87\xe6\x9d\xe4\x91\x87\x38\xe0\xed\xdf\ +\x84\x14\xcb\x54\xaa\x5a\x07\xab\x83\x29\x2d\x9c\xa2\xd5\x0e\x06\ +\xc0\x6b\x3d\x72\x50\x2c\x31\x5f\xd6\xf7\xf2\x98\xb1\xd9\x26\x53\ +\x6b\x8e\xb3\x98\xc9\x5a\xf6\xb1\x8f\xb5\x94\x70\x56\x68\x3e\xc7\ +\xc9\xd4\xc0\x2f\x9d\xde\xaf\x42\xab\xec\x12\x77\x79\xa6\xd3\xb3\ +\x3e\x69\x32\xdd\x73\xc8\x9a\xd3\x66\x12\x2a\x3e\xea\xa9\xa7\xe1\ +\xd3\x7f\x2a\xf5\x33\x9e\xd4\xcf\x1b\xe7\xf5\x70\x8f\x3c\x8f\x71\ +\x3f\x43\x86\x27\xc3\xfd\x9b\x52\x3e\x19\x3b\xb7\x6b\x09\x49\xb2\ +\x84\x0b\x7d\x92\x2c\x2f\x0a\xbf\xbe\xf4\x4f\xab\x3b\x3d\x26\xc7\ +\x69\x82\xbb\x7c\xd2\xea\xae\x76\xe3\x71\xf4\x4f\xac\xbc\xc4\x61\ +\x26\xf9\xa0\x28\x54\x17\xbc\x4d\xa5\xdc\x5d\x90\x47\xbe\x6f\x17\ +\x1c\xa7\x44\x88\x39\x8e\x0a\x1d\x99\xe5\xf3\xbf\xcf\x50\xee\xf6\ +\x73\x0b\xdd\x02\x44\xf8\x6f\x75\x30\x95\x0d\x82\xeb\x02\xe4\x28\ +\x5c\xe9\x3e\xc4\xe7\x34\x25\x39\xca\xa9\x65\x5d\x24\xdf\x16\x68\ +\x42\x82\xe2\x40\x1f\x74\x7a\x29\x9a\xbb\x31\xc5\x13\x01\x84\x67\ +\x58\xcf\xcd\x11\xb8\xb8\x93\x14\xf3\xbf\x40\xf2\xff\x16\xdd\x47\ +\xd9\x12\x3c\xe0\x31\x3b\x3c\x75\x78\x9c\xe5\x70\x73\xbd\x88\x87\ +\x94\x2d\xce\x96\xa5\x8e\xfb\xa3\x9e\x9e\x8d\xf2\x48\xb8\xdc\xb2\ +\xbc\xcd\x9d\x96\x39\x1f\x9a\x90\xcf\x64\x25\xbc\x41\x2a\x7b\x1a\ +\x13\xa3\x44\x8d\x33\x58\x1c\x58\xa5\x0c\x4d\x9f\xd9\xe9\x6e\x24\ +\xa3\xb3\xc5\x67\x76\x96\xd9\x74\xb6\xdd\xed\x92\xe4\x7e\x1e\x30\ +\xfe\x14\x52\x05\x15\x6c\x66\x23\x1b\xf9\x48\x3c\xa5\x90\xc7\x00\ +\x0a\x19\xcc\x20\x8a\x28\x32\xfe\x8c\xd3\x83\xcc\xa2\x21\x5b\x1d\ +\x00\x30\x8c\xb9\x9c\x6b\xdd\x1a\x0e\x53\xcd\x51\x8e\x00\x9d\xe9\ +\x44\x6e\xa8\xc3\xba\xeb\xb9\x35\xe2\x88\x26\x11\x39\x4c\x53\x73\ +\xff\x65\xaf\xd4\xf0\xdb\x38\xbf\x48\x7a\x56\xec\x9f\xdb\x2b\x08\ +\x6f\x44\x58\x5c\xca\x8a\x58\x8c\x5f\xee\xbf\xcb\x93\x3d\x8c\x36\ +\x38\x0c\xeb\xa2\xac\xf0\xde\xe2\x8c\x07\xc3\x58\x98\x95\x8f\xae\ +\x96\xc6\xfc\x61\x17\x4f\xf4\x61\x6a\xc8\x74\xea\x5e\x65\x37\xb3\ +\xdb\xc2\x33\xef\x87\x14\x57\x32\x97\xfd\x4e\x4d\xdf\xcf\x13\x5c\ +\xf1\xd5\x8a\x5a\x6c\xc7\x28\x1e\x51\x32\x7a\x9a\x94\xf7\x98\xc3\ +\xc8\x28\x97\x37\x51\xa3\x27\xc3\xb9\x84\x8b\x38\xcf\x68\xaa\x73\ +\x88\x72\xd6\xb0\x92\x55\x7c\x1a\xad\x7a\xd9\x74\x6c\x9f\x43\x11\ +\x05\x14\xd0\x97\x5e\x74\xa7\x3b\x1d\x49\xd3\x19\x38\x42\x1d\x35\ +\x54\x51\xc5\x27\xec\x64\x1b\x5b\xa9\x50\x4e\x33\x46\x80\xff\x03\ +\x24\x3f\x99\x53\x66\x22\x7d\x64\x00\x00\x00\x25\x74\x45\x58\x74\ +\x64\x61\x74\x65\x3a\x63\x72\x65\x61\x74\x65\x00\x32\x30\x32\x30\ +\x2d\x30\x34\x2d\x30\x36\x54\x30\x35\x3a\x31\x34\x3a\x31\x33\x2b\ +\x30\x30\x3a\x30\x30\x9e\x6d\xd4\x5e\x00\x00\x00\x25\x74\x45\x58\ +\x74\x64\x61\x74\x65\x3a\x6d\x6f\x64\x69\x66\x79\x00\x32\x30\x32\ +\x30\x2d\x30\x34\x2d\x30\x36\x54\x30\x35\x3a\x31\x34\x3a\x31\x33\ +\x2b\x30\x30\x3a\x30\x30\xef\x30\x6c\xe2\x00\x00\x00\x19\x74\x45\ +\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\ +\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\ +\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x05\x74\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x05\x29\x1d\x18\x8e\ +\x0f\xc6\x00\x00\x04\x42\x49\x44\x41\x54\x78\xda\xed\xdd\x5b\x68\ +\x14\x67\x18\xc6\xf1\xbf\x56\x14\xa4\xed\x82\x27\x52\xb1\x78\x40\ +\x05\x73\xe3\x01\x6b\x11\x51\x49\x41\x73\xa9\x20\x42\x51\xb0\x20\ +\x88\x88\x58\xbc\x12\x11\xc4\x2b\x95\xdc\x95\xb4\x08\x2a\x28\x88\ +\x48\x4d\x8a\x17\xc5\x58\x92\xa0\xe2\x85\x85\x14\x4a\xb4\x07\xaa\ +\xa2\x89\xa2\x25\xe2\x01\x22\x18\x63\xd0\x24\xbd\x48\x53\x24\xdd\ +\xd9\x6f\x67\x9d\x7d\x9f\x77\x67\xe7\x9d\x9b\x64\xd8\x99\xef\x7b\ +\x7e\x99\x99\x9d\x73\x00\x20\xc7\x21\xfe\xa0\x9f\x61\x83\xe1\x2d\ +\xb7\x39\xca\x54\x1c\x55\x2d\xf7\x4d\xa2\xbf\x3b\xf4\xf0\x99\x3a\ +\xf6\x68\xe5\x04\xf1\x87\x19\xa6\x97\x15\xea\xe8\x23\x75\x48\x12\ +\xdf\x11\xc1\xef\x32\x00\x27\x04\x36\x9b\x3e\xc7\x04\xca\xf8\x23\ +\x04\x9f\x57\x37\x80\x9c\x40\x1d\x5f\x4e\xa0\x0e\x2f\x27\x50\x47\ +\x17\x13\x8c\x63\x38\xef\xf8\x07\xfc\x92\x60\x2b\x4b\x99\x5f\xc4\ +\xa7\x5e\xb0\x3e\xd1\x56\x8b\xac\xfc\x7f\x8f\x33\x89\xb6\xd1\xe8\ +\x77\x29\x18\x6f\xdd\x60\xc1\xca\xd1\x6a\x4d\xe0\x0b\x40\x40\xe0\ +\x0d\xc0\x9c\xc0\x1f\x80\x31\x81\x47\x00\x53\x02\x9f\x00\x86\x04\ +\x5a\x80\xa1\x00\x81\xc1\x91\xa2\x16\x60\x1b\x03\x05\x09\xda\xca\ +\xbf\x14\x68\x01\x5a\xd8\x14\x20\x28\xfb\x8a\xa0\xde\x06\xc8\x09\ +\xd4\x00\x72\x02\x3d\x80\x98\xc0\x03\x80\x94\xc0\x07\x00\xb4\xb0\ +\x85\x37\x0a\x02\x2f\x00\x70\x81\x2f\x15\x04\x7e\x00\x44\x04\x9e\ +\x00\x24\x04\xbe\x00\x04\x04\xde\x00\xcc\x09\xfc\x01\x18\x13\x78\ +\x04\x30\x25\xf0\x09\x60\x48\xe0\x15\xc0\x8c\xc0\x2f\x80\x11\x81\ +\x67\x00\x13\x02\xdf\x00\x06\x04\xde\x01\xca\x4e\xe0\x1f\xa0\xcc\ +\x04\x95\x00\x50\x56\x82\x09\xd2\x60\x1b\xe9\x2b\xfa\xb3\xcd\x6c\ +\x09\x10\x94\x74\x71\x5d\x0b\x70\x3a\xc1\x79\xe5\x68\xa3\x9e\x8e\ +\xb8\x93\x55\xc6\x2a\x50\x2c\x41\x2b\x4b\xaa\x19\x00\x72\x9c\x67\ +\x62\x35\x03\xc0\x42\x36\x57\x37\x00\xac\xf3\x08\x30\x68\x08\x50\ +\xe3\x11\xa0\xc7\x10\x60\x9c\x47\x80\x76\x43\x80\x98\x65\x03\xd0\ +\xc9\x45\x75\x50\x2d\x00\x6c\xe7\x96\x3a\xaa\x16\xe0\x29\x2b\x39\ +\x46\xbf\x3a\xee\xff\xcb\x6e\x57\xb8\x97\xdd\xec\x63\x31\x35\x7c\ +\x90\xd0\x1c\x4f\x92\xab\x24\x00\x80\x3e\x7e\x4e\x70\x6e\xdf\x26\ +\x01\x90\xbe\x1d\xa1\x0c\x20\x03\xc8\x00\x32\x80\x0c\x20\x03\xc8\ +\x00\x8a\x2d\xed\x49\xd1\x77\x6b\x22\xab\x98\x47\x3f\x1d\xdc\xb3\ +\x6d\xd8\xe2\xa1\xa9\x70\x6d\xa3\xe7\xbf\xb6\x7f\x62\x4e\x51\xd3\ +\x3c\xce\xdb\xf3\xd6\x4a\x04\x38\x3a\xa6\xf5\x27\x2c\xb0\x02\xf0\ +\xb0\x0d\x68\x60\xff\x98\x31\xd3\x39\x17\xf7\xcc\x4e\xa9\xa5\x07\ +\x68\x60\x5f\x9e\xb1\xcb\xa9\xab\x0e\x80\xfc\xf1\x01\xd6\x56\x03\ +\x40\x74\x7c\xac\xde\x33\xa3\x04\x28\x14\x1f\x1e\xa5\x1d\xa0\x70\ +\x7c\xb8\x94\x6e\x80\x50\xfc\x1f\xf8\x2d\xcd\x00\xa1\xf8\x7f\xb2\ +\xc3\xaa\x2b\x0a\x80\xc3\x81\xf8\xb7\x58\x47\x6f\x7a\x01\x0e\x73\ +\x20\x10\xff\x0b\xcb\x4b\x69\xd6\x07\x43\xe1\xf8\x75\x3c\xb6\xec\ +\x90\xed\x12\xe0\x2e\xbe\xed\x12\x10\xda\xf4\xc1\x14\xae\xc7\x98\ +\xdf\xb4\xbc\x63\x57\x17\x71\x38\x3d\xc0\x3d\x9a\x38\x37\x72\xd9\ +\xde\xea\x68\xb0\x41\xfe\x9e\x9a\xb1\x43\x07\x9f\xd8\x01\xec\x91\ +\xc7\xcd\x37\xfc\xca\x24\x9b\x6d\xc0\x14\x8e\x98\xb4\x13\xb7\x96\ +\xb1\xd3\x06\x60\x03\x1f\xaa\xb3\x46\xd4\x56\x1b\x80\x5a\x75\xce\ +\xe8\x9e\xd9\x00\x0c\xbd\xff\x2c\xca\x54\x83\x36\x00\x37\xd5\x39\ +\xa3\x7b\x66\x03\xf0\x23\xcf\xd4\x49\x23\xea\x94\x0d\xc0\x4b\xf6\ +\x44\xbc\xb4\x4d\x5b\xed\x9c\xb5\xda\x15\xfe\x9e\xbd\xee\x08\x2e\ +\xb0\x89\x41\xbb\x5d\xe1\x46\xe0\x9b\xc0\xc9\xee\xbf\xf8\x3b\xc6\ +\x1c\xd7\xe4\xbd\x31\xfa\x39\x9d\xc1\x29\xdf\xd2\x45\x13\xd7\x46\ +\x7e\xb1\xbc\x30\xb2\x93\xa1\x82\x7b\x66\x0f\x8b\x7a\xef\xe0\x68\ +\x55\xe0\x85\x91\xe3\xec\x2a\xb8\x22\xcc\xe2\x6a\x2c\x82\x04\xca\ +\xfa\x84\x88\x3b\x02\xfb\x33\x42\xce\x08\x14\xe7\x04\x8f\x07\xbe\ +\x11\x66\x71\x85\x99\x69\x06\x80\xc6\x00\xc1\xa7\x9c\x4d\x37\x40\ +\x98\xa0\x8e\x95\xe9\x06\x08\x13\xc4\x7c\xf4\xa5\xf2\x00\x42\x04\ +\x33\xd2\x0f\x50\x98\xc0\xe8\xec\xb0\xfa\xfe\x80\x68\x02\xa3\xc7\ +\x6c\xd4\x00\x51\x04\x6d\xf1\x1f\x82\xad\x54\x00\x68\xe4\xeb\x31\ +\x0f\xd6\xdd\xe5\x2b\xab\xc6\x3d\x00\xc0\x77\xd4\x73\xe3\xdf\x9f\ +\x07\x38\xc1\x0a\xbb\xeb\x43\x5e\x6e\x94\xbc\xcc\x52\xe6\xb2\x80\ +\x57\x74\xc6\x78\xa4\x3e\x45\x00\x00\xdd\x74\xdb\x37\xea\x63\x15\ +\x10\x56\x06\xa0\xee\x80\xba\x32\x00\x75\x07\xd4\x95\x01\xa8\x3b\ +\xa0\xae\x0c\x40\xdd\x01\x75\x45\xed\x09\xae\xa1\x49\xdd\xb5\x60\ +\x25\xf0\xe8\x74\x34\xc0\x6c\x66\xab\xf3\xd9\x54\xd5\xaf\x02\x19\ +\x80\xba\x03\xea\xca\x00\x78\xad\xee\x42\xc2\x15\x33\xcf\x78\xee\ +\xaa\x7b\x9c\x70\xdd\x89\x0b\xd0\xac\xee\x71\xc2\x15\x3b\xcf\xc7\ +\x74\xcb\xef\xd9\x4d\x6e\x28\xe9\xa2\xea\x22\xba\xe4\x1d\x4f\x66\ +\x68\x61\x72\x69\x8b\xcd\x47\x1c\xe4\xa6\xf8\x7f\x50\xbf\xdf\xd0\ +\xcb\x15\xb6\x96\xf2\x9d\xf6\x0f\x10\x86\xe6\xa2\x11\x01\xdd\x73\ +\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x63\x72\x65\ +\x61\x74\x65\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\ +\x35\x3a\x34\x31\x3a\x32\x39\x2b\x30\x30\x3a\x30\x30\xbd\xee\xf0\ +\x79\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x6d\x6f\ +\x64\x69\x66\x79\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\ +\x30\x35\x3a\x34\x31\x3a\x32\x39\x2b\x30\x30\x3a\x30\x30\xcc\xb3\ +\x48\xc5\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\ +\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\ +\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\ +\x42\x60\x82\ +\x00\x00\x0e\xc2\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x01\x00\x00\x00\x01\x00\x08\x04\x00\x00\x00\xf6\x7b\x60\xed\ +\x00\x00\x02\xe2\x7a\x54\x58\x74\x52\x61\x77\x20\x70\x72\x6f\x66\ +\x69\x6c\x65\x20\x74\x79\x70\x65\x20\x65\x78\x69\x66\x00\x00\x78\ +\xda\xed\x97\x5b\x92\xdc\x20\x0c\x45\xff\x59\x45\x96\x60\x49\x08\ +\x89\xe5\x60\x1e\x55\xd9\x41\x96\x9f\x0b\x7e\x4c\xcf\x2b\x99\x54\ +\xf2\x95\x6a\x53\x06\x5a\x60\x49\xbe\x07\xe8\xee\xd0\x7f\x7c\x1f\ +\xe1\x1b\x2e\xca\x89\x43\x54\xf3\x94\x53\xda\x70\xc5\x1c\x33\x17\ +\x74\x7c\x3b\xae\xbc\x6a\xda\xe2\xaa\xaf\x8b\xce\xfa\x95\x3d\xdc\ +\x5d\x46\x2b\x68\xe5\x18\xb0\x72\x3e\x55\x60\xd7\x97\x07\xae\x18\ +\xb4\xbf\xb6\x07\x3f\x47\xd8\x4f\x47\x57\xe4\xd3\xa1\xcc\xc8\x8c\ +\x4e\x7b\x4c\x12\x76\x3e\xec\x14\x4f\x47\xb9\x1f\x9d\x94\xdd\x1e\ +\x53\xdd\xf9\x68\xeb\x39\x71\xa5\x72\xde\xfb\xe9\x74\x65\xb4\x1d\ +\x9f\xc3\xa3\x21\x1a\x54\x6a\x8a\x40\xc2\xdc\x85\x64\x5b\xb5\x1f\ +\x19\xc8\x71\x17\xdc\x82\x9a\x25\x61\xde\xd5\xb7\x80\x26\xca\xf5\ +\x4a\x10\xe4\xd5\xeb\xbd\x08\xfc\x28\xd0\x87\xe2\x87\xb7\xea\x7f\ +\x26\x3e\x97\xd3\x2e\x6f\xb4\x4c\x17\xb5\xf4\xf1\x00\xe9\xc7\xe2\ +\x2f\x89\x1f\x02\xcb\x9d\x11\xbf\x1e\x30\xb9\x5c\xbd\x17\x79\x8c\ +\xe6\x63\xf4\xe3\xed\x4a\x4c\x50\x34\x9d\x2b\xea\x5e\x47\xcb\x0d\ +\x26\xee\x90\x5c\xd6\x63\x09\xc5\x70\x2b\xfa\xb6\x4a\x46\xf1\xad\ +\x6c\x15\xc8\xdb\x56\xb7\x1d\xa5\x52\x26\x06\x95\x11\x28\x52\xa3\ +\x42\x83\xfa\x6a\x2b\x55\xa4\x18\xb9\xb3\xa1\x65\xae\x2c\xcb\xe6\ +\x62\x9c\xb9\x82\x12\x01\x0e\x0a\x0d\x36\xc9\xd2\xc4\xc1\xac\x72\ +\x0f\xc0\x17\x85\xef\x5c\x68\xc5\xcd\x2b\x5e\x25\x47\xe4\x46\x98\ +\xca\x04\x67\x84\x47\x3e\x2d\xe1\x57\x83\x7f\x52\xc2\x18\x75\x4a\ +\x44\x9b\xdf\x5a\x21\x2f\x9e\xeb\x1a\x69\x4c\x72\xb3\xc6\x2c\x00\ +\xa1\x71\x72\xd3\x25\xf0\x55\xee\x4d\xbb\x3d\x80\x15\x10\xd4\x25\ +\xb3\xe3\x05\xcb\xb6\x1f\x2e\x76\xa5\x97\xb5\x25\x8b\xb3\x60\x9e\ +\xa2\x3d\xb6\x10\x05\x6b\xa7\x03\x48\x84\xd8\x8a\x64\x48\x40\x60\ +\x4b\x24\x4a\x89\x36\x63\x36\x22\xe8\xe8\x00\x54\x90\x39\x4b\xe4\ +\x1d\x04\x48\x95\x1b\x92\x64\xec\x0c\x9c\x47\xc6\xce\x33\x36\x9e\ +\x31\x5a\x73\x59\x39\xf1\x34\xe3\x6c\x02\x08\x95\x24\x06\x36\x59\ +\x0a\x60\xc5\xa8\x58\x3f\x16\x1d\x6b\xa8\xa8\x68\x54\xd5\xa4\xa6\ +\x1e\x34\x6b\x49\x92\x62\xd2\x94\x92\xa5\x79\xc8\x15\x13\x8b\xa6\ +\x96\xcc\xcc\x2d\x5b\x71\xf1\xe8\xea\xc9\xcd\xdd\xb3\x97\xcc\x59\ +\x70\x06\x6a\x4e\xd9\xb2\xe7\x9c\x4b\xe1\x50\x10\xa8\xc0\x57\xc1\ +\xfc\x02\xcb\xce\xbb\xec\x71\xd7\x3d\xed\xb6\xfb\x9e\xf7\x52\xb1\ +\x7c\x6a\xac\x5a\x53\xb5\xea\x35\xd7\xd2\xb8\x49\xc3\x31\xd1\x52\ +\xb3\xe6\x2d\xb7\xd2\x29\x74\x9c\x14\x3d\x76\xed\xa9\x5b\xf7\x9e\ +\x7b\x19\x58\x6b\x43\x46\x1c\x3a\xd2\xb0\xe1\x23\x8f\x72\x53\x3b\ +\xa9\xbe\x2b\x7f\x40\x8d\x4e\x6a\xbc\x48\xcd\x79\x76\x53\x83\x35\ +\x98\x5d\x2e\x68\x1e\x27\x3a\x99\x81\x18\x47\x02\x71\x9b\x04\xb0\ +\xa0\x79\x32\xdb\x9c\x62\xe4\x49\x6e\x32\xdb\x32\x63\x53\x28\x23\ +\x49\x9d\x6c\x42\xa3\x49\x0c\x08\x63\x27\xd6\x41\x37\xbb\x17\x72\ +\x5f\xe2\x16\xd4\xbf\xc4\x8d\x7f\x47\x2e\x4c\x74\xff\x82\x5c\x00\ +\xba\xf7\xdc\x3e\xa0\xd6\xe6\xf7\x5c\x5d\xc4\x8e\x5d\x38\x35\xdd\ +\x04\xbb\x0f\xe3\xdd\x4b\x60\x2f\xf3\x4b\xad\xfc\x6d\xfb\x74\xf4\ +\x74\xf4\x74\xf4\x74\xf4\x74\xf4\x74\xf4\x74\xf4\x1f\x38\x1a\xf8\ +\xf1\x80\x3f\xb1\xe1\x27\x78\x5f\x9e\x2f\x26\x43\x8c\x3f\x00\x00\ +\x01\x24\x69\x43\x43\x50\x49\x43\x43\x20\x70\x72\x6f\x66\x69\x6c\ +\x65\x00\x00\x28\x91\x9d\x90\xbf\x4a\xc3\x50\x14\xc6\x7f\x69\xfd\ +\x87\xe8\xa4\x38\x54\x87\x0c\xae\x05\x11\xcc\xe4\x52\x15\x82\xa0\ +\x10\x63\x05\xab\x53\x9a\xa4\x58\x4c\x62\x48\x52\x8a\x6f\xe0\x9b\ +\xe8\xc3\x74\x10\x04\x9f\xc0\x27\x50\x70\xf6\xbb\xd1\xc1\xc1\x2c\ +\x5e\x38\x7c\x3f\x0e\xe7\x7c\xdf\xbd\x17\x5a\x76\x12\xa6\xe5\xdc\ +\x0e\xa4\x59\x55\xb8\x7e\x6f\x70\x39\xb8\xb2\x17\xdf\x68\xd3\x61\ +\x9e\x5d\x36\x83\xb0\xcc\x7b\x9e\x77\x42\xe3\xf9\x7c\xc5\x32\xfa\ +\xd2\x35\x5e\xcd\x73\x7f\x9e\x85\x28\x2e\x43\xe9\x4c\x95\x85\x79\ +\x51\x81\xb5\x2f\x76\xa6\x55\x6e\x58\xc5\xfa\x6d\xdf\x3f\x14\x3f\ +\x88\xed\x28\xcd\x22\xf1\x93\x78\x3b\x4a\x23\xc3\x66\xd7\x4f\x93\ +\x49\xf8\xe3\x69\x6e\xb3\x12\x67\x17\xe7\xa6\xaf\xda\xc2\xe5\x98\ +\x53\x3c\x6c\x86\x4c\x18\x93\x50\xd1\x95\x66\xea\x1c\xe1\xb0\x27\ +\x75\x29\x08\xb8\xa7\x24\x94\x26\xc4\xea\x4d\x35\x53\x71\x23\x2a\ +\xe5\xe4\x72\x20\xea\x8b\x74\x9b\x86\xbc\x4e\x9d\xe7\x29\x65\x28\ +\x8f\xb1\xbc\x4c\xc2\x1d\xa9\x3c\x4d\x1e\xe6\x7f\xbf\xd7\x3e\xce\ +\xea\x4d\x6b\x63\x96\x07\x45\x50\xb7\xda\xaa\xd6\x68\x04\xef\x8f\ +\xb0\x3a\x80\xb5\x67\x58\xbe\x6e\xc8\x5a\xfa\xfd\xb6\x86\x19\xa7\ +\x9e\xf9\xe7\x1b\xbf\x00\x04\xb3\x50\x73\x3e\x89\xe7\x7d\x00\x00\ +\x00\x02\x62\x4b\x47\x44\x00\xff\x87\x8f\xcc\xbf\x00\x00\x00\x09\ +\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\ +\x1b\x00\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x06\x1b\x2b\ +\x38\x02\x70\x77\x00\x00\x0a\x35\x49\x44\x41\x54\x78\xda\xed\x9d\ +\x6d\x70\x54\x57\x19\xc7\x7f\x9b\x17\xf2\xd2\x26\x94\x62\x02\x14\ +\x02\xc8\xeb\x18\x68\x79\x11\xb1\xc5\x51\xdb\xd8\x4e\xb5\x75\x10\ +\x83\xed\x08\x5f\xad\xcc\x38\xa2\x56\x3f\x76\x6a\xa7\xd3\x8e\x5f\ +\x74\x54\x6a\x84\x8c\x7e\x71\x14\x6b\x99\x4e\xb5\xed\x58\xd0\xa9\ +\x40\x47\x07\x69\x3f\x38\x43\xb0\x38\x69\x6a\x9a\x86\x24\x40\xd8\ +\x98\x74\x4a\x20\x21\x6f\x1b\x3f\x31\x93\x92\xcd\xee\xbd\x7b\xf7\ +\xde\x7b\xee\x39\xff\xdf\x7e\xcb\xee\xde\x73\xf6\x3c\xff\x3c\xf7\ +\xfc\x9f\x7b\xee\xb9\x60\x2b\x5b\x68\xa5\x83\x31\xa6\x03\xbe\xc6\ +\xe8\xe0\x10\x9b\x11\x09\xa2\x8a\x5f\x93\x09\x1c\xfa\x99\xaf\x0c\ +\x87\x98\xa7\x81\x4d\x4a\xf8\xff\x5e\xd4\xe0\xdf\x78\x1d\x97\x04\ +\x92\xc1\xaf\x42\x09\xff\x34\xd3\xb4\x44\xfa\x3b\xca\x29\x57\x30\ +\x0b\x39\xf7\x67\x42\x13\xc0\x24\x1b\x23\xfa\x15\x0f\xf2\x1a\x9d\ +\x74\xf2\x1a\x0f\x2a\xa4\xfe\x68\x0d\x2d\xfc\xd3\x4c\x73\x28\x92\ +\xdf\xf0\xcc\x47\xda\x7c\x46\x41\xf5\x43\x47\xa8\x02\x78\x27\x82\ +\x5f\xb0\x6b\x56\xab\xcd\x0a\xab\x77\xc6\x42\x15\xc0\xf5\xd0\xfb\ +\x5f\x9a\x45\xc2\xef\x52\xa6\xc0\x7a\x65\x3a\xe4\x57\xd8\xdc\x9f\ +\x65\x0e\x93\xe1\x81\xb0\x9a\x2b\x91\x62\x0c\x63\x1f\xa9\x59\x7f\ +\x4b\xb1\x4f\x03\xe3\x46\x06\x58\x3d\x67\xbb\x6b\x94\x01\x5c\xe0\ +\xbb\x05\xbc\x23\xac\xc9\x00\xd5\x5c\x9e\xb3\xdd\x34\xd5\xca\x00\ +\xb6\xb3\x87\xfa\x39\xdf\xab\x63\xaf\x06\xc8\xee\x0c\x50\xc2\x3b\ +\x39\x5b\xee\xa0\x54\xe1\xb5\x59\x00\x4d\x79\x8a\xd8\x19\xbe\xa0\ +\x53\x80\xcd\x7c\x33\x8b\x01\x94\x19\x74\x26\x03\xac\xf0\xd4\xfa\ +\xc7\x95\x01\x6c\xe5\x7b\x01\x6d\x62\x81\xa4\x2c\xcc\x00\xe1\x12\ +\xce\x88\x55\xd2\xcd\x22\x0f\x9f\x4b\xb3\xa2\xb8\xd7\x23\x94\x01\ +\xcc\xe0\xeb\x9e\xc2\x0f\xf5\x32\x83\x36\xce\x01\x52\xb4\xfb\xb8\ +\x20\x9d\x52\x90\x6d\x13\xc0\xbd\x3e\x56\x31\x65\xb8\x4f\xa7\x00\ +\xdb\x78\xcc\xc7\x7f\x75\x8a\xc7\x34\x09\xb4\x6b\x12\xd8\xc0\x79\ +\x9f\x47\x5d\x41\x8f\x32\x80\x4d\x06\x30\xe5\xfb\x1b\xca\x00\xd6\ +\x64\x80\x4a\xba\x58\xe2\xf3\x3b\x97\x58\xcd\xa8\x32\x80\x1d\x3c\ +\xea\x3b\xfc\xb0\x84\x47\x35\x70\x76\xb8\x80\x14\xe7\x0a\xea\xc5\ +\x39\x99\x41\x3b\x04\xf0\x39\xa6\x0a\xea\xc5\x14\x9f\xd7\x29\xc0\ +\x06\xbe\x51\x60\x04\x4a\x8a\x65\x06\x35\x09\x8c\x73\xc4\x96\x72\ +\xbe\xe0\x45\x1e\x19\x56\xd2\xab\x0c\x90\x54\xca\xa8\x63\x3d\x3f\ +\x0c\xb0\xc6\xa7\x84\x27\x59\xcf\xa2\xa0\x77\x2c\x2b\x03\x84\x3b\ +\x62\xa5\x94\x51\xc6\x3c\x96\xb0\x8c\xe5\x2c\x66\x21\xcb\x58\xce\ +\x32\x6a\x29\xa3\x2c\xf0\xfd\x3e\x93\x4c\x32\xc9\x08\x7d\xf4\xd0\ +\xc7\x00\x69\x7a\xe8\xe3\x02\x63\x4c\x30\xc9\x94\x04\x10\xb5\x00\ +\xca\xb8\x83\x06\x1a\x58\xcc\x02\xea\xa9\xe3\x63\x2c\x60\x01\xb7\ +\x73\x4b\xc4\x63\x30\xca\x07\x7c\xc0\x10\x83\x0c\x90\x66\x88\x34\ +\xbd\xf4\xd2\xc7\xb8\x04\x50\x7c\x01\x2c\xe4\x11\xb6\xb2\x92\xa5\ +\x34\x50\x63\xf8\xd8\x8c\xd2\xcb\x79\xba\x39\xc3\x8b\x0c\xca\x06\ +\x16\xc3\x06\x6e\xa7\x3f\xf4\x16\xc3\x78\x5d\xe6\x1e\x65\x80\xe0\ +\x19\x60\x15\x67\xa8\x4d\xe8\x38\x5d\x61\x0b\x5d\x72\x01\xc1\xf8\ +\x7e\x62\xc3\x0f\xb5\xfc\x40\x19\x20\x68\x06\xe8\xa1\x21\xc1\x23\ +\xd5\xcb\x72\x09\x20\x98\x00\x26\x12\xbd\x71\xc3\x38\x15\x3a\x05\ +\x04\xa3\x33\xd1\xbd\x7f\x0f\x24\x80\x60\x1c\x49\x7e\xef\x75\x0a\ +\x08\x72\x0a\x58\xc8\xbf\x58\x99\xd0\x71\xea\x66\x1b\x83\xca\x00\ +\xc1\x18\xa4\x89\x8e\x44\xf6\xfc\x5d\x9a\x6c\x2d\x05\x45\xbd\x1e\ +\x60\x3e\xff\x48\x5c\x19\xe8\x14\xf3\xed\xfd\xaf\x8c\x7e\x41\x48\ +\x35\x87\x13\x15\xfe\xdf\x87\xb3\xd7\x88\xbb\x02\x00\xf8\x71\x62\ +\xc2\xff\x13\xdb\xcf\xcb\x71\x2d\x09\xfb\x16\x13\xc6\x07\x7f\x82\ +\x6f\xdb\x3f\x31\x8b\x6f\x4d\xe0\x4e\x86\x8d\x0e\xff\x55\x76\xb9\ +\x30\x33\x8f\x73\x51\xe8\x3d\x5c\x32\x36\xfc\x97\x6e\x5c\xff\x93\ +\x00\xc2\x5c\x15\xbc\x8a\xf7\x8c\x0c\x7f\x17\xab\x5d\xf1\xe6\x71\ +\x2f\x0b\xaf\xe3\x94\x71\xe1\x3f\x9d\x63\xfb\x39\x09\xa0\xe8\xf7\ +\x05\x54\xf0\x92\x51\xe1\xff\x23\x15\x2e\x55\xe7\x4c\xb8\x31\xa4\ +\x84\x03\xc6\x84\xff\x39\xd7\xaa\xbd\xa6\xdc\x19\xf4\x1d\x23\xc2\ +\xef\xe0\x0e\xc3\xe6\xdc\x1a\xd6\xcc\xd5\x58\x83\x7f\x8d\xdd\x2e\ +\x5e\xa0\x31\xe9\xde\xc0\x6d\xa4\x63\x0b\xff\x00\xdb\xdd\xbc\x42\ +\x67\xd6\xcd\xa1\x6b\x7d\x6c\xff\x54\xcc\x57\x3b\xeb\x5c\xbd\x44\ +\x6b\xda\xdd\xc1\x8b\x78\x33\xf2\xf0\xbf\xc5\x62\x90\x00\x4c\xb9\ +\x3d\xbc\x92\x97\x23\x0d\xff\xab\x54\x81\x04\x60\xd2\xfe\x00\xa5\ +\xfc\x2c\xb2\xf0\x1f\x70\x7d\x53\x79\x53\x37\x88\x78\x2e\x92\xf0\ +\xff\xc2\x6f\xb7\xb4\x24\x2c\x2a\x86\x23\x69\xe5\x8a\x04\x60\x2a\ +\xf7\x99\xd9\x8a\x56\x05\x47\x33\x62\xd5\xa4\x23\xb9\x49\xfc\x1a\ +\xf5\x8c\x28\x03\x98\xc7\x86\x88\x66\xe6\x55\x6c\xd0\x29\xc0\x44\ +\xb6\x46\x34\xd2\x25\x6c\x95\x00\x4c\x64\x9b\xa9\x2d\x69\x0e\x10\ +\xcd\x1c\xe0\xfd\xc8\xee\x20\xea\xf6\xf7\x5c\x21\x09\x20\x0a\x01\ +\xdc\xc1\x85\x08\x47\x60\x29\x17\x75\x0a\x30\x8b\x07\xcc\x6d\x4d\ +\x02\x88\x82\x4f\x99\xdb\x9a\x04\x10\x3e\x29\xbf\x33\xf3\xc0\x8e\ +\x23\xe5\xa7\x73\x9a\x03\x84\x3d\x07\xa8\xa6\x3f\xd2\x0d\xe4\x86\ +\x59\xec\xbd\x18\xa4\x0c\x10\x3e\x8d\x11\xef\x1f\x58\x43\xa3\x4e\ +\x01\x26\xd1\x64\x72\x8b\x12\x40\xf8\xdc\x6b\x72\x8b\x9a\x03\x84\ +\x3d\x07\x98\xc7\x65\x6e\x8b\x78\x0c\x3e\xa4\x3e\xdb\xbe\xc0\xca\ +\x00\x71\xb0\x2e\x86\x1d\x84\x6f\xf5\xbe\x24\x54\x02\x08\x9b\xcd\ +\x31\x2c\xd1\x2a\x65\xb3\x04\x60\x0a\x9f\x2c\xf0\x7b\x69\xbe\x46\ +\x33\xfd\x05\xd7\x02\x9c\xc5\xb4\x35\x81\x6d\x05\xb4\x91\xe1\x47\ +\x2c\x00\xe0\x36\x9e\x65\xb2\x80\x23\xb4\x49\x00\x66\x08\xa0\xb6\ +\x80\x16\xfe\x76\xd3\xa2\x8e\x46\x5e\x2f\xe0\x28\xb5\x12\x80\x09\ +\x02\x68\xf6\x79\xf4\x8b\x34\x67\x39\x2d\x97\xb0\x8b\x0b\x3e\x8f\ +\xd4\xac\x39\x80\x09\xf8\xb9\x3f\x6f\x82\x67\xf9\x04\x7f\x22\x33\ +\xeb\x9d\x0c\xaf\xd0\xc8\xd3\x4c\x84\xd4\xb2\x32\x40\x68\x19\xe0\ +\xb8\xe7\xe3\xbe\xce\xc6\xbc\x47\xdb\xc0\x5f\x3d\x1f\xef\xb8\x04\ +\x10\xbf\x00\x2a\x3d\x3e\x50\xa6\x97\xaf\x78\xcc\xc5\x29\x76\xd2\ +\xe3\xe9\x98\xfd\x54\x4a\x00\x71\x0b\x60\x9d\xa7\xbd\xfb\x9e\xf6\ +\xb9\x71\x6b\x2d\x4f\x79\xda\x93\x70\x9d\x04\x10\xb7\x00\xf6\xe7\ +\x3d\xda\x5f\x58\x5f\xd0\xaf\x5c\xcb\xb1\xbc\xc7\xde\x2f\x01\xc4\ +\x2d\x80\x96\x9c\x47\xea\xe3\xe1\x00\x93\xf0\x12\x1e\xa2\x37\xe7\ +\xf1\x5b\xe4\x02\xe2\x66\x22\xc7\x3b\x4f\xd1\xc8\xd1\x2c\x33\x7e\ +\xaf\x64\x38\xc6\x06\x9e\xcc\x71\xd1\x67\x5c\x19\x20\xee\x0c\xb0\ +\x77\x8e\x63\x1c\x65\x6d\xd1\x7e\xef\x1a\xfe\x3c\x47\x2b\x7b\x24\ +\x80\xf8\x5d\xc0\xec\x7d\x43\x7b\xf8\x72\x91\xf3\x6e\x8a\x87\xe9\ +\xce\xb2\x37\xa8\x5c\x80\x01\x75\x80\xed\x0c\xcd\xf8\xee\x28\x4f\ +\x84\x74\x71\xf8\x56\x9e\x60\x74\x46\x4b\x43\x2a\x04\x99\x72\x31\ +\x68\x35\x2f\x70\x91\xeb\xf4\x71\xb8\x88\x89\x3f\xbb\x33\x38\x4c\ +\x1f\xa3\xf4\xf1\xbc\xf7\x9d\x81\xb5\x22\xc8\x7f\xc2\xf5\x4f\x05\ +\xe5\x4c\x30\x16\xc1\xaf\xf7\xdd\x92\x04\x10\x85\x00\x0c\x46\x36\ +\xd0\x71\x24\x00\x09\x40\x48\x00\x42\x02\x10\x12\x80\x90\x00\x84\ +\x04\x20\x24\x00\x21\x01\x08\x09\x40\x48\x00\x42\x02\x10\x12\x80\ +\x90\x00\x22\xe0\x2e\x7e\x49\x3b\x23\xb1\x3d\xd1\xc3\x2b\x41\xfb\ +\x37\x42\x3b\x07\xd9\x24\xe9\xcd\xa4\x8a\x43\x4c\x19\xfb\xe0\xf5\ +\x30\x5e\x53\xb4\xba\xfd\x68\xa7\x8f\x86\xff\x84\x53\xc1\xbf\xf1\ +\x7a\xc3\x04\x09\x98\xf0\x84\xa9\x16\x1e\x71\x52\xf8\x2b\xb9\x9d\ +\xa3\x71\x77\x22\xfe\x05\x4e\x77\xd2\xe6\xec\x54\x34\xc3\x66\xde\ +\x76\x7d\x12\xb8\xcf\x61\x27\x52\xc2\x3e\xb9\x80\xfb\x9d\x9e\xff\ +\x34\xc5\xdd\x81\xf8\x4f\x01\xa3\xae\xde\xc1\x02\xc0\x48\x24\xcf\ +\x12\x53\x21\x48\x98\x2b\x80\xf7\x9d\x1e\xff\x6e\x09\xe0\x84\xd3\ +\x02\x78\x43\x73\x80\x8d\x9c\x75\xd8\x06\x6e\xe1\xdf\xf1\x76\x21\ +\xfe\x42\x50\x9a\x3a\x67\xef\x64\x6d\xe5\x37\x9a\x85\x40\x25\xff\ +\x74\xb2\x14\x7c\xda\x04\xff\x63\xc6\xad\x8e\x0b\x39\xed\xdc\x9e\ +\x56\x9d\xec\x60\x40\x02\xb8\xc1\x1a\x4e\x53\xe7\x50\xf8\x07\xb9\ +\x9b\x4e\xd9\xc0\x99\xff\x0f\x3b\xfd\x3d\xf6\x3c\xd1\x8c\xb2\xd3\ +\x8c\xf0\x9b\xc5\xae\x82\x36\x46\x4f\xe2\x5a\x80\xdd\x0a\x76\x76\ +\xf6\x3b\x21\x80\xc7\x15\xe8\xb9\xf9\xb9\xf5\xe1\x3f\xa0\x20\xe7\ +\x9e\x93\xbc\x64\x75\xf8\x5f\x36\x62\x09\x8e\xd1\x54\x71\xca\xda\ +\xf0\xbf\x49\xb5\x02\xec\xa5\x2a\xd0\x61\x65\xf8\xff\xeb\x94\xd1\ +\x0d\x58\x15\x48\x5b\x17\xfe\xff\xb1\x46\x81\xf5\xce\xdd\x5c\xb3\ +\x2a\xfc\x23\xec\x50\x50\xdd\xad\x0a\xc8\xf9\x3b\x5e\x15\x90\xf3\ +\x77\xba\x2a\x20\xe7\xef\x74\x55\x40\xce\xdf\xe9\xaa\x80\x9c\xbf\ +\xd3\x55\x01\x39\x7f\xa7\xab\x02\x72\xfe\x4e\x57\x05\xe4\xfc\x9d\ +\xae\x0a\xc8\xf9\x3b\x5e\x15\x90\xf3\x77\xba\x2a\x20\xe7\xef\x74\ +\x55\x40\xce\xdf\xe9\xaa\x80\x9c\xbf\xd3\x55\x01\x39\x7f\xa7\xab\ +\x02\x72\xfe\x4e\x57\x05\xe4\xfc\x9d\xae\x0a\xc8\xf9\x3b\x5e\x15\ +\x90\xf3\x77\xba\x2a\x20\xe7\xef\x74\x55\x40\xce\xdf\xe9\xaa\x80\ +\x9c\xbf\xd3\x55\x01\x39\x7f\xa7\xab\x02\x72\xfe\x4e\x57\x05\xe4\ +\xfc\x9d\xae\x0a\xc8\xf9\x3b\x5e\x15\x90\xf3\x77\xba\x2a\x20\xe7\ +\xef\x74\x55\x40\xce\xdf\xe9\xaa\x80\x9c\xbf\xd3\x55\x01\x39\x7f\ +\xa7\xab\x02\x72\xfe\x4e\x57\x05\xe4\xfc\x9d\xae\x0a\xc8\xf9\x3b\ +\x5e\x15\x90\xf3\x77\xba\x2a\x20\xe7\xef\x74\x55\x40\xce\xdf\xe9\ +\xaa\x80\x9c\xbf\xd3\x55\x01\x39\x7f\xa7\xab\x02\x8e\x39\xff\xa0\ +\xc3\x65\x5b\x55\xc0\x34\xe7\x1f\x7a\x7c\x6c\x12\x40\xf0\xaa\x80\ +\x79\xce\x5f\x02\x88\xb4\x2a\xf0\xb8\x7b\x19\xda\x36\x01\x04\xa9\ +\x0a\x98\xe8\xfc\x25\x80\xc8\xaa\x02\x66\x3a\x7f\x09\x20\xa2\xaa\ +\x80\xa9\xce\x5f\x02\x88\xa4\x2a\x60\xae\xf3\x97\x00\x22\xa8\x0a\ +\x98\xec\xfc\x25\x80\xd0\xab\x02\x66\x5f\xf3\x97\x00\x42\xae\x0a\ +\x98\x7e\xcd\x5f\x02\x08\xb9\x2a\x60\xfa\x35\x7f\x09\x20\xd4\xaa\ +\x80\xf9\xd7\xfc\x25\x80\x10\xab\x02\x49\xb8\xe6\x2f\x01\x14\xa1\ +\x2a\x70\x32\x6b\xcf\x4f\x50\x95\x80\xde\x4b\x00\x45\xa0\x92\x56\ +\x32\x37\x4d\xfd\x0e\x52\x91\x88\xbe\x4b\x00\x45\x62\x13\x07\x69\ +\xe7\x3a\xd7\x69\xa7\x85\x3b\x13\xd3\xef\x22\xc7\x27\x95\xa5\x81\ +\x60\xa4\x10\xe1\x0a\xa0\xa8\xf1\x29\xd1\x88\xba\x8d\x04\x20\x01\ +\x08\x09\x40\x48\x00\x42\x02\x10\x12\x80\x90\x00\x84\x04\x20\x24\ +\x00\x21\x01\x08\x09\x40\x48\x00\x42\x02\x10\x12\x80\x90\x00\x84\ +\x04\x20\x24\x00\x21\x01\x08\x09\x40\x48\x00\x42\x02\x10\x12\x80\ +\x90\x00\x84\x04\x20\x24\x00\x21\x01\x08\x09\x40\x48\x00\x42\x02\ +\x10\x12\x80\x48\x0a\x65\x45\x3f\xe2\xb4\x06\x55\x19\x40\x48\x00\ +\x42\x02\x10\x12\x80\x90\x00\x84\x04\x20\x24\x00\x21\x01\x08\x09\ +\x40\x48\x00\x42\x02\x10\x12\x80\x90\x00\x84\x04\x20\x24\x00\x61\ +\x9c\x00\xc6\x35\x28\x16\x33\x96\x5f\x00\x57\x34\x4a\x16\x73\x25\ +\xbf\x00\xba\x34\x4a\x16\xd3\x95\x5f\x00\x67\x35\x4a\x16\x73\x36\ +\xbf\x00\x4e\x6a\x94\x2c\x66\x56\x74\x67\x3f\xe2\xa9\x86\x4b\xdc\ +\xa2\x91\xb2\x92\x11\x16\x71\x35\x5f\x06\x18\xe6\x88\x46\xca\x52\ +\xfe\x70\x73\xf8\xb3\x3f\xe4\x6d\x2d\xff\xa1\x5c\xa3\x65\x1d\x13\ +\x34\xd2\x79\xf3\x1f\xb3\x3d\x2b\x77\x88\x1a\x3e\xa3\xf1\xb2\x8e\ +\x9f\xf2\xe2\xec\x3f\x66\x7f\xcc\x63\x15\x6f\x71\x97\x46\xcc\x2a\ +\xce\xf1\x69\x46\x66\xff\x39\x7b\x29\x78\x94\xdd\xf4\x6b\xcc\x2c\ +\x22\xcd\x57\xb3\x85\x7f\xee\x6b\x01\x9d\x7c\x49\x12\xb0\x86\x01\ +\xbe\x38\xfb\xec\x9f\x5b\x00\xd0\xc6\x67\x69\xd3\xd8\x59\xc0\xdb\ +\xec\xe0\xcc\x5c\x6f\x96\xe6\xf8\xe2\x10\xbf\xa3\x9c\xed\x39\x3f\ +\x23\xcc\x66\x92\x03\xec\x25\x3d\xf7\x07\x4a\xf3\x7c\xfd\x38\x2f\ +\x50\xcd\x7a\xe6\x69\x2c\x13\xc7\x08\xbf\x65\x0f\x47\x98\xcc\xf5\ +\x21\x6f\x0f\x7b\xaf\xe1\x21\x9a\xd8\xc4\x2a\xe6\x4b\x0a\x86\x33\ +\xce\x87\x74\x71\x96\x93\x1c\x63\x38\xff\xc7\xff\x0f\x61\x64\x99\ +\x8b\xff\x8b\x93\x3e\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\ +\x82\ +\x00\x00\x09\xe1\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x06\x38\x19\x4e\x7c\ +\x56\x96\x00\x00\x08\xaf\x49\x44\x41\x54\x78\xda\xdd\x9d\x6b\x70\ +\x55\xd5\x15\xc7\x7f\xf7\x02\x49\xe0\x06\x35\xe0\x15\x14\x09\xa1\ +\x48\x82\x84\xd1\x2f\x9d\x56\x1d\xc7\x02\xe3\xa3\xe0\x03\x11\xc5\ +\xc1\x8a\x33\x0a\xa6\x0a\x76\xf8\xe0\x8c\xe9\x80\x3a\x8e\x8e\x33\ +\x0e\x8f\x52\x5a\x45\x6b\xa9\x1f\x7c\x52\x11\x4a\x6b\xa5\x50\x1f\ +\x15\x90\x41\x47\x41\x14\x52\x09\x4a\xb4\xa8\x08\x02\x09\x90\x44\ +\x42\x42\xee\xf1\x43\xb8\x24\x81\xb5\xcf\x73\x9f\xbd\x0f\xfd\xef\ +\x6f\xfb\x9e\xbb\xd6\xfa\xaf\xbb\x1f\x6b\xef\x7d\xf6\xba\x29\xcc\ +\xa2\x80\x9f\x52\x49\x05\x15\x0c\x21\x43\x09\x19\xa0\x99\x06\x9a\ +\xf9\x1f\xdb\xd9\x41\x0d\x1f\xd2\x66\xd8\x26\x43\x18\x41\x35\x6b\ +\x68\xc2\xf1\x28\x4d\xac\xe6\x01\xca\x6d\x9b\xab\x13\xfd\x98\xc9\ +\xfb\x9e\xc4\x4f\x2e\x1b\x99\x41\x89\x6d\xd3\xa3\x63\x00\x8f\x70\ +\x28\x30\xf9\xce\xd6\xb0\x88\xf3\x6d\x53\x08\x8f\xb3\x59\x4c\x4b\ +\x68\xf2\xf9\xd2\xc2\x93\xf4\xb7\x4d\x25\x38\x52\xdc\xc1\xf7\x91\ +\xc9\xe7\x4b\x3d\xb3\x48\xdb\xa6\x14\x04\x65\x6c\xd0\x46\x3e\x5f\ +\xd6\x33\xc4\x36\x2d\xbf\x98\xc0\x01\xed\xf4\x1d\x1c\x0e\x72\x8b\ +\x6d\x6a\xde\x48\xb3\x80\x5c\x2c\xf4\x1d\x1c\x72\xcc\x4d\x76\x57\ +\x28\xe0\x95\xd8\xc8\xe7\xcb\x4b\xf4\xd2\x69\x72\x4a\xa3\xac\x0c\ +\x2b\xb8\xda\xf3\x29\x87\xad\xac\xa5\x86\x5a\x76\xd1\x40\x33\x90\ +\xa1\x1f\xa5\x94\x53\xc9\x68\x46\xf9\xb0\x68\x0d\x93\x68\xd6\xe9\ +\x04\x3d\x28\x60\x8d\x67\x03\x7e\x97\xbb\xc8\xba\x4a\xc9\x32\x8d\ +\xb5\x9e\x9d\x68\xb5\xde\x56\xa0\x03\x69\x8f\xc6\xdf\xc2\x9f\x18\ +\xe6\x5b\xda\x05\xfc\x99\xa3\x1e\x1d\x21\x61\x63\xc1\x02\xd7\x5f\ +\xfe\x05\x06\x05\x96\x38\x98\x97\x5d\x5d\x30\xd7\x36\xe5\xae\x98\ +\xe8\xd2\x68\x3f\x67\x74\x68\xb9\x63\xd9\xe9\xe2\xd6\x09\xb6\x69\ +\xe7\x51\xea\x32\xef\xaf\xe0\xac\x48\xb2\xcf\x60\xa9\x52\x76\x3d\ +\x65\xb6\xa9\x03\xa4\x95\x51\x5f\x8e\x6a\x2d\x1a\x66\x2b\xdb\xd7\ +\x3a\xad\xb3\x58\x48\xdc\xad\x30\xee\x18\x77\x6a\xd3\x31\x8d\x63\ +\x0a\x2d\x77\xd9\xa6\xdf\x9f\x7d\x8a\x5f\x7f\x9a\x56\x3d\x53\x15\ +\xad\x60\x3f\x67\xdb\x75\xc0\x62\xc5\x2f\xa3\xa7\xf1\x77\xc5\x1c\ +\x85\xa6\x3f\xda\xa4\x7f\x2e\x47\x44\xa3\x5e\x8d\x45\xdb\x6b\x8a\ +\x08\xe3\x3c\x7b\x0e\xf8\x9d\x62\xe2\x3b\x23\x16\x6d\x67\x51\x27\ +\xea\x9b\x6f\x8b\x7e\x3f\x71\x8b\x33\x17\x61\xde\xf7\xc2\x95\xa2\ +\x03\x1a\x6d\xed\x1d\xce\x14\xcd\x79\x3e\x56\x9d\x72\x54\x70\x8f\ +\x1d\x07\x48\x3b\xbd\x2d\x21\x82\xde\x20\x28\x15\xd7\x08\x1b\x6c\ +\xd0\x2f\x17\x7f\x8b\x67\x62\xd7\xbb\x44\xd4\x6b\xe1\x1c\xa1\x5a\ +\xec\xff\xfe\x57\x7c\x61\x31\x5c\x8c\x08\xee\x37\xef\x00\x69\xf5\ +\xff\x1f\x23\x9a\xd7\x0b\x9a\xdf\x30\x4d\xbf\x40\x9c\x01\xcc\x04\ +\xa6\x55\xe2\x4c\x60\x78\x8b\xe4\x32\xb1\x03\x64\xa3\x0b\xf6\x81\ +\x01\x62\x27\xb8\x24\x9c\xb0\xb0\xfb\x2a\x95\x42\xdd\xa7\xec\x33\ +\xe2\x80\xbd\xfc\x57\xa8\x1d\x69\xd6\x01\x15\x42\xdd\x3a\x23\xf4\ +\x01\xde\x15\xea\x46\x98\x75\x80\xa4\x6e\x9b\x31\x07\xd4\x08\x75\ +\x15\x81\xa5\x44\x72\x40\xa9\x50\xb7\xc3\x98\x03\x6a\x85\xba\x90\ +\x07\x67\x61\x1d\xd0\x57\xa8\xfb\xca\x98\x03\x24\x4d\x21\x17\x60\ +\x61\x1d\x20\xa9\x3b\x6c\xcc\x01\x92\xa6\xbe\x81\xa5\x44\x82\x14\ +\x91\x17\x18\xd3\x5e\x28\xae\x42\x42\x21\x6c\x0b\xc8\x19\x23\xeb\ +\x17\x3d\xcc\x3a\xe0\xa0\x50\x57\x6c\x8c\xac\xd4\xdc\x8f\xd8\x77\ +\xc0\x99\xc6\x1c\x20\x8d\x40\x4d\xf6\x1d\x50\x1a\x54\x48\x68\x94\ +\x09\x75\x7b\xcc\x3a\xa0\x41\xa8\x0b\x19\x8b\x85\x80\x14\xf4\x18\ +\x76\xc0\x67\x42\x5d\x65\x60\x29\x61\x31\x4a\xa8\xab\x09\x2c\x25\ +\x92\x03\x3e\x16\xea\x7e\x61\xcc\x01\x92\xa6\xcd\xc6\xb4\x03\x30\ +\x4a\x5c\x0e\x0f\x30\xa2\x5b\x5e\x0e\x5f\x68\xd6\x01\x3d\xf9\x41\ +\x30\x42\xef\x71\x98\x0a\xbf\x16\x34\x37\x87\x8d\x03\xc2\xe3\x1d\ +\xc1\x8c\xb5\x46\x34\x4b\x5b\x62\x6f\x99\xa6\x0f\xf7\x89\x9d\xe0\ +\x82\xd8\xf5\x96\x8b\x1d\x60\x86\x79\x07\x0c\x12\x0d\x79\x36\x76\ +\xbd\xcf\x09\x5a\xdb\xed\x9c\x0f\x6e\x14\x4c\x39\xca\xe0\x58\x75\ +\x96\xd1\x2a\x68\x7d\xcf\x06\x7d\x98\x25\x1e\x51\xbc\x1c\xab\xce\ +\x65\xa2\xce\xdf\xd8\x71\x40\x86\xfd\xa2\x39\x63\x63\xd3\x78\x95\ +\xa8\xef\x80\xc1\x65\xd8\x49\x78\x5c\x34\x68\x67\x4c\xcb\xa2\x12\ +\xbe\x14\xf5\x3d\x66\x8b\x3e\x9c\x23\x46\x03\x0e\xcb\x62\xd0\x95\ +\x62\x85\xa8\xeb\x08\x03\xed\x39\x00\xfe\x20\x1a\xe5\x30\x5b\xbb\ +\xa6\x87\x15\x9a\x16\xd9\xa4\x0f\x67\xb2\x5b\x34\x2b\xc7\x74\xad\ +\x7a\xaa\x14\xf4\xf7\x44\x7c\x0f\x51\x03\x7e\xa5\x30\xed\x98\x46\ +\x17\x54\x29\x5f\x93\x9b\x62\x9b\x3e\xc0\x5b\x0a\xe3\x72\xcc\xd1\ +\x20\x3d\xa5\x6c\xfc\x0e\x6b\x6c\x53\xef\xc0\x4f\xa8\x57\x9a\xb8\ +\x3c\x62\x13\x2d\xe1\x6f\x4a\xd9\x07\x18\x6a\x9b\x7a\x1e\xe3\x68\ +\x57\x9a\x59\xc7\x95\xa1\xe5\x5e\xad\x98\xf8\x3a\xba\xd8\x35\xb6\ +\x69\x77\xc5\x1c\xa5\xa1\x0e\x0e\x4b\x43\xec\x17\x96\x29\xa2\xbe\ +\x7c\xf9\xad\x6d\xca\xdd\x91\xf2\x30\xf7\x28\x4b\x18\xee\x5b\x5a\ +\x39\xcf\x89\x31\x7f\x67\xf9\x6b\x12\x5e\x93\xee\x8e\x02\x56\xba\ +\x9a\xec\x90\x63\x1d\x77\x7b\xec\x1a\x0d\xa4\x8a\xf5\x9e\x57\x66\ +\x56\xe8\x3b\x85\xd2\xe9\xc7\x02\x96\x32\xd1\xf3\x29\x87\x1a\xd6\ +\xb2\x8d\x1d\xec\xa2\x81\x46\xa0\x2f\x25\x94\x52\x41\x25\xa3\x7d\ +\x6d\xac\x2e\x67\x4a\x52\xaf\xd8\xf7\xe2\x55\x8f\xdf\x2e\x7a\x59\ +\x4a\x4f\xdb\x34\xdd\x90\xa2\x5a\x19\xb4\x44\x2f\x39\x9e\x48\xda\ +\x65\x29\x09\x63\xd8\x1b\x0b\xfd\xfd\xc9\x9a\xf8\xd4\xe8\xc9\xcc\ +\x18\xae\xcf\xe6\x98\x99\xec\xc6\xdf\x81\x11\xcc\xe3\xbb\xd8\xba\ +\xc0\x6e\xe6\x86\x7d\x17\xc8\x04\x2e\xe7\xf5\x18\x2f\x4e\x77\xb6\ +\x84\x37\xb9\x3e\x69\x51\x40\x8a\x9b\xf8\x24\x76\xea\x5d\xcb\x16\ +\x26\x26\xc7\x09\xd7\xb1\xd9\x28\xf9\x7c\xf9\x88\xf1\xb6\xa9\x43\ +\x39\xab\xac\x90\xcf\x97\xb7\x0d\x9e\x49\x9f\x82\x62\xe6\x7b\x44\ +\xec\x26\x4a\x2b\xf3\xc9\x84\x27\x11\xbe\x1f\x8d\x65\x49\xc0\xf5\ +\xb8\xc3\xd7\xec\xa0\x96\xed\xd4\xd1\x40\x33\x4d\x27\xf2\x07\x94\ +\x50\x4c\x86\x12\x86\x31\x82\x72\xca\x19\x1c\xd0\xae\x3a\xa6\x1b\ +\x7a\x55\xff\x38\xfa\xf0\x74\x80\xf1\xbe\x95\xf7\x78\x94\x31\x01\ +\x7e\xa7\x0c\x63\x79\x8c\x0d\xb4\xf9\xd6\x91\xe3\x29\xfa\x98\xa2\ +\x3f\x92\x6d\x3e\xcd\xaa\x67\x31\xe3\x22\x1c\x5b\x14\x33\x9e\x67\ +\x5c\xf6\x9b\xba\x97\xad\x66\xde\x11\x98\xea\x23\x1f\x98\x43\x2b\ +\x7f\x67\x12\x85\x5a\x34\x16\x71\x33\xff\xf0\x35\xda\x34\x72\x7b\ +\xbc\xe4\xd3\xcc\xf7\x61\xc6\x3e\x1e\x8c\xe1\xea\x44\x96\x87\x14\ +\x47\x71\xdd\xcb\xbc\xf8\x96\x4b\x19\xcf\x2d\x0f\x87\xbd\x3c\x12\ +\xd3\xbd\xd1\x0e\x0b\x66\xf1\x8d\xa7\x0d\xab\xe2\xb1\x20\xcb\x26\ +\x0f\xc5\xdf\x33\x43\x53\xa3\x77\x43\x11\xf7\x79\xb6\x84\x0f\xf5\ +\xdf\x29\x1f\xc8\xa7\x1e\xa3\xf0\xf3\x06\x2f\xb2\x97\xb0\xc8\x65\ +\x1f\xda\xc1\xe1\x33\xbd\x59\xe8\xca\x5c\x72\x79\x38\x38\x6c\xe2\ +\x67\xc6\xc8\xe7\x71\x09\x1f\xbb\xda\xf4\x85\xbe\x04\x1b\xe7\xbb\ +\xd2\x6f\xa3\xda\xfc\x1b\x5a\x00\xf4\x60\xb6\x6b\xa4\xb0\x53\xcf\ +\x35\xde\x2c\x35\x2e\x4a\xbe\xe6\x72\x2b\xe4\xf3\xb8\xc2\x75\x50\ +\xac\x8d\x7e\x74\x5e\xec\x3a\xf4\xfd\x33\x01\xa9\x0e\xb3\xfc\xcb\ +\x75\x38\x8c\xb0\x4e\x80\x1e\xbc\xee\x22\xfc\xe1\x84\xac\xc8\x53\ +\x3c\xea\x62\xe5\xca\x28\x1d\xf4\xf7\x4a\xb1\xc7\xa8\xb2\xcd\xbb\ +\x1b\xee\x75\xd9\x8b\x5e\x10\x56\xe8\x14\xa5\xc8\x16\x26\xdb\x66\ +\x7c\x0a\x6e\x54\xe4\x33\x71\x70\x98\x1a\x46\xe0\x45\x34\x2b\xc4\ +\x1d\x8a\x31\x49\x46\x14\x8c\xe1\xb0\xc2\xe2\xc6\xe0\xdb\x26\x45\ +\x6c\x55\x08\x3b\xca\x55\xb6\x99\x2a\x31\x5a\xd9\x0a\x6a\xe8\x1d\ +\x4c\xd4\x93\xca\xbe\x7f\xb3\x6d\x96\xae\x98\xa0\x1c\x0b\x02\xbd\ +\x4c\x75\x8d\x72\xbb\x23\x59\x43\x9f\x84\x7b\x15\x96\xe7\xfc\xb7\ +\xdc\x62\xbe\x52\x4e\x7c\xa7\x03\x54\x93\x62\x9d\xdf\x3d\xa3\x85\ +\x0a\x01\xab\x12\x32\xef\x7b\x21\xcd\x6a\x05\x03\x5f\x49\x18\x2f\ +\x56\xf4\xa2\x5d\x09\x88\xfa\xfc\x22\xab\x08\x90\xdb\xfc\xcc\x06\ +\xff\x56\x7c\xd5\x6e\xcc\x1f\x14\x57\x28\x96\x49\x9e\x37\x4b\xae\ +\x57\x34\x1e\xfd\xd9\xe1\xe2\x86\xea\xb5\xad\x71\x6e\x5f\x4a\xb1\ +\x45\xfc\xd2\x26\x4b\x0b\xde\x28\xe8\xa1\xd8\x2f\xd8\xec\x36\x92\ +\x4d\x16\xbf\xd2\x1e\x36\x43\x8b\x65\x5c\xa6\x98\xcc\x95\xef\x31\ +\xa5\x14\xfb\xfd\xf1\xa7\xc7\x8a\x0b\x7f\x11\xf9\x6c\x51\xb5\x81\ +\xf1\xe2\xe3\xd6\x93\x56\x46\x80\x2a\xe1\xa7\x22\x01\xb8\x3c\xfe\ +\x5b\xb8\x90\xa6\x11\xb3\x44\x4e\xab\xa5\x47\x47\x8a\x3d\xe6\x5b\ +\x8a\x6c\x73\x88\x84\xde\xec\x11\xc3\xe2\x13\x87\x68\x9d\xa7\x28\ +\xd3\xc5\x9e\xb1\x20\x6c\x6e\x8e\x84\xe0\x08\x0b\x85\xda\xd4\xa9\ +\x59\xcf\x0a\xc4\x7f\x04\xd9\x6f\xef\x3e\x96\x36\x64\xc4\x71\x60\ +\xef\xc9\xc9\xd7\x6e\x12\xfb\xca\x83\xb6\xad\xd7\x02\xf9\xc2\xc5\ +\x0d\xdd\x1f\x92\x72\x75\xb6\x1a\xca\x0e\x17\x37\xce\x11\x4f\x96\ +\x5f\xec\xfa\x48\x91\xb8\x99\xb4\xd2\xb6\xe5\xda\x20\xed\x6d\x1f\ +\xee\x3a\xbc\xcb\x2b\x80\x49\xb6\xed\xd6\x06\x39\xc2\xbd\xb6\xf3\ +\x81\xa7\x84\x8f\xeb\x0d\x9c\xf4\x9a\x42\x11\x0d\x02\xc3\x2e\x9b\ +\x64\x9f\x0b\x1f\x3f\x6d\xdb\x6a\xad\x78\x56\x60\xb8\x3d\xff\xe1\ +\x10\xb1\x81\xfc\xd2\xb6\xcd\x5a\x71\xad\xc8\xf1\xf8\x11\xfa\x6d\ +\xc2\x47\x6d\xa6\xb3\xb3\xc5\x8c\xbe\xe2\x4c\x70\x6b\x47\x24\x28\ +\x2d\x76\x3f\xa0\xd1\xb6\xcd\x5a\xd1\xc8\x26\xa1\xf6\xd2\x0e\x07\ +\x48\x2f\x37\xbc\x63\xdb\x62\xed\x90\x18\xfd\x1c\x20\x2d\xbe\xf6\ +\x36\xc6\xb6\xbd\xda\x21\x65\xa6\x6f\x24\x05\xc3\xc4\xd5\x52\xa4\ +\x53\xf5\x44\xa2\x58\x5c\xed\x0e\x4d\x8b\x1b\xc5\xdf\x24\xf1\xbf\ +\xbc\x22\xa2\x89\xdd\x42\xed\xc8\xb4\xf8\xc2\xf3\x76\x2f\x69\xa7\ +\x25\xa4\x5c\xb4\x43\xd3\x62\x3a\xda\x5a\x2f\x59\xa7\x25\xc4\x64\ +\xbc\x69\xcb\x19\x82\x13\xe0\x00\x69\xc9\xfb\x85\x6d\x5b\x63\x81\ +\xc4\x2a\x9b\x16\xf7\x7c\x0f\xda\xb6\x35\x16\x1c\x12\xea\xfa\xa7\ +\xc5\x7f\x68\x09\x99\xa0\x34\xe1\x90\xd2\x31\xf7\x4b\x8b\x33\xbe\ +\xb9\x1c\xd1\x26\x21\x05\xf7\x7d\xd2\xe2\x4d\xfc\xff\xaf\x75\x80\ +\x1b\xab\xc2\x14\xed\xc2\x05\x83\x42\x5a\x6d\x5b\x1b\x03\x0a\x85\ +\x2d\xfe\xf6\x1f\x01\x56\x1a\xba\x4a\xe4\x85\x9c\xfc\x00\x00\x00\ +\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x63\x72\x65\x61\x74\x65\ +\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x36\x3a\x35\ +\x36\x3a\x32\x35\x2b\x30\x30\x3a\x30\x30\xe4\xd6\x32\x7a\x00\x00\ +\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x6d\x6f\x64\x69\x66\ +\x79\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x36\x3a\ +\x35\x36\x3a\x32\x35\x2b\x30\x30\x3a\x30\x30\x95\x8b\x8a\xc6\x00\ +\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\ +\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\ +\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\ +\x00\x00\x0d\xd2\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x07\x05\x1e\xbb\x32\ +\xe1\xbc\x00\x00\x0c\xa0\x49\x44\x41\x54\x78\xda\xd5\x9d\x6b\x74\ +\x15\xd5\x15\xc7\x7f\x21\x37\x04\x08\x90\x04\x49\x44\x50\x5e\x06\ +\x8d\x10\x45\x45\xa9\x54\x4b\xa9\xad\x4b\x5c\xb6\x3e\xda\x5a\xb5\ +\xb5\xd8\x25\xe8\x6a\xab\xab\x48\xad\x02\x2e\x8b\xa8\xd5\xb2\x44\ +\x50\xa4\xf8\x02\xad\xa2\xd5\x56\xd0\xaa\xf5\x81\xa0\xa0\x08\x08\ +\xe5\xa1\xc8\x23\x3c\x14\x8c\xb1\x02\x01\xf1\x86\x84\x87\x09\xc9\ +\xf4\x43\x72\xe1\x26\x67\x9f\x33\xe7\xcc\xcc\xcd\x85\x7d\xd6\xfd\ +\x32\x33\xfb\xbf\xcf\xde\x73\xe6\x3c\xf7\xde\x37\x83\x96\xa1\xce\ +\x5c\xc5\x85\x94\x70\x2c\x6d\x7d\x9e\xdc\x4f\x05\x6b\x99\xcb\xbf\ +\xd8\xd1\x42\x75\x4b\x39\xe5\xf0\x00\xd5\x78\x8e\x65\x2f\x0f\xd2\ +\x3e\xdd\x55\x8f\x82\x4a\xd8\xe0\xac\x7c\xa2\x6c\xe4\xd4\x74\x57\ +\x3f\x2c\x15\xb3\x3d\xb0\xfa\x1e\x1e\x3b\xe9\x97\x6e\x15\xc2\x50\ +\x2e\x65\xa1\xd4\xf7\xf0\xd8\x72\x34\x7f\x08\xd3\x43\xab\xef\xe1\ +\x31\x25\xdd\x6a\x04\xa5\x1e\xd4\x46\x62\x80\x03\x1c\x9b\xba\x4a\ +\xb6\x4a\xa1\x01\x46\x10\x8b\x04\x27\x9b\x6b\x8f\x4e\x03\xfc\x20\ +\x32\xa4\x1f\xa6\xb0\x96\x21\x29\x8f\x7c\x5a\x8b\x77\x62\xec\x57\ +\x1a\xf3\x13\xe4\xf9\x22\xe6\xf3\x0f\x85\xaf\x92\x0c\xe1\xc9\x6c\ +\x4e\x63\x00\xa7\xd2\x2e\x1d\x8a\x77\x61\x14\x8b\x88\xe3\xe1\x51\ +\xc3\x6a\xa6\x70\x7a\xb3\x27\x4e\x56\xd4\xa8\xa6\x8d\x15\x76\x47\ +\xbe\x55\x78\x7b\x35\x7b\xe2\x46\x96\x51\xd3\x78\xaf\x9e\x4f\xb8\ +\x83\xe3\x5a\x4e\xf9\x6c\xfe\xcc\x5e\xa1\xb3\x7a\xbd\x49\x35\x7f\ +\xaa\xdc\x5f\x6a\x2d\xe1\x23\x85\xf7\xc7\x49\x77\xaf\x64\x9b\x20\ +\xbd\x9a\x71\x64\xb5\x84\xfa\xf9\xbc\x6f\x98\xbe\xde\x78\xa8\xb1\ +\xde\xa1\xdc\x7d\xd2\x5a\xc6\x73\x0a\xef\xe8\xc6\x3b\xb9\xfc\xd3\ +\x30\x5e\xbc\x4b\xa7\x54\xab\x5f\xc0\x27\x3e\x83\xd6\x9b\x74\x01\ +\xe0\x79\xe5\xce\x2d\xd6\x52\xc6\x2a\xbc\x33\x01\x18\xc4\x56\x1f\ +\xe9\x6b\x28\x48\xaf\xfa\x1e\x1e\x15\x5c\x06\xc2\x93\x43\xad\xe5\ +\x5c\xa2\xf0\xae\x24\x83\x91\x87\xbe\x7a\x53\x29\x6d\x7c\x01\x29\ +\xa0\x42\xd6\x58\x4f\x5e\xa6\x73\x40\xb9\xd6\xdd\x5a\xd2\x89\x0a\ +\xef\x3e\xe6\x5b\xcb\x5e\x43\x61\xfa\xde\xbe\xbe\xc8\x43\x99\x4c\ +\xad\x02\x2c\x9f\x53\xdc\x0a\xc2\xaa\xef\xf1\xa1\x93\xbc\x15\x21\ +\xa5\x59\x9b\xc0\x6e\x26\x58\xc8\x7c\xcd\xca\xfc\x5b\x76\x59\xaa\ +\xb4\xde\xc9\x00\xeb\xac\x9e\xaa\x61\x15\x55\xe2\x9d\x62\xe6\x45\ +\xf7\x21\xe8\xbf\xfd\xf9\x74\x22\x83\x5f\xb1\xdb\xe2\x9d\xd8\x8f\ +\x01\x00\xb7\x59\x20\x2e\xa3\x08\xe8\xc4\x7b\xa9\xed\x0b\xf4\x8d\ +\x7f\xe1\xa1\x95\x7a\x17\xde\xf2\xad\xae\xfd\x18\x00\x70\xb1\x0f\ +\x5a\x3d\x53\x0e\x4d\xc1\xdb\xf1\xae\xf6\x43\x08\x3d\x3f\xb4\x51\ +\x1f\x20\x83\x91\x42\xbf\x9f\x5c\x4e\x70\x92\xdb\xd3\x88\xb5\x83\ +\x8b\x9a\x3c\x9d\x32\x13\xd8\xaa\xdf\x40\xfd\x84\x29\x6c\xa2\xec\ +\x71\x18\x03\x1a\x0c\xba\x47\x8b\x35\x57\xe8\xe0\x52\x62\x02\x37\ +\xf5\x01\xda\x30\x81\x3a\x91\xc3\x6d\x0c\x00\x58\x26\xe2\xd4\x30\ +\x5e\xd3\x71\x47\x6e\x82\x7c\x43\xd7\x67\x5a\x80\x5e\xc0\x97\x02\ +\xcf\x54\x67\xf9\x7f\x13\x50\x36\x70\x86\x81\x23\x87\x05\xda\xee\ +\x30\xcf\x55\x7c\x16\xef\x04\x52\x1f\xa0\x13\xb3\x94\x2e\x6b\xa0\ +\xb3\x01\x06\x51\xdf\x0c\xe5\x29\x72\x7c\x78\xf4\x26\x58\xa0\xd9\ +\xb5\xd0\xd2\xd8\xc0\xea\x37\xd0\xb5\x54\x26\x71\xdd\xe3\xac\x3e\ +\xc0\x7d\x49\x08\x71\xae\xb2\xe2\xd1\x9b\xc0\x69\x18\x2e\x68\x52\ +\x7d\x77\xf5\x01\xba\x33\x83\x72\x76\xb3\x98\x2b\x02\xa9\x0f\x70\ +\x25\x4b\xd8\x4d\x39\xd3\x1d\xc6\x10\x9d\x09\xe2\x2e\x9f\xc1\x1f\ +\x9d\xba\xbe\x23\x8d\x74\xdd\xe1\x75\xf6\x10\x1f\x8a\x5f\x51\x5a\ +\xf6\xde\x02\x91\xdc\x0a\x5e\xb3\x65\xcf\x14\xb6\x33\x8f\x26\xf5\ +\x1b\x4c\xa0\x4e\x90\xb7\xd8\x32\x77\x57\x58\x0f\xd0\x39\xdd\x1a\ +\x39\xd3\x71\xca\x28\x72\x50\x7a\x4c\x9a\x54\xa8\xc7\x19\x15\xd6\ +\x6b\xbe\x23\x87\x2a\xa9\x6f\x76\xa5\x5e\x7a\x4c\x32\xc0\x3e\xe5\ +\xca\x09\x0c\x4e\xb7\x3e\xce\x34\x8a\xcc\x66\x57\x76\xdb\x33\xef\ +\x14\xbe\x9f\xdc\x74\x6b\xe4\x44\x67\x0a\x67\x0b\x6f\xd9\xb3\xbf\ +\x22\xf4\xa1\xcf\xa5\x5b\x27\x07\x6a\xcb\x7a\x41\x83\xdb\xec\x01\ +\x2e\x17\xc7\xd1\x6b\xd2\xad\x97\x35\x49\xeb\x88\x5a\x79\x49\x24\ +\x2f\x52\xb3\x58\x45\x89\x72\x75\x0f\x67\xd8\x0f\x25\x46\x2a\xe2\ +\x54\x4e\xa6\x07\x79\xe4\x00\x7b\x89\x53\xc6\x46\xd6\xf0\x69\x24\ +\xe8\x17\xf1\x86\xa0\xd7\x34\x6e\x72\x01\x39\x57\x5c\xd6\x2e\x09\ +\x79\xe0\xdd\x9a\xcb\x79\x96\xff\x69\x66\xeb\x1e\x5f\x32\x93\x4b\ +\x5d\x97\x2d\xcd\xa8\xb3\x78\x70\xb6\xcd\x7d\x3d\x78\xbf\x58\xc5\ +\xbb\x02\x57\xec\x78\x26\xb3\x4b\xab\x7a\x72\xd9\xc9\x24\xba\x05\ +\x96\x33\x4b\x40\xac\x6f\x72\xb6\x68\x49\x59\x2c\x15\xa0\xea\x18\ +\x12\xa0\x52\x85\x3c\x26\xf4\xca\xa6\x72\x80\x47\x02\x1d\x72\x5d\ +\x2f\xa2\x4d\x0b\x66\xcb\x22\x71\x5b\xaa\xdc\xf1\x00\x32\x83\xdf\ +\x59\xed\x1a\xab\xe5\x6b\x6e\x70\xdc\x48\xeb\x2d\xd6\x78\x73\xf0\ +\x45\xdc\x08\xb1\x62\xb3\x1d\x10\x3a\xf3\x46\x20\xe5\x13\xe5\x3f\ +\x1c\x63\x2d\x2b\x26\x2e\xe2\x6a\x03\x6c\xc6\x24\xd1\x6c\xb1\x5a\ +\xbf\xb1\xe4\x3e\x43\xdc\x1e\x73\x2b\x5f\x70\x9a\xa5\xb4\xf1\x22\ +\xff\xdd\x66\x26\xb9\x89\xe5\xd0\x97\x12\xfa\x52\x42\x7f\x71\xf4\ +\xac\xa6\xbf\xc5\x80\xf8\x7d\x5e\x8d\x64\xfe\x18\xe7\x52\x16\xfa\ +\x3e\x75\x16\x1f\x6a\xc6\xa8\x32\x4a\x59\xdb\xf8\xab\x32\x83\xb4\ +\x61\x18\x33\xd9\xac\xac\xa3\xd4\xf2\xbc\x6f\x85\x06\xb3\x2f\xf4\ +\xdb\x4f\x94\x7d\x9c\xe7\x2b\x6f\x8e\x05\x4e\x1d\x1f\x31\x91\xde\ +\xc9\x6c\x87\x5b\x40\x26\xa3\x18\x6d\xbd\xec\xad\x26\x8f\x3a\xc3\ +\xfd\xfe\xbc\x6f\x7c\xfb\x9b\xf8\x80\x52\xca\x89\x03\x79\x74\xe7\ +\x14\xbe\x47\x1f\xc3\xf3\x71\x06\xb3\xc6\x70\x3f\x8b\x2a\xb2\x2d\ +\xeb\x5e\xc7\xd3\xdc\x42\x65\xd3\x8b\x5d\xc4\x41\xcf\x54\x4c\xe2\ +\x3a\x19\x5c\x64\xb7\x73\x17\x45\x22\x57\x1f\xee\x66\x87\x96\x6f\ +\x2b\xf9\x06\x89\xd9\x8e\xb5\xff\x9c\x53\x92\xd9\xbb\xb1\xd1\x11\ +\xe0\x33\x43\x65\x32\x78\x4d\xc3\x15\xe7\x66\x9f\x78\x81\xb6\xdc\ +\xa2\xd9\x90\xf5\x78\xd9\x38\x28\xda\x3b\x6f\x24\x5e\x44\x71\x82\ +\x35\xc6\x07\xce\x5f\xa5\x69\x93\xf9\x3a\x0d\xcf\x5c\xcb\x13\x9a\ +\x6e\xda\x33\x9e\x61\x06\xae\x61\xce\x3a\xac\x4e\xb8\xed\xfd\xc9\ +\x99\xf5\x05\x65\xb3\x21\xb9\xf9\x57\x88\x3c\x13\x1c\xbc\x52\x33\ +\x99\xa8\x79\x6b\x79\x06\xae\x7b\x34\xc7\x72\xfa\x72\x27\x40\x3b\ +\x07\x8f\xfe\x7a\xb6\xf2\x12\x3f\x33\x56\xfe\x41\x91\x73\x8c\xb5\ +\xf2\x09\xba\x5d\xc4\x79\xc0\xc8\xd3\x9f\x7b\x79\x9b\x2f\xac\xf5\ +\xd9\x4d\x47\xb8\xd2\xf7\xb1\x32\xe6\x30\x91\xeb\x18\x48\x07\xdf\ +\x6a\x17\x8a\x2e\x94\x13\x9d\xd5\x07\x98\x2c\x20\x55\x5b\x8d\x53\ +\xb9\x9c\xc3\x08\x26\x59\x18\xe3\x1a\x78\x4a\xbc\xf1\x25\x73\x99\ +\xcc\x08\xce\x71\x9c\xca\xfc\x45\xc0\x9a\x6f\xf8\x60\x4c\x14\x13\ +\x5d\x32\x5d\xd7\xa3\x87\x8d\xb1\x53\x40\x7b\x16\xfe\x2b\x54\xb8\ +\xb7\xa3\x90\x04\x65\x0a\x53\xdf\x2a\x47\xd7\x88\x64\xea\x29\xb4\ +\xa7\xb2\xc0\x1e\xee\x31\xe1\x65\xaf\x40\x18\xb1\x8b\x03\x0a\x80\ +\x0b\x04\x1b\x8f\x0d\x8c\x06\x30\x4e\x40\x3c\x3f\x30\x5a\x0f\xa1\ +\xa5\x0b\x9b\x14\x1d\x03\x0b\x98\xa6\x60\x7d\x6d\xd1\x6f\x98\xa8\ +\x23\xdf\x28\x98\x0f\x05\x46\xcb\x55\xb0\x76\x45\x1b\x30\xa1\x86\ +\x48\x3c\xed\xb7\xfc\xf0\xa1\x3d\x3c\xa3\x5c\x8b\x38\x7c\x22\xba\ +\x16\x50\x28\x2c\xa2\x4e\x0f\x5d\xbf\xb3\x84\xc1\x38\xe8\x41\x5d\ +\x8a\x5b\x40\x3f\x65\xaa\xba\x83\xd5\xa1\x51\x57\x2a\x21\xb4\x19\ +\xf4\x8d\xae\xd2\x51\x1a\xe0\x64\xe5\xca\x22\xbc\xd0\xa8\x92\x83\ +\x55\xf0\x6e\x3a\xa5\x06\xe8\xa1\x5c\x29\x8d\x04\x57\x45\xe9\x11\ +\x00\xa5\x05\x0c\xa0\xf6\xf7\xe5\x91\xe0\xaa\x28\xc1\xc7\xa9\x94\ +\x1a\x40\xdd\x7b\xad\x8e\x04\x77\x8f\x72\x25\xdc\xd0\x9a\x32\x03\ +\xa8\xdf\xbb\xdb\xa6\xb6\x8e\xd4\x89\x74\x5d\x00\x94\x16\x30\x80\ +\x3a\xe2\x47\xd3\x54\x55\x94\x68\x5a\x56\xe4\x06\x50\x9b\x6a\xcf\ +\x48\x70\x55\x94\x4a\x77\x90\x96\x30\x80\xba\x51\x1e\x4d\xec\xbf\ +\x8a\x12\xcd\x19\x75\xe4\x06\xd8\xa8\x5c\x39\x2f\xe0\x42\x38\x99\ +\x62\x7c\x57\xb9\xb6\xe1\xc8\x34\xc0\x3a\xa5\x73\xca\xb5\xd8\xcf\ +\xf7\xa3\xc1\x4a\x1f\x50\xe7\x18\x7e\xd3\x62\x06\x88\xf3\xb1\x72\ +\xed\xd7\xa1\x51\xd5\x8d\xd0\x95\x42\x6f\x73\x44\x18\x00\xe6\x2b\ +\x57\x7e\x19\x32\x80\xad\x9b\xe0\x24\xfd\x6e\xa4\x75\x16\xf6\x70\ +\xec\x03\x1c\x9b\xd3\x40\x61\xfb\x22\x5c\x02\x8c\x47\x04\xc4\x33\ +\x03\xa3\xf5\x13\xf6\x97\x58\xa5\x5c\x9c\x11\xc2\x11\xa6\x54\x41\ +\xab\x0d\xb1\x24\x3e\x8b\x83\x0a\xde\xda\xc0\x68\x45\x2c\x54\xd0\ +\x96\x21\x46\x63\xef\x64\x0e\x93\x18\xee\xbc\x25\x0a\xa3\x04\xb4\ +\x75\xbe\x81\x0e\x32\xb5\x17\x33\x10\x8d\x74\xc2\xc8\x65\x10\xd7\ +\xf3\x10\xf3\xf8\x4a\xc0\xf2\x98\x0e\xc3\xc5\x1b\x87\xcb\x17\xbc\ +\xcd\x24\xeb\xfd\xe1\x1c\x71\xef\x75\x76\x80\xe1\x30\x93\x7f\x0b\ +\x48\x3b\x2c\x9c\xb6\x3b\x30\x90\xe1\x3c\xc0\x1c\x8b\x13\x82\x9f\ +\xc3\x31\x54\xf9\x3e\x96\x6c\x8c\x7b\xe9\x6f\x14\x2f\xc7\x9a\x3c\ +\xe9\x68\x82\x4c\xcd\x76\xfd\xad\x06\x9e\x0c\x7e\xc1\x4b\xbe\x01\ +\xf6\xc9\xe5\xab\x86\xc3\xb1\x49\x0e\x2c\x1e\x1e\x75\xc6\x10\x98\ +\x36\x6c\x16\xb9\x5e\x76\x58\xc3\x75\xe4\x55\x11\x63\x83\xe1\x4c\ +\x3a\x26\xfa\x87\x99\xcb\x1f\x12\x4d\xe6\x53\x67\x56\xd3\xf8\x3e\ +\x54\xc3\xb3\x91\xef\x58\xa9\x3f\x48\x63\xc2\x7a\x7e\x64\xe0\xb2\ +\x09\xb6\x6d\x5a\x16\x1c\x6e\x95\x03\x1a\xd3\xa1\xd8\x97\x4f\x8c\ +\x2a\x4c\xd5\xb6\x9d\x19\x3e\x0b\xa4\x5e\xfc\x5d\xeb\x9f\xf2\xa0\ +\x91\xd3\xa5\xe9\x7b\x28\xd1\x84\x67\x1b\x5c\x13\xe4\xb7\x61\x4a\ +\x58\x92\x6d\x08\x7f\xaf\xe1\x45\x7e\x22\x74\x65\x39\x5c\xca\x6c\ +\x43\xee\xa9\x65\x46\x0f\xd2\xb6\x8e\xea\x2f\x4a\x04\x56\x1f\xde\ +\xb2\x28\x64\x2a\x57\x58\x6f\x61\x1c\xa0\x83\x1c\x81\xd1\x48\x5d\ +\x59\x6c\x7c\xd7\x35\xac\x60\x3d\x65\x54\x01\x1d\xe8\x49\x5f\x06\ +\x18\x15\xdc\xc2\xb9\x6c\x37\xdc\xcf\x24\x6e\xed\x0d\x58\xc1\x5f\ +\x79\x58\x0e\x9f\x28\x66\x32\xab\xad\x4e\xd9\xdf\xf4\x15\x73\x92\ +\x63\x9b\x32\x95\xed\x46\xff\xa1\x06\x7a\xc1\xa2\xd5\x6e\xe6\x19\ +\xae\x69\xda\x91\x4a\x6f\xbc\x03\xa7\x50\xd2\xf8\x93\xf7\x5f\x0f\ +\x72\x0e\x2b\x7d\xab\xd4\x9b\xb7\x35\xde\x40\x6e\xb4\x95\xa1\x6c\ +\xf2\x7d\xaa\x17\x1f\x8b\x3b\x50\x15\x7c\xc4\x1a\xd6\xb3\x96\xd2\ +\x20\x3b\x49\x77\x8b\xb6\x1c\x6f\xc9\x7d\x9c\x26\x04\xda\xa5\x2c\ +\xb1\x5e\x4e\x5d\x25\xf2\x7f\x6c\x99\xbf\x4a\xa4\x33\x45\x17\xe7\ +\xe5\x0e\x19\x9b\x62\x8c\x77\x76\x5c\x49\x6e\xb4\x53\x9c\x9c\xe7\ +\x9f\x10\x51\x02\xba\x4a\x43\x3b\x71\x2e\x5e\xcd\x49\x8e\x38\x83\ +\x9d\x3d\xb8\x1a\xca\x06\xe3\xb8\x2f\x51\x1b\x4d\x06\x83\xab\x83\ +\x19\xe0\x51\x11\x6c\x78\x00\xa4\x2c\x46\x1a\xc2\x24\xa4\x52\xce\ +\x4d\x81\x32\x83\xf5\x11\xfd\xc5\xe3\x41\x5c\x3e\x86\x8a\x13\x92\ +\x57\x82\xd9\x12\xc8\xe6\x06\xcb\x1e\x61\x29\xd7\x5b\x7b\x7d\xaa\ +\x74\xb5\x88\xb9\xdc\x35\x0e\x25\x5f\x13\x78\x12\x36\x82\xb4\x98\ +\xdb\x79\x47\xe3\x45\xbc\x8f\x79\x8c\x15\x8e\x58\x5d\xe9\x69\x11\ +\x5d\xe3\x35\xae\x9b\xf8\x3c\xca\x6f\x95\x6b\x1e\x17\xbb\xc4\xde\ +\x19\xa8\x35\x27\x52\x4c\x2f\xda\xd3\x1e\xa8\xa6\x9a\xad\x94\xf2\ +\x19\xb5\x91\xa0\xe7\xb0\xbc\xa9\x23\x2c\x00\x35\xf4\xb7\xdf\x4d\ +\xee\x26\xec\xc4\x78\x3c\x1c\x49\xf5\x5a\x82\x4a\x44\x67\xbd\x47\ +\xec\x01\xc6\x08\xec\xeb\x7d\xb3\x82\x1f\x49\x24\xc5\x0e\xc5\xed\ +\xfb\x01\x35\x7f\x48\x0d\x67\xa7\x5b\x27\x47\x7a\x4e\x30\xc1\x00\ +\x5b\x66\x75\x5b\xeb\xf6\x74\xeb\xe3\x4c\x1d\x29\x57\xb4\x10\xd2\ +\xf3\xca\xe7\x02\xcd\x3d\xf3\x0f\x32\x39\xdd\xfa\x38\xd3\x1e\xde\ +\x50\xae\x09\xfb\x89\xb2\x01\x32\x94\xa7\x8e\xae\xd8\xf1\x06\x52\ +\x83\x7f\xeb\x6d\x59\x3f\x57\x1a\xcf\xd1\x96\x42\x23\xa4\x8f\xe9\ +\xeb\x02\xb3\x4b\x0a\x9d\xf4\x93\xa4\x7e\xad\x7d\x0c\xa2\x1c\x80\ +\x7a\xb4\xa4\xd1\x81\xd1\x62\xfd\xe7\xd8\x03\xe4\x45\x90\x48\x29\ +\x9f\xfb\xd8\xcc\x7e\xd6\x72\x53\x60\x2f\x81\xf3\x99\xc7\xd7\x7c\ +\xc3\x2c\xa7\x7f\x9a\x18\x27\xd6\xdd\xe3\x32\x17\xd1\xba\x30\x1a\ +\x5b\x13\x9c\xdf\xe4\xd0\xf5\xc5\x40\xee\x52\xc3\x93\x96\x63\x35\ +\x8c\xb5\x34\xa3\x4e\xfd\xf7\xdc\x84\x67\x69\xd3\xd8\xfa\x9b\x20\ +\x4b\x48\xaa\xe7\x9e\x7d\xe2\x78\x65\xc9\xb4\xd0\xc2\xe7\x48\xa7\ +\x7e\x85\xbb\x73\x65\x5e\xc0\x74\x7a\x27\xb1\x5c\xe0\x99\xeb\x6c\ +\x80\x91\x02\x4a\xa5\x8f\xc3\x85\x4e\xfd\xbd\x9c\xeb\x2c\x1f\x53\ +\x42\xc5\x0f\xb4\xdd\xe1\x30\xcd\x49\xa3\x43\x0a\x9b\x46\x7a\x56\ +\x23\x7b\xb6\xb6\x2f\x1f\xad\x55\x3f\x70\x88\x85\x9b\x09\x72\x85\ +\x7c\xe2\x87\x4b\x57\x47\xd9\xab\xb4\x48\xdb\x9a\x65\x14\x4d\x99\ +\xfa\x6e\x26\x18\xc4\x16\x83\xfa\x1e\x17\x38\xc9\x6d\x65\x0c\xbd\ +\xae\xe7\xf1\x66\x9f\x61\x8a\xd4\xb7\x35\x41\x8c\xf1\xe2\x0e\x42\ +\x72\xb9\xd9\x49\x6a\x91\x0f\x9a\xc7\xba\x24\xcf\x93\x14\xaa\x0f\ +\xa6\xc4\xca\x0b\x38\x06\xe8\x63\xb5\xd7\x37\xdd\x49\xe6\x25\x16\ +\x88\x07\xb8\x95\x2c\x62\x4c\xd0\xaa\x3f\x24\x0a\xf5\xcd\x26\x88\ +\xb3\xc8\x27\x9f\x70\xa2\x2c\x71\x92\x38\xc6\x0a\xd3\xa3\x52\x9b\ +\x7e\x37\x42\xf5\x21\x8a\xe4\xea\x2e\xd9\xe5\xf5\x63\x80\x6d\x89\ +\xa8\xf1\x47\x6b\x02\x97\x00\xca\x55\x47\x9a\xfa\xe0\xf6\x07\x0b\ +\x4b\x85\x31\xc1\x3e\xbb\xb8\x34\x06\x3c\x66\xfd\xbf\x55\x11\x37\ +\x7e\xf7\x56\x50\xc7\x14\x5a\x0b\xc7\xd5\xf6\x89\x6d\x8b\x04\xa5\ +\x5a\x31\x90\x4d\xe9\x7b\xfb\xf6\xad\xa0\xac\x31\xf9\xa2\xfa\x2f\ +\x33\x4f\x59\x4b\x51\xc7\x80\x15\x00\xb4\x67\x86\x8f\xf4\xea\xd4\ +\xbd\xfd\x04\x75\x62\xae\xa1\x02\x2f\x1e\xda\x4f\xbc\x4c\xb9\xb7\ +\xcc\x5a\x86\x3a\x06\xcc\x3c\x74\xef\x72\x4d\x82\x06\x0f\x8f\xcf\ +\x23\x08\xd4\xb4\xa0\x18\x77\x88\x83\xcf\xb6\x26\x8e\xcd\x7d\x94\ +\xfb\x55\xd6\xe3\x80\x3a\x06\x8c\x4e\xba\x5b\xc0\x4c\xe1\xc8\xbd\ +\x96\xc7\x53\xfb\x17\x3b\x4d\xa9\x80\x71\xac\x4c\x12\xbe\x94\xdf\ +\x37\x9b\x1a\x67\x0a\x1d\x99\x6d\x4e\x28\xf5\x58\xbe\x79\x36\xb8\ +\x22\xee\x67\x65\xe3\xec\x73\x3f\xab\x18\xef\x7c\x68\x1f\x09\xe5\ +\x51\xc2\x10\x4a\x34\x2e\x90\xaa\xa7\xd8\x7b\x16\x43\x61\x0e\x77\ +\x0a\xad\xab\x97\xf8\x6c\x06\x05\x0e\xb9\xc6\x5a\x9c\x74\xde\x82\ +\xee\x65\x5b\xea\x2a\x99\xca\xff\x1b\x9c\x1f\x1e\xa2\x91\x16\xa4\ +\xb0\x96\x29\xa4\x98\x70\x38\x15\xac\x0c\x49\xb7\x2a\x41\xe9\xc6\ +\x48\xd4\x5f\x9c\x6e\x35\x82\x53\x2b\x21\x46\xc3\xb5\xec\x8d\xc0\ +\x67\x24\x8d\x54\xc0\xba\x50\xea\xd7\x70\x49\xba\x55\x08\x4b\x5d\ +\x59\x1c\x58\xfd\x5d\x5c\x9c\xee\xea\x47\x41\x31\xc6\x58\x26\xd4\ +\x4d\x2e\x07\x79\x3e\x44\x72\x5d\x6b\x8a\x26\xc0\xdd\x9f\x3a\x70\ +\x05\x17\x72\x3a\x5d\x7d\xcf\x17\xbf\x65\x27\x9b\x78\x87\x59\x11\ +\xe5\x99\xf6\xa1\xff\x03\x45\x01\xed\x8a\x3b\x61\x7c\x4e\x00\x00\ +\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x63\x72\x65\x61\x74\ +\x65\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x37\x3a\ +\x30\x35\x3a\x33\x30\x2b\x30\x30\x3a\x30\x30\x3d\xb2\xd3\x87\x00\ +\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x6d\x6f\x64\x69\ +\x66\x79\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x37\ +\x3a\x30\x35\x3a\x33\x30\x2b\x30\x30\x3a\x30\x30\x4c\xef\x6b\x3b\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\ +\x67\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\ +\x82\ +\x00\x00\x07\xde\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x07\x00\x07\xa2\x2e\ +\xbd\x39\x00\x00\x06\xac\x49\x44\x41\x54\x78\xda\xed\x9d\x5b\x6c\ +\x14\x55\x1c\xc6\x7f\x5b\xe8\x96\x04\xa8\xda\x22\x11\x2f\x80\x95\ +\x5b\x52\x0c\xa1\x46\xab\xf1\x81\x86\x37\xa3\xd4\x26\x84\x08\xa2\ +\x26\x52\x78\xd0\xf0\x40\x0c\xc6\x84\x18\x2c\x26\x6a\x5b\xd1\x46\ +\x49\x7c\x50\x20\x46\x1f\x44\x89\x81\xa2\xa0\x3e\x08\xc2\x83\x01\ +\x25\xc1\x22\x2d\xd0\x50\xc0\x12\xb9\x85\x16\x68\x6b\xda\x22\xed\ +\xf8\xb0\x6d\xdd\x39\x73\xd9\x99\xdd\x33\xfd\xef\x4e\xe7\x9b\x97\ +\x3d\xf3\x9f\xcb\xf7\x7d\x73\xe6\xcc\xfc\xcf\x9c\x99\x8d\x11\x36\ +\x4c\xa1\x8a\x4a\x66\xf3\x00\x70\x81\x56\xf6\xb0\x9b\x0e\x69\x52\ +\xa3\x85\xc9\xbc\x4d\x0f\x86\x32\x75\xb3\x89\x49\xd2\xd4\x46\x03\ +\xb3\x68\xb1\x88\x1f\x9e\x5a\x99\x27\x4d\x2f\x68\xcc\xa7\xd3\x51\ +\xbe\x81\x41\x07\xa5\xd2\x14\x83\xc4\x14\xce\xba\xca\x37\x30\x68\ +\xa3\x58\x9a\x66\x70\xf8\x3c\xa5\x7c\x03\x83\xad\xd2\x34\x83\xc2\ +\xc3\xdc\xf6\x64\xc0\x6d\xeb\x69\x30\x4e\x9a\xbb\x16\x6c\xe0\x09\ +\x65\x4e\x0b\x5f\x72\x98\x3b\xb9\xdb\x34\x37\x8f\x7e\x7e\x92\x26\ +\x1b\x04\xce\x29\x47\xba\x8e\xf1\x00\xe4\x53\x6f\x69\x07\x42\x88\ +\x62\x45\xe4\xcf\xc4\x46\x62\x31\xf6\x2b\xd1\x22\xf3\xca\x79\xd2\ +\xec\x35\xe0\x5e\xa5\xfc\x19\xc6\xc8\x6f\x6b\xc3\xa7\x2c\x1d\x06\ +\x03\x26\x2b\xe5\xeb\xa6\x52\xa7\x12\x55\xee\x09\xc3\x60\x80\x2a\ +\xb1\xdc\xa5\x04\x97\xa4\xe9\xea\x47\x01\xbd\xa6\xb3\xbc\x8b\x92\ +\x91\x58\x09\xdd\xa6\x58\x2f\x71\x69\xba\xfa\x11\xa7\x55\x69\xe8\ +\xae\xf0\x02\x85\xdc\xc1\x8b\x5c\x55\x22\x7b\xa5\xc9\xea\xc7\x4c\ +\x8e\x78\xba\x09\x4a\x4c\xab\xa4\xe9\xea\x46\x25\x1d\x3e\xe4\xb7\ +\x85\xeb\x04\x88\xd3\xc0\xa0\x0f\xf9\x83\x3c\x2b\x4d\x59\x27\xa6\ +\xf3\xab\x0f\xf1\x06\x06\xef\x48\x53\xd6\x89\xaa\x14\xd9\xbf\x75\ +\xfa\x28\x14\x97\x7c\xc0\xad\xea\x1f\xe7\x86\x43\x67\xc8\xcb\xd2\ +\xa4\xf5\xc1\xa9\xd5\x1f\xa4\x81\x38\x53\xa9\xe5\x92\x69\xfe\x5f\ +\xbc\xcb\x5d\xce\x9b\x8b\x79\xdf\xf3\xa8\x22\xc6\x02\x4a\x99\xca\ +\x78\xae\x71\x8c\x3f\x19\x18\x9a\x5f\xc5\x76\x5b\x39\xd7\x59\xc5\ +\xee\xa1\xdf\xe3\x98\x4f\x19\xc5\x0c\x70\x99\x16\x9a\xa4\xa5\xf8\ +\x47\x11\x75\xb4\x9b\x8e\xe2\x65\xea\x98\xea\x52\xf5\x8f\x30\x53\ +\x9a\xb4\x3e\xac\xe1\xba\xad\xc8\x1b\x1c\x77\xa9\xfa\x21\x41\x1e\ +\x5b\x7c\xb6\xed\x9d\x54\x49\x93\xd6\x89\x3a\x9f\xf2\x8f\xf2\x90\ +\x34\x65\x9d\x58\xea\x4b\x7c\xa8\xaa\x3e\x40\x9c\x33\x3e\xe4\xdf\ +\x64\x99\x34\x61\xdd\x58\xed\x43\x7e\xa8\x5a\xfd\x61\xfc\xa0\x88\ +\xbc\xc2\xf3\x4c\xa6\x90\x95\x5c\x51\x22\xad\xe1\xaa\xfa\x09\x14\ +\xd0\x67\x12\xd9\xc5\x83\x23\xb1\x12\xba\x4c\xb1\x5e\x0a\xa4\xe9\ +\xea\xc7\x4c\xe5\x28\xbf\x69\x8a\x6e\x54\xa2\x73\xf5\xed\x38\x5b\ +\x32\xa4\x7b\x94\xf2\xef\xa6\xd2\x6f\x4a\xb4\x08\x6d\xc8\x16\x03\ +\xba\x5c\x25\xaa\x82\xbb\xa5\xe9\xea\x47\xa1\x52\xc9\xcd\x4f\x77\ +\x0e\x28\x51\x8d\x35\x20\x7b\x70\x5e\x11\x59\x4f\x3e\x00\xf9\x6c\ +\x56\x22\x67\x74\xee\x76\x7c\x8a\xf8\x70\x5a\x0a\x57\x69\xa6\x09\ +\xc3\xcb\x46\xd3\xc2\x0e\xde\x30\x95\x5f\x67\x09\x3f\x02\x4f\x59\ +\x9a\xbc\x3d\x36\x6b\xc7\x59\xca\xf4\x14\x7b\x68\xe7\x5b\x6e\xf9\ +\xa1\x64\x4d\x4b\xdb\xa9\x0d\xac\xfa\x3d\xed\xf1\x26\xc8\xe6\x19\ +\x3f\x33\x68\xf2\xb4\x6e\x13\x33\xbc\x13\x72\x4a\x4b\x3b\xa9\x0e\ +\x40\xfe\x5c\xfe\xf6\x68\x80\xdd\x28\x8f\x83\x9e\xef\x21\x0f\x78\ +\xa3\x93\x2a\x2d\xfd\x58\xf3\xd5\xc3\xbb\xfc\x33\x36\xe3\x7c\xe6\ +\xfa\xb8\x89\x36\x98\xe3\x85\x50\xea\xb4\xf4\x3d\x11\xf9\xf6\x23\ +\xbd\x16\xf9\x32\x60\x51\x6a\x42\x5e\xd2\xd2\x41\x6d\x1d\x11\xde\ +\xe5\x37\x33\xcb\x76\x0b\x15\xbe\x0c\xa8\x48\x45\x28\xee\x61\xc0\ +\x59\xa2\x32\xea\x48\x49\xec\xe5\xdf\xb2\xcc\xe9\xe6\x2d\x26\x3a\ +\x6c\x43\xb3\x01\xde\xd3\xd2\xcc\x1b\x43\x7b\xf9\x9b\x29\xe4\x25\ +\x76\x71\x82\x9b\x74\xd3\xc2\x2e\x56\xb9\x8e\xf0\xd3\x6c\x80\xf7\ +\xb4\x74\x5f\x40\xf2\xfd\xa2\xc2\x55\x62\x85\x3f\x03\xfc\xa4\xa5\ +\x7d\x19\x9d\x04\xba\xe4\x67\x68\x80\x7a\x39\x9b\xa6\xe4\xda\xf5\ +\x9c\x1b\xf9\x7d\x56\xa1\x57\xc0\xb4\x0c\xe4\xef\xb7\x0c\x6e\x82\ +\x0f\x58\x9f\x81\xa5\x69\x41\x35\xc0\x5f\x5a\x9a\xae\x01\x59\x23\ +\xdf\x6a\xc0\x3f\x4a\xd9\x3d\x2d\xfd\x37\xd7\xe5\x5b\xe1\x3e\xe8\ +\xf0\x80\x12\xdd\x4b\x59\x1a\xf2\x75\x9d\xfb\xc3\xd0\xda\x08\x62\ +\xb9\x0b\x70\x4e\x4b\x13\x37\x44\x8d\xbe\x4c\xd0\x2f\x5f\xbb\x01\ +\x0d\x16\x7a\x27\x69\xa0\x81\x53\x8e\x57\x56\x27\x13\x62\x2c\x64\ +\x25\xaf\xb1\x8e\x65\x94\x11\x0b\x48\xbe\x76\x03\xe6\x7b\x1c\x7a\ +\xee\x6e\x42\x11\xb5\x5c\x30\x2d\x71\x96\xda\x40\xe4\x6b\x37\x00\ +\xb6\xa7\x61\x80\xd9\x84\x6a\x87\x91\x1a\x41\xc8\x0f\xc0\x80\x29\ +\xb4\xa5\x69\x41\xc2\x84\xad\x9e\x97\xd6\x21\x3f\x43\x03\xec\xba\ +\xc4\xae\x51\xc9\x21\xd7\x9e\x9f\x5e\x26\x38\x8c\x2d\x89\x51\xe9\ +\x99\x78\x56\x5c\xf8\xec\x3b\x36\x9a\x79\x8c\x16\xc7\x75\x4e\xf3\ +\x08\x0b\xd8\x49\x66\xfd\x83\x0d\xd9\x20\xdf\x0d\x93\xa8\x51\x06\ +\x1a\x1b\x18\x74\x25\xbd\x84\x58\x46\xa3\xaf\x81\x8a\xc9\xd3\x87\ +\x1a\x99\x6a\x6f\x03\xfe\x47\x31\xd5\xec\xa6\x85\x6e\x7a\x38\xc9\ +\x2e\xaa\x2d\x69\x69\xba\x26\xac\xcc\x0d\x03\xbc\xc1\xd9\x04\xe7\ +\x54\xfa\x7b\x9b\xed\x2c\xe7\x14\x03\xae\xb6\x0d\x70\x92\xe7\xb2\ +\xcf\x00\x27\x13\xdc\x52\xe9\x7e\xcb\xfb\x6a\x6b\x3d\xd7\x9e\x57\ +\xb3\xd1\x00\x80\x4a\x65\x57\xee\x4f\x78\xef\x33\x45\x0b\x6d\x5e\ +\x7a\x76\x9a\x7a\x94\x97\x64\xb4\xf6\x07\x64\x82\x6b\x4a\xd9\x3d\ +\x95\xbe\xdf\x54\x2a\x75\xec\xf1\xb3\x62\xa2\xce\xb7\x80\x75\x1a\ +\xa0\x3e\xb3\x75\x4f\xa5\xcd\x89\xb7\xbf\x21\x0f\x13\xb2\xd3\x80\ +\x8b\x4a\x79\xb5\x29\x95\x5e\x93\x62\x69\x21\xe8\x34\xa0\x23\xa9\ +\xfb\x0c\x60\x31\x75\x23\xa9\xf4\xfb\xca\xb9\xd7\x66\x79\xd7\x4b\ +\x08\xa9\x9e\x0e\xfb\x43\x23\xeb\x4c\x65\x7f\x4f\x78\x93\xf1\x15\ +\xed\x49\xa5\xe9\xac\x10\xf2\xc7\x27\xbc\xa6\xd2\xd6\x27\xbc\x99\ +\xb4\xe4\x59\x73\x15\x80\x13\x7c\xe1\x69\xb9\xed\x34\x07\x73\x04\ +\xfc\x43\xf7\x18\xa1\xf5\x1e\xc6\x6f\x9c\x67\x83\xb4\xec\xe0\x0c\ +\xe8\xa4\x2a\x45\xf3\xd6\xc9\x33\x96\x3b\x86\x10\x19\x90\x2a\x95\ +\x6e\xe5\xc9\xec\xa9\xfe\xc1\x18\x00\x6d\x94\xb3\x89\x1e\xcb\xfc\ +\x1e\x6a\x28\xe3\x94\xb4\x64\x33\xf4\x5e\x06\x93\xa5\x6e\xa1\x8a\ +\x25\xcc\x19\xfa\xa2\xd3\x69\xbe\xa3\x31\x1b\xbf\xe8\x64\x67\xc0\ +\x72\x6a\x98\xed\x5a\x37\x06\x69\xa5\x86\xaf\x5d\xb7\xdc\xc1\x36\ +\xb6\x49\xcb\x4b\xc7\x80\xb5\x6c\x49\xb9\x56\x1e\xf3\xd8\x41\x31\ +\x9f\x48\xd3\xcf\x1c\xea\x71\x2e\xa4\xd6\xf3\xba\xf5\x96\x6f\x37\ +\xe4\x20\x54\x03\xc4\xd2\xd2\x6c\x31\x40\x2c\x2d\xcd\x16\x03\xc6\ +\x1c\xc6\xbc\x01\xa9\xee\x03\x72\x34\x2d\xd5\x67\xc0\xa7\xfc\x92\ +\x54\xaa\x08\x9f\x01\x63\xfe\x14\x88\x0c\x90\x26\x20\x8d\xc8\x00\ +\x69\x02\xd2\x88\x0c\xd0\xbc\xbd\x74\x9f\xf0\x8a\x41\x6f\x87\x48\ +\x0e\xa6\xd2\x3a\x6b\x40\x4e\xa6\xd2\x3a\x0d\xc8\xc9\x54\x5a\xa7\ +\x01\x39\x99\x4a\x47\x57\x01\x69\x02\xd2\x08\xa6\x5b\x3c\x81\x9c\ +\x48\xa5\x83\x34\x20\x27\x52\xe9\x31\x7f\x0a\x44\x06\x48\x13\x90\ +\x46\x64\x80\x34\x01\x69\x44\x06\x48\x13\x90\x46\x64\x80\x34\x01\ +\x69\x44\x06\x48\x13\x90\x46\x64\x80\x34\x01\x69\x44\x06\x48\x13\ +\x90\x46\x64\x80\x34\x01\x69\x44\x06\x48\x13\x90\x46\x64\x80\x34\ +\x01\x69\x24\x7a\x85\x17\xf3\xe8\x50\xb9\x44\x89\xaf\x30\xfd\x57\ +\x57\x98\xa2\x06\x47\xd9\x0f\x10\x67\x67\x1a\x6f\x7f\x87\x63\xfa\ +\x86\xfc\x71\x6c\xe4\x15\xbd\x95\x2a\x87\x50\xca\x40\x8c\x4b\x96\ +\xcf\x67\x8d\x25\x5c\x8c\xd1\x17\xc6\x4f\x95\x7b\x46\x5f\x9e\xe5\ +\xad\xee\xb1\x85\x23\x31\x16\x72\x38\x8c\xdf\xeb\xf7\x84\x5b\x94\ +\xe7\x71\x8c\xc7\x39\x44\xbf\x34\x97\x51\x47\x3f\x07\x29\xe7\x8f\ +\xff\x00\x08\x88\xdf\x89\x4e\xda\x86\x68\x00\x00\x00\x25\x74\x45\ +\x58\x74\x64\x61\x74\x65\x3a\x63\x72\x65\x61\x74\x65\x00\x32\x30\ +\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x37\x3a\x30\x30\x3a\x30\ +\x36\x2b\x30\x30\x3a\x30\x30\x36\xc4\x2a\x1a\x00\x00\x00\x25\x74\ +\x45\x58\x74\x64\x61\x74\x65\x3a\x6d\x6f\x64\x69\x66\x79\x00\x32\ +\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x37\x3a\x30\x30\x3a\ +\x30\x36\x2b\x30\x30\x3a\x30\x30\x47\x99\x92\xa6\x00\x00\x00\x19\ +\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\x77\x77\x77\ +\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\ +\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x07\xdf\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x01\x00\x00\x00\x01\x00\x08\x04\x00\x00\x00\xf6\x7b\x60\xed\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x06\x1c\x3a\x1d\xf3\ +\xc6\x42\x00\x00\x06\xad\x49\x44\x41\x54\x78\xda\xed\xdd\xcf\x8b\ +\x55\x65\x1c\xc7\xf1\xcf\xcc\xd4\xe4\x15\x93\x40\xe8\xc7\x22\x2a\ +\x03\x17\x35\x95\x8b\x94\x12\x5a\xd8\x22\x82\xb2\x45\xf4\x0f\x48\ +\xf4\x63\xd7\x3f\x22\x5e\x33\x6d\x82\x16\xad\x8a\xa0\x56\x41\xe5\ +\xa2\x9c\x55\x26\x42\xa4\x69\x48\x81\x5a\x60\x64\x91\x60\x39\x33\ +\x96\xce\x74\x5b\x54\xe0\xdc\x7b\x9d\xb9\xf7\x9e\xe7\x39\xdf\xe7\ +\xdc\xcf\xfb\x75\x76\x57\x9c\x9e\x7b\xce\xbb\x71\xbe\x5f\xa3\x2b\ +\x8d\xab\x19\xed\xd3\x29\xcd\xab\x53\xf1\x9a\xd7\x29\xed\xd3\x4c\ +\xf4\xdb\xc1\x30\xa6\x75\x50\xcb\x95\x1f\xfd\xf5\xd7\x92\xde\xd0\ +\x74\xf4\xdb\xc2\x60\xa6\xf5\x79\xd2\x87\xff\xff\xf5\x19\x09\x34\ +\xc3\xc1\x2c\x8f\xbf\xa3\x8e\xf6\x47\xbf\x35\xac\x6d\x46\x4b\xd9\ +\x02\x58\xd2\x83\xd1\x6f\x2f\xb5\xc9\xe8\x03\x24\xf7\x92\xa6\xb2\ +\x7d\xed\x29\xbd\x18\xfd\xf6\xb0\x96\x6f\xb3\xfd\xfb\xdf\x51\x47\ +\x27\xa3\xdf\x5e\x6a\x13\xd1\x07\x48\xee\xb2\x36\x64\xfd\xea\x1b\ +\xa3\xdf\x60\x5a\xe3\x17\x40\x27\xf3\xd7\x1f\xb3\x3b\x36\x7e\x3f\ +\x03\x60\x28\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\ +\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\ +\x02\x30\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\ +\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\ +\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\ +\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\ +\x30\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\ +\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\ +\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\ +\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\ +\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\ +\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\x73\ +\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\x1c\ +\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\ +\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\x11\ +\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\ +\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\x5c\x39\ +\x01\x6c\xd5\x41\x9d\xd6\xa2\x3a\x15\xaf\xdc\xaa\x9e\x6f\x51\xa7\ +\x75\x40\x8f\x44\xdf\xee\xb2\xb4\x34\xab\xe5\xca\xb7\xb6\x49\xd7\ +\xb2\xde\x52\x2b\xfa\xb6\x97\xa2\xa5\xb9\xf0\x07\x12\x71\xcd\x95\ +\x90\xc0\x54\xf4\x01\x24\x1d\xd0\x0b\xd1\x47\x08\x71\xaf\x36\xe9\ +\xe3\xe8\x43\x4c\x44\x1f\x40\x0f\xeb\xeb\x82\x7e\x12\xa9\xd7\xdf\ +\xda\xaa\x93\xb1\x47\x88\xbf\xf5\xaf\x14\x70\x86\x28\x93\x7a\x39\ +\xfe\x08\xd1\x9e\x8c\x3e\x40\xa8\x9d\xd1\x07\x88\xff\x23\xe0\x4f\ +\xdd\x12\x7d\x84\x40\x0b\xda\x10\x7b\x80\xf8\xef\x00\xf9\x27\x77\ +\xac\x22\x3e\x80\x73\xd1\x07\x08\xf5\x43\xf4\x01\xe2\x03\x38\x1c\ +\x7d\x00\xef\x77\x1f\xff\x33\xc0\x43\x3a\x5e\x40\x86\x31\x18\x03\ +\x25\x9d\xd4\x6c\xf4\x11\xc2\xbc\x19\xfd\xf8\xcb\xb0\x4e\x47\xc2\ +\xd7\xb2\x11\xd7\x11\xad\x8b\xbe\xf5\x25\xfc\x11\x20\x49\x9b\x74\ +\x44\x5b\xa2\x0f\x51\xb3\xb3\x7a\x5c\xbf\x46\x1f\xa2\x94\x00\xa4\ +\xcd\xfa\x52\xb7\x47\x1f\xa2\x46\x17\xb5\x43\xdf\x47\x1f\x42\x2a\ +\xe1\x67\x80\x7f\x9d\xd5\xb3\x5a\x88\x3e\x44\x6d\xae\xe8\xb9\x32\ +\x1e\x7f\x59\x76\x69\x29\xfc\xcf\xe5\x3a\xae\x65\x3d\x1f\x7d\xab\ +\x4b\xf5\x6a\xf8\xc3\xa9\xe3\x7a\x2d\xfa\x36\x97\xac\x1d\xfe\x78\ +\x72\x5f\x7b\xa3\x6f\x71\xd9\x26\xf5\x61\xf8\x23\xca\x79\x7d\x54\ +\xc4\x7f\x82\x53\xb4\x96\xbe\x08\x7f\x4c\xb9\xae\x63\x5a\x1f\x7d\ +\x7b\x9b\x60\x93\xbe\x0b\x7f\x54\x39\xae\x33\x56\x83\x6e\x25\x9b\ +\xf5\x4b\xf8\xe3\x4a\x7d\xfd\x66\xb7\xea\xaa\x64\x9b\xe6\xc3\x1f\ +\x59\xca\x6b\x51\x3b\xa2\x6f\x69\xd3\x8c\xd3\x56\x80\xc9\x7f\x24\ +\xe3\xb3\x15\x60\xf2\x1f\x51\x3b\xfc\xd1\xa5\xb8\x98\xfc\x47\x36\ +\x0e\x5b\x01\x26\xff\x4a\x9a\xbe\x15\x60\xf2\xaf\xac\xc9\x5b\x01\ +\x26\xff\x24\x9a\xba\x15\x60\xf2\x4f\xa6\x89\x5b\x01\x26\xff\xa4\ +\x9a\xb6\x15\x60\xf2\x4f\xae\x59\x5b\x01\x26\xff\x0c\xda\xe1\x8f\ +\x75\xd0\x8b\xc9\x3f\x8b\xa6\x6c\x05\x98\xfc\xb3\x69\xc2\x56\x80\ +\xc9\x3f\xab\xd2\xb7\x02\x4c\xfe\xd9\x95\xbc\x15\x60\xf2\xaf\x45\ +\xa9\x5b\x01\x26\xff\xda\x94\xb8\x15\x60\xf2\xaf\x55\x79\x5b\x01\ +\x26\xff\x9a\xb5\xc3\x1f\xf9\xf5\x17\x93\x7f\xed\x4a\xda\x0a\x30\ +\xf9\x87\x28\x65\x2b\xc0\xe4\x1f\xa6\x84\xad\x00\x93\x7f\xa8\xe8\ +\xad\x00\x93\x7f\xb8\xc8\xad\x00\x93\x7f\x11\xa2\xb6\x02\x4c\xfe\ +\xc5\x88\xd9\x0a\x30\xf9\x17\xa4\x5d\xfb\xe3\x67\xf2\x2f\x4a\xdd\ +\x5b\x01\x26\xff\xe2\xd4\xb9\x15\x60\xf2\x2f\x52\x5d\x5b\x01\x26\ +\xff\x62\xd5\xb1\x15\x60\xf2\x2f\x5a\xee\xad\x00\x93\x7f\xf1\x72\ +\x6e\x05\x98\xfc\x1b\x21\xdf\x56\x80\xc9\xbf\x21\xda\x59\x1e\x3f\ +\x93\x7f\x63\xe4\xd8\x0a\x30\xf9\x37\x4a\xea\xad\x00\x93\x7f\xe3\ +\xa4\xdc\x0a\x30\xf9\x37\x52\xaa\xad\x80\xd9\xe4\x5f\xf5\x76\x95\ +\x24\xc5\x56\xa0\xb4\xc9\x3f\xfb\xf3\x19\xa7\x00\xaa\x6f\x05\xca\ +\x9b\xfc\x09\x60\x48\xd5\xb6\x02\xe5\x4d\xfe\x04\x30\xb4\xf6\xc8\ +\xef\xa5\xc4\xc9\x9f\x00\x86\x36\xea\x56\xa0\xcc\xc9\x9f\x00\x46\ +\x30\xca\x56\xa0\xd4\xc9\x9f\x00\x46\x32\xec\x56\xa0\xdc\xc9\x9f\ +\x00\x46\x34\xcc\x56\xa0\xe4\xc9\x9f\x00\x46\x36\xe8\x56\xa0\xb4\ +\xc9\x7f\x25\x02\xa8\x60\x90\xad\x40\x79\x93\xff\x4a\x04\x50\xc9\ +\xda\x5b\x81\xf2\x26\xff\x95\x08\xa0\xa2\xf6\xaa\xa7\x2f\x71\xf2\ +\x5f\x89\x00\x2a\x5a\x6d\x2b\x50\xe6\xe4\xbf\x12\x01\x54\xd6\xd2\ +\xa1\xbe\x27\xff\x54\xad\xe8\xa3\x0d\x80\x00\x12\x98\xd6\xeb\x5d\ +\x3f\x0e\x5e\xd3\x5e\xdd\x1c\x7d\xac\x81\x24\x7e\x3e\x13\x7d\xfe\ +\x01\xd5\x4c\x54\xfc\xfd\x75\x99\xd1\x6e\x3d\xa5\x7b\xd4\xd1\x8f\ +\x3a\xa4\x77\x74\x3a\xfa\x40\x03\x4a\xfc\x7c\x7c\x03\x68\xaa\xc4\ +\xcf\x67\x32\xfa\xfd\x20\x16\x01\x98\x23\x00\x73\x04\x60\x8e\x00\ +\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\ +\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\x08\xc0\ +\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\ +\x47\x00\xe6\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\ +\x11\x80\x39\x02\x30\x77\x53\xf2\xaf\xd8\x8c\xff\x47\x08\xfe\xc3\ +\x77\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\x02\x30\x47\x00\xe6\ +\x08\xc0\x1c\x01\x98\x23\x00\x73\x04\x60\x8e\x00\xcc\x11\x80\x39\ +\x02\x30\x47\x00\xe6\x08\xc0\x5c\x6f\x00\x57\xa3\x8f\x84\x8c\xfe\ +\xea\x7e\xa1\x37\x80\x3f\xa2\xcf\x88\x8c\x7e\xef\x7e\xa1\x37\x80\ +\x73\xd1\x67\x44\x46\x67\xbb\x5f\xe8\x0d\xe0\x78\xf4\x19\x91\xd1\ +\x89\xee\x17\x7a\x03\x98\x8b\x3e\x23\x32\x3a\xdc\xfd\x42\xef\x47\ +\x3c\xdd\xaa\x0b\x5a\x1f\x7d\x4e\x64\xb1\xa8\x3b\x34\xbf\xf2\xa5\ +\xde\xef\x00\x97\xf5\x7e\xf4\x39\x91\xc9\x7b\xdd\x8f\xbf\xff\x87\ +\xbc\x6d\xd1\xa9\x86\x7c\x88\x22\x86\x71\x55\x0f\xe8\x4c\xf7\x8b\ +\xfd\x3e\x2b\xf7\xa2\x36\x6a\x47\xf4\x69\x91\xdc\x1e\x7d\xd0\xfb\ +\x62\xff\x8f\x79\x5c\xa7\x39\x3d\x16\x7d\x5e\x24\xf5\x95\x9e\xd0\ +\x95\xde\x97\x6f\xf4\x39\x9f\x77\xea\x98\xee\x8e\x3e\x33\x92\xf9\ +\x59\xdb\xf4\x53\xbf\x5f\xb8\xd1\xdf\x05\x5c\xd0\x2e\x9d\x8f\x3e\ +\x35\x12\x39\xaf\xa7\xfb\x3f\xfe\xd5\xfe\x32\xe8\x84\xb6\xeb\x68\ +\xf4\xc9\x91\xc0\x51\x6d\xd7\x37\x37\xfa\xc5\xa9\x55\x7e\xe3\xbc\ +\xde\x55\x47\x8f\x6a\x3a\xfa\x1d\x60\x64\xd7\xb4\x47\xbb\x75\xa9\ +\xca\x97\xb8\x4b\xb3\x5a\xa8\xfc\xa9\xf5\x5c\xf5\x5f\xf3\x7a\x5b\ +\xf7\xaf\xf5\x78\x07\xfb\xb0\xf7\x0d\x7a\x46\x3b\xb5\x55\xf7\xe9\ +\x36\xbe\x1f\x14\xee\xaa\x2e\xe9\x9c\x8e\xeb\xb0\x3e\xe9\x5d\xfb\ +\xf4\xfa\x07\x50\x6c\xed\x28\xaf\x2b\x94\x87\x00\x00\x00\x25\x74\ +\x45\x58\x74\x64\x61\x74\x65\x3a\x63\x72\x65\x61\x74\x65\x00\x32\ +\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x36\x3a\x32\x38\x3a\ +\x35\x38\x2b\x30\x30\x3a\x30\x30\xb8\x36\x7f\x93\x00\x00\x00\x25\ +\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x6d\x6f\x64\x69\x66\x79\x00\ +\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\x30\x36\x3a\x32\x38\ +\x3a\x35\x38\x2b\x30\x30\x3a\x30\x30\xc9\x6b\xc7\x2f\x00\x00\x00\ +\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\x00\x77\x77\ +\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\x6f\x72\x67\x9b\xee\ +\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x0a\x35\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x06\x39\x2d\x76\xd3\ +\x93\x62\x00\x00\x09\x03\x49\x44\x41\x54\x78\xda\xed\x9d\x6d\x70\ +\x55\xc5\x19\xc7\x7f\xf7\xe4\x06\x02\x81\xe4\xc6\x62\x4a\xd1\xa2\ +\x44\x92\x58\xb5\xd5\xb6\x0c\xad\xe2\x0c\x44\x98\xa2\x9d\x76\x2c\ +\x63\xb1\xad\x33\x66\x52\x9d\xe9\xe8\x87\xca\x58\xa9\xb1\x23\x74\ +\x52\x60\xfa\x32\x45\x34\xf8\x42\x3b\xf5\x95\x89\xb6\x65\x64\xac\ +\x50\x2d\x1a\x84\x0e\x16\x47\x01\x65\xa0\xe8\x90\xb4\x12\x11\x0b\ +\xb4\x30\x81\x24\x37\x24\x90\xe4\xde\x7e\x30\x49\x6f\xdc\x67\xcf\ +\xdd\x73\xcf\xcb\xde\xce\xdc\xff\x7e\xca\x66\xcf\xb3\xcf\xff\x7f\ +\xce\xd9\xb3\xbb\xcf\xee\xde\x18\x61\xe1\x02\x6a\xa9\xa1\x96\x6a\ +\x2a\x28\x65\x32\x09\x4a\x71\x48\x72\x9a\x5e\x92\x9c\xa2\x83\x83\ +\xb4\xd1\xce\x87\xa4\x42\xf3\xc1\x00\xb1\x80\xed\x15\x31\x8b\x3a\ +\xea\xb8\x9a\xc9\x86\x57\xf4\xb1\x87\x6d\x6c\xe3\x2d\xce\xda\x14\ +\xc2\x3f\xca\xb8\x8d\x4d\x74\x91\xce\x31\xf5\xf2\x2a\x77\x71\xbe\ +\x6d\x1a\xb9\xc0\x61\x01\xeb\x49\xe6\x4c\x3d\x33\x0d\xd2\x4a\x3d\ +\x13\x6d\x53\x32\x47\x82\xe5\x1c\x0d\x84\x7a\x66\xea\x64\x35\x9f\ +\xb1\x4d\x2d\x3b\xa6\xd0\xc4\xa9\xc0\xc9\x8f\xa4\xb3\xac\xa7\xda\ +\x36\x45\x3d\x2a\x78\x90\xde\xd0\xc8\x8f\xa4\x73\x3c\xc5\x05\xb6\ +\xa9\xaa\x88\x51\xcf\xbf\x43\x27\x3f\x92\x92\x34\x31\xce\x36\xe5\ +\x4c\xd4\xd0\x1a\x19\xf9\x91\xb4\x8f\x6b\xc3\xbb\x9b\x5e\xe0\xb0\ +\x8c\x65\x14\x7b\xb8\x62\x80\xf7\x69\xa7\x9b\x24\x5d\xf4\x30\x44\ +\x82\xc9\x4c\xa2\x8c\x2a\x6a\x28\xf1\x60\x27\xcd\x3a\xee\xa1\x3f\ +\x2c\x19\xcc\x30\x95\xad\xc6\xef\xee\xdf\x58\xc9\x37\xa9\x26\xee\ +\x22\xe6\x0c\x16\x72\x3f\x5b\x39\x63\x68\x75\xaf\xdd\x46\x71\x3e\ +\xc7\x0c\x9c\x3c\xc1\xc3\xdc\xc0\x24\x4f\x96\x4b\x98\xc7\x2f\x39\ +\x62\x60\xbd\x9b\xef\xda\xa2\xdf\xc8\x50\x16\xe7\xfa\xd9\xc8\x8d\ +\x3e\x9a\x2b\x87\xf9\x3c\x43\x4f\x56\x11\xd6\xe2\x44\x4d\x3e\xc6\ +\xaf\xb3\xb6\xd4\xcd\x01\x7d\xae\x26\xb3\x24\xeb\x93\xf6\x82\xa7\ +\xd6\xc3\x37\x8a\x69\x71\x75\xa7\x8b\x95\x4c\x09\xb4\xc6\x49\x2c\ +\xe5\xb8\x6b\x9d\xaf\x51\x16\x15\xfd\x52\xfe\xe2\xe2\x48\x8a\x16\ +\xa6\x86\x52\x6f\x39\xcd\x0c\xba\xd4\xfc\x36\x95\x51\xd0\x2f\xe6\ +\x65\x17\x27\xda\xf9\x5a\xa8\xb5\x5f\xc9\x4e\x97\xda\xf7\x53\x11\ +\x36\x7d\xc7\xe5\xe1\x4f\xb1\x26\x82\xfe\x99\x43\x23\x03\x5a\x1f\ +\xb6\x87\xdd\x16\x3c\xe0\xf2\xde\x2f\x0e\x9d\xfc\x08\x66\x73\x48\ +\xeb\xc7\x8b\x2e\xfd\x0c\xdf\x68\xd4\x56\xbb\x87\x8b\x22\xa3\x0f\ +\xf0\x29\xb6\x68\x7d\x59\x17\x56\xa5\xf3\xb4\x4d\x50\x84\x2d\xf0\ +\x28\x8a\xf8\x9d\x56\x82\x86\x30\x2a\xac\xd4\x4e\x72\x6c\x64\x7c\ +\xe4\xf4\x01\x62\x34\x69\x3c\xea\xe3\x0b\x41\x57\xe6\x68\xfb\xfc\ +\x8f\x45\xdf\x0b\xcb\xc0\xbd\x1a\xaf\x0e\x04\x3d\x85\xf6\x53\x4d\ +\x45\xcf\x59\xa5\x0f\xf0\x73\x8d\x67\x4f\x06\x59\xc9\xe5\x9c\xd3\ +\xbc\xfb\x76\x1e\xfe\x4c\xc4\x78\x5c\x23\xc1\xd7\x83\xab\x62\x87\ +\x58\xc1\x6e\x8f\x23\xbc\xb0\x10\x67\xb3\xa6\x53\x16\xd0\xed\x69\ +\x10\xcd\x9f\x62\x86\x6d\xe6\xa3\x48\xf0\xbe\xe8\xe3\xf2\x20\x8c\ +\x57\x88\x73\x7d\x29\x16\x69\xca\x4f\xe0\x2b\xcc\x66\x42\xc4\x12\ +\xcc\xa2\x5f\xf0\xf2\x0c\x55\xfe\x4d\x3f\x28\x6a\xbb\x46\x2c\x1b\ +\x67\xd5\x70\x30\x24\xc9\x0a\x8a\x22\x95\xe0\x2e\xcd\x27\xda\x27\ +\x2a\xc5\x89\xee\xf7\xc4\x3e\xbf\xc3\x86\x31\xa5\xd6\x47\x2a\x40\ +\x4c\x9c\x98\x4d\x71\x85\x3f\xb3\xbf\x10\x75\xbd\x4e\x2c\xbb\x56\ +\x29\xb7\x20\x52\x09\xaa\xc5\xd7\xe0\x59\x3f\x26\xcb\xc5\x28\x4f\ +\x8b\x58\x56\x1a\x27\xfc\x26\x52\x01\x60\x95\xe0\xc3\x20\x35\xb9\ +\x1b\x5c\x2e\x18\xec\x12\xa7\x3b\xe6\x8a\xf3\x83\x9b\x22\x16\x60\ +\x22\x1d\x82\x17\x4f\xe4\x6a\x2e\x2e\xf6\xfe\x57\x0a\x25\x13\x1c\ +\x16\x5f\x95\x47\x22\x16\x00\x6e\x17\xbc\xe8\xe7\xbc\xdc\x8c\xdd\ +\x20\x18\x4b\x8a\xd1\xfa\x67\x35\x7d\xb1\xd0\x62\x37\x5a\x14\xf3\ +\x81\xe0\xc7\x9d\xb9\x19\x7b\x4e\x30\xb5\x5a\x28\x37\x57\x43\x3f\ +\xb4\x71\xb9\x2b\x7e\x28\x78\xf2\x46\x2e\x86\xca\x84\xe8\x4c\x3f\ +\xd3\x94\x72\x31\x76\x8b\xf4\x5f\x0a\x73\x66\xc6\x05\x13\xc4\xd9\ +\xe3\x1c\xe2\x47\xd2\xdb\xf4\xbc\x50\xee\x16\x91\xfe\x6e\x4a\xad\ +\xd0\x07\x79\xda\x6e\x85\x77\x33\x9b\x04\x33\x37\x0a\xe5\xde\x11\ +\xca\xf5\x58\x8d\xd8\x5d\x25\x78\xb4\xdf\xab\x91\x22\xa1\x07\x70\ +\x52\xe8\xff\xcd\x11\xef\x7f\x83\x45\xfa\x00\xfb\x14\x8f\x52\x7c\ +\xda\x9b\x89\xd9\x02\xad\x87\x85\x72\x52\x43\xf9\x66\xe0\x0b\xed\ +\xbc\x62\xa9\xe0\xd5\x77\xbc\x99\xf8\x89\x60\xe2\x7a\xa5\x54\x29\ +\x7d\x82\xd6\x73\x2c\xd3\x87\xcb\x04\xef\x3d\xf6\x4a\x5f\x55\x0c\ +\x0c\x08\xcb\x1c\xbf\x25\x54\xb4\xcd\x36\x7b\x00\xfe\xa5\xf8\xd5\ +\xee\xe5\xf2\x18\xdd\x8a\x81\x9d\x42\x39\x69\x62\x3a\xba\xf0\x88\ +\x1b\xa4\xf8\x95\x71\xb8\xd6\x61\x9a\x70\xb7\x5f\x13\x4a\xaa\x71\ +\xc0\x4e\xfe\x64\x9b\x3b\x00\xdb\x85\xbc\x5a\x73\x01\xa4\xa2\xbb\ +\x95\x9c\xf3\x98\xae\xe4\xbd\xc2\x80\x6d\xee\x00\xbc\x15\xbc\x00\ +\x07\x95\x1c\x29\xf8\xb0\xc5\x36\xf3\x61\xfc\x83\xa1\x60\x05\x38\ +\x47\x87\x92\xf7\x79\xe1\xda\x5d\xb6\x99\x0f\xe3\x2c\x87\xfd\x08\ +\x30\x53\xc9\x3b\xc4\xa0\x92\x77\xb1\x92\xd3\xcf\x3f\x6d\x33\x1f\ +\x45\x9b\x92\x63\xdc\x3b\x75\x48\x28\x79\x12\x31\xb5\xd4\x07\x82\ +\x4c\xb6\xa0\x7a\x6c\xbc\x78\xc2\x11\x62\xbd\xa7\x84\x72\xea\x34\ +\x43\xa7\x6d\xd6\x19\x38\xad\xe4\x98\x6e\xd7\xc0\x11\x8a\xf6\x08\ +\xe5\x54\x99\xba\x6d\xb3\x76\xf5\xb8\xd4\x34\x8e\x69\x2a\xc0\x80\ +\x70\x65\xfe\x40\xbd\x19\x31\xd3\x21\xba\xa9\x00\x49\x25\xc7\xf8\ +\x21\x8b\x00\x66\xcf\xac\x46\x00\x15\x69\x23\x01\xf2\x69\x5b\xcb\ +\x90\x90\x67\xfc\x0a\xf4\x2a\x79\xd2\xbd\x3d\x69\x20\x89\x3d\x48\ +\x77\xdb\xb0\x8d\x72\x84\xc7\x47\x0a\x84\xff\xd5\x20\x27\x9f\x04\ +\x48\x9b\xde\x20\x49\x00\xe9\x09\x78\xf9\x13\x23\xc4\xff\xd0\x6c\ +\x9b\xb5\xab\xc7\x67\xc4\xd7\x42\x14\x40\x7d\x54\x12\x42\xb9\x14\ +\x8b\x78\x7d\xf4\xaf\x0e\x16\x72\xc2\x36\xeb\x0c\x94\x2b\x39\x3d\ +\xa6\x97\xc6\xe9\x52\xf2\xe4\x38\xfb\x09\xe6\xb2\x80\x3a\x26\xb0\ +\x8b\x17\x6c\xef\xdd\xc8\xea\x71\x97\xf9\xc5\x8f\x2a\x93\x09\x7d\ +\x11\x47\xfb\xfd\xe3\xa0\x10\xa9\x30\x84\x23\x0c\x24\x4a\xf8\xac\ +\x6d\x46\x9e\x50\x2c\x3c\x01\x6d\xa6\x17\x3b\x62\x51\xe3\xc1\x64\ +\x5e\xa0\x4a\xd8\xc6\xe5\x53\x80\x59\xb6\x39\x79\xc2\x97\x85\x3c\ +\x0f\x02\x7c\x48\x9f\x92\x5b\x67\x9b\x93\x27\x48\x6b\x58\x3c\xcd\ +\x0b\xef\x10\x9a\xc1\x48\x77\xe5\xf8\x84\xba\x9c\xfe\x23\xf3\x8b\ +\x1d\x10\x66\xf7\x4b\xf8\xaa\x6d\x56\xc6\xa8\x12\xd6\x30\x6e\xf5\ +\x26\x80\x34\xad\x7c\xbd\xb9\x09\xcb\x58\x28\xe4\x6d\xf7\x66\x62\ +\x9c\xb0\x3c\xee\xa3\xff\x9b\xbe\xc0\x1b\x42\x58\xc4\xf3\x86\x0e\ +\x69\xd5\xdd\x7c\xdb\xcc\x8c\x70\x09\x29\x7f\x81\xb1\x8f\x47\xcd\ +\x9b\x85\xff\xd4\xdb\xe6\x66\x84\x5b\x85\xe8\xf4\x9f\xbd\x9b\xa9\ +\x14\x96\xc8\xf7\x88\x83\xa2\xfc\x42\x5c\x5c\x2a\xf7\xc5\x5c\x4c\ +\x49\x6b\x44\x96\xd9\xe6\x97\x15\xb7\x0a\x5e\x1f\xc8\xcd\xd4\x62\ +\xc1\xd4\xc9\xbc\x9a\xf7\x53\xe1\xf0\x77\xc1\xeb\x1f\xe7\x66\xac\ +\x84\x4e\xc1\xd8\x3d\xb6\x39\xba\xe2\xdb\x82\xc7\x43\x5c\x98\xab\ +\xb9\xd5\x82\xb9\xe3\xc2\x54\x43\xbe\x20\xce\x7e\xc1\x63\x1f\x4b\ +\xe6\xa7\x8a\x27\x39\x3c\x64\x9b\xa7\x16\x77\x0b\xde\xa6\xfd\x0d\ +\xe3\x1e\x13\x0c\x0e\xe6\xd6\xa6\x86\x8e\xa9\x9c\x16\xbc\xcd\xe1\ +\x03\x98\x89\xe9\x9c\x15\x8c\xee\xca\xab\x18\xd0\x08\xfe\x20\xde\ +\x7f\xdf\x4b\xb6\x9e\x14\xcd\x36\xda\x66\xab\xe0\x66\xd1\x4f\x0f\ +\x43\x20\x1d\xe4\x07\x6b\xc0\xc2\x4a\x70\x37\xcc\x14\xbd\x1c\xe4\ +\xaa\x20\x8c\xff\x48\xd4\xf6\x48\xc0\x87\x64\xf8\xc1\x78\xde\x16\ +\x7d\x7c\x28\x18\xf3\x71\x61\xe9\x69\x9a\x34\x5b\x2c\xad\x07\x57\ +\x21\xef\x1d\x3d\x1a\xdc\x9e\xf6\x39\xc2\xf8\x2a\x4d\x9a\x16\xeb\ +\x8b\x62\x01\x7e\x26\xfa\x96\xe6\x7b\x41\x56\xf2\xa8\xa6\x92\x5f\ +\xd9\x66\xaf\xa5\xef\xf3\xf3\xf7\x49\x94\xb0\x57\x53\xd1\xbd\x56\ +\xe9\xaf\xd5\x78\x15\x42\x0b\x35\x53\x7b\x36\x68\xb3\xb5\x17\xe1\ +\x7e\x8d\x47\x21\x7d\xa3\x6e\xd6\x54\x97\xe6\x19\x4f\xa7\xc9\x05\ +\x05\xfd\x89\x26\xf7\x85\x55\xe5\x5a\x6d\x95\x9b\x23\x9e\x2a\x29\ +\xd1\x9e\x1a\x90\xe6\xf5\xf0\xfa\xa9\x0e\x7f\xd4\x56\x7b\x38\xc2\ +\x69\xf3\x8b\x78\x53\xeb\x47\x9a\x64\x98\x9d\xb4\x71\xbc\xa2\xad\ +\x78\x80\xc6\x48\x5a\x83\x45\x59\x8f\x6d\xed\x09\x53\x82\x32\x4d\ +\xaf\xeb\xe3\xd4\xea\x67\xa7\xae\x01\xa6\xf1\xfb\x2c\xe4\x23\x90\ +\xa0\x52\xfb\x49\x4c\x93\xa6\x9f\x55\x21\xad\x16\x8b\x73\xb7\x87\ +\x53\xaa\x43\x95\xa0\x9c\xed\xae\x95\x77\x70\x7b\xc0\xa7\x8a\x39\ +\xdc\x24\xce\xf6\xb9\xa5\x24\xf3\xc2\x93\x60\xbc\x4b\x73\x38\xd2\ +\x28\x2e\x09\xe8\x49\x70\x58\xcc\xbb\x1e\xc9\x47\x20\x41\x11\xeb\ +\xb2\x3a\x70\x9c\x07\xb8\xd2\x57\x2d\x33\x69\x12\xe7\xf9\xc7\xa6\ +\x1d\xc2\x1e\xa7\x08\x24\x80\x06\xa3\x93\xa4\xf7\xb1\x94\xcf\x79\ +\xb6\x3d\x83\x3b\xd9\xa9\x19\x84\x8d\xfd\xf6\xdc\x47\x8c\x6b\x6c\ +\x49\x70\x19\x07\x0c\x1f\xc7\xa3\xb4\x70\x1b\x57\x64\x39\xdb\xa7\ +\x98\x1a\x6e\xe1\x71\x97\xe3\xf2\xc6\xa6\x23\xa3\x4d\x5d\x80\x12\ +\x78\xfb\x96\x4f\xe4\x11\xbe\xef\xa1\xfc\x10\x87\x69\xa3\x9d\x6e\ +\x7a\xe8\xa2\x9b\xd4\xf0\xc1\xca\xe5\x54\x71\xa9\xb8\xb6\x47\x8f\ +\x97\x68\xc8\x58\xb0\x7b\x0d\x5b\x34\x61\x9b\x5e\xbe\x11\xee\x2a\ +\xd6\x3a\xde\xcb\xa9\x91\xf2\x93\x8e\x51\xaf\xdc\x2a\x6b\x2f\x02\ +\x14\xb3\x24\xa0\x9f\x53\x30\x49\x43\xfc\x56\x13\x9e\xb1\x28\x01\ +\x54\xb1\xd1\xa0\xd1\xf2\x9f\xb6\xba\x4e\x75\x5a\x95\x00\x2e\x67\ +\xbd\xcb\x91\xa7\xfe\x53\x2b\x57\x67\xf5\xc1\xb2\x04\x50\xcd\x13\ +\xe2\x91\x46\xfe\xd2\x20\x1b\xf9\x92\xa1\x07\xd6\x25\x80\x04\xf5\ +\xb4\x06\xf6\x42\xbc\x4b\x93\xb0\x4b\x31\xcf\x25\x00\xa8\x61\x05\ +\xfb\x7d\xc9\xd0\xce\x9a\x1c\x23\x91\x79\x22\x01\xc0\xf9\x2c\xa6\ +\x99\x3d\x9e\x88\x1f\x63\x03\x3f\xf0\x78\xd7\x03\x93\x20\xac\x49\ +\x8d\x29\xd4\x72\x29\x35\xd4\x52\x43\x82\x49\x63\x3a\x2d\xbd\x24\ +\x39\xcd\xa1\xe1\x9f\xda\x6a\xe3\x68\x20\x35\x5a\xec\x1a\x99\x21\ +\xc1\x85\x4c\xa7\x22\xc4\x79\xa4\x3c\x7a\x11\x6c\xa1\x20\x41\x41\ +\x82\x82\x04\x05\x09\x0a\x12\x14\x24\x28\x48\x50\x90\xa0\x20\x41\ +\x41\x82\xff\xe1\x5a\xed\xcf\x77\x85\x1a\x50\xcb\x27\xe8\x25\xe8\ +\xcc\xa3\x53\xf3\x2d\x49\xf0\x94\x6d\xd7\x6c\x4b\x70\xcc\xb6\x63\ +\xb6\x25\x38\x63\xdb\x2d\xdb\x12\xbc\x63\xdb\x29\xdb\x12\xdc\x61\ +\xdb\xa5\xe8\x25\xc8\xdc\x1b\xb5\x21\x2f\xf7\x42\x84\x8c\x19\x3c\ +\xcd\x71\xfa\xd9\xcb\x1d\x38\xf0\x5f\xf7\xf0\xdc\xf4\x49\xe5\xa7\ +\x81\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x63\x72\ +\x65\x61\x74\x65\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\x54\ +\x30\x36\x3a\x35\x37\x3a\x34\x35\x2b\x30\x30\x3a\x30\x30\xcd\x7b\ +\x50\xc3\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\x3a\x6d\ +\x6f\x64\x69\x66\x79\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\x32\x36\ +\x54\x30\x36\x3a\x35\x37\x3a\x34\x35\x2b\x30\x30\x3a\x30\x30\xbc\ +\x26\xe8\x7f\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\ +\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\ +\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\x45\x4e\x44\ +\xae\x42\x60\x82\ +\x00\x00\x0c\x48\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x80\x00\x00\x00\x80\x08\x04\x00\x00\x00\x69\x37\xa9\x40\ +\x00\x00\x00\x04\x67\x41\x4d\x41\x00\x00\xb1\x8f\x0b\xfc\x61\x05\ +\x00\x00\x00\x20\x63\x48\x52\x4d\x00\x00\x7a\x26\x00\x00\x80\x84\ +\x00\x00\xfa\x00\x00\x00\x80\xe8\x00\x00\x75\x30\x00\x00\xea\x60\ +\x00\x00\x3a\x98\x00\x00\x17\x70\x9c\xba\x51\x3c\x00\x00\x00\x02\ +\x62\x4b\x47\x44\x00\x00\xaa\x8d\x23\x32\x00\x00\x00\x09\x70\x48\ +\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\x95\x2b\x0e\x1b\x00\ +\x00\x00\x07\x74\x49\x4d\x45\x07\xe4\x03\x1a\x06\x2b\x19\x2f\x93\ +\x17\x04\x00\x00\x0b\x16\x49\x44\x41\x54\x78\xda\xd5\x9d\x6b\x70\ +\x55\xd5\x15\xc7\x7f\xf7\x06\x02\xe1\x25\xcf\xf0\x10\x93\xf0\x8c\ +\x20\x3e\xe8\xe0\x58\x6c\x3b\x80\x96\xd1\xd2\x32\x0e\x16\x04\x0b\ +\xed\x20\x5a\xad\xb5\x23\xa2\x25\xd8\x19\x3a\x50\x18\xac\x65\x1c\ +\x06\x06\x28\x62\x6b\x45\x90\xe7\x88\x56\x6d\x0b\x15\x21\x32\xa8\ +\xe5\x21\x10\x4a\x0d\x48\x00\x43\x40\x81\xf0\x08\x90\x84\x67\x92\ +\xd3\x0f\x37\x37\xb9\x7b\x9d\x7b\xee\x79\x9f\x73\xfd\xef\x4f\xe7\ +\xec\xbd\xd7\xda\x6b\x9d\x7d\xf6\xd9\x6b\xed\xb5\xf7\x89\xe0\x17\ +\xda\x33\x9e\xfb\xb9\x8b\x4e\xc0\x19\x8a\xf8\x88\x95\x54\xf8\xc6\ +\x2d\xcd\xd0\x92\xb9\x5c\x46\x13\xa9\x9a\x97\x69\x19\x76\xd3\x82\ +\x40\x7f\x0e\xeb\x84\x8f\xa7\x12\x6e\x0d\xbb\x79\x7e\xe3\x4e\x2e\ +\x18\x8a\xaf\xa1\x71\x9e\xdb\xc3\x6e\xa2\x9f\xe8\xc8\x89\x94\xe2\ +\x6b\x68\x94\xd1\x21\xec\x66\xfa\x87\xb7\x4c\xc5\xd7\xd0\x78\x33\ +\xec\x66\xfa\x85\x01\xd4\x59\x52\x40\x1d\xfd\xc3\x6e\x6a\x1c\x4d\ +\x6c\xd7\xc8\x62\x24\x3d\x0c\xf2\x86\x13\x11\x77\x8a\xd9\x48\x84\ +\x07\xe9\xa7\xdc\x8d\xb0\x80\x8f\x0c\x68\x1c\xe5\x03\xae\x86\xad\ +\x16\x63\xf4\xa4\xc4\xd2\x33\x8e\xa5\x3f\xd5\x2b\xb8\x29\x73\x6d\ +\xd4\x3a\x64\xa8\xe0\x34\xc0\x0e\x1b\x82\x6c\x4e\xe8\x0f\x11\xb6\ +\xd8\xa8\xb9\x3d\x6c\x31\x8d\xd0\xd7\x86\x10\x1a\xe3\x94\xba\x3f\ +\xb3\x55\xb7\x4f\x50\x22\x45\x6d\x95\x6e\x67\xab\xf4\x31\xe5\xea\ +\xa4\xad\xba\x6d\xd3\x53\x01\xc5\x5c\xb7\x5c\xb6\x8a\xfd\xca\xb5\ +\x66\x83\xcf\x75\x0e\xa6\xa7\x02\x2a\xd9\x6c\xb9\x6c\x01\x55\x8e\ +\x5b\xb5\x99\xca\xf4\x54\x00\xbc\x6d\xa1\x4c\x2d\x07\x18\xcb\x12\ +\x17\xad\xb2\xc2\x25\x24\x74\xe0\x86\x32\x58\xfd\xcf\x72\xcd\xa1\ +\x62\x98\x1b\x9a\x90\xf7\x85\x92\x73\x83\x8e\xc1\x09\x64\xb7\x07\ +\x9c\xe3\x63\xe5\xfa\x36\x31\xc5\x71\x82\x7c\x31\x2f\x2c\xe4\x6c\ +\xfa\x2a\x00\xd6\x8b\xeb\x87\x5d\xb7\x61\x8c\x09\x87\x34\x43\x67\ +\x6a\x94\x0e\xbb\xc7\x62\xbd\x21\xe2\x15\x18\xd2\x90\xb3\x47\xb9\ +\x5f\x43\xe7\xb0\x45\x34\xc3\x56\x21\x4a\x2f\x4b\xb5\xf2\x0d\xa6\ +\x3a\xbd\xc4\xfd\xad\xc1\x0a\x63\xff\x15\xd0\x8f\xd1\x3f\xb5\x54\ +\xeb\x4b\x45\xb4\x42\x4a\x0c\x6a\xa7\xf1\x17\x20\x8e\xee\xc2\xe8\ +\xdd\x61\xb1\x5e\x2e\x45\xf5\x35\x8a\xc8\x69\xb8\xab\x5a\x17\x75\ +\x74\x0f\x5b\x3c\x2b\xf8\x4c\x34\x3a\xc7\x62\xbd\x4c\xc6\x32\x8d\ +\x47\xc8\x6c\xb8\x93\x23\x94\xf9\x59\xd8\xa2\x59\xc3\x0b\xe2\xbd\ +\x7d\xce\x31\xa5\x29\x82\xd2\xf3\x61\x8b\x66\x0d\x79\xe2\xb9\x6d\ +\x73\x4c\xe9\x13\xa1\x80\x34\xf6\x04\xa8\xd8\xa5\x34\xbb\x96\x6e\ +\x8e\xa8\x74\xa1\x56\xa1\xb3\x33\x78\x41\x9c\x7c\x05\x40\x4e\x56\ +\xa2\x8c\x72\x44\x65\xb4\xe0\x9f\xe6\x53\xa0\x44\xf4\x11\x5d\x77\ +\x8b\x23\x2a\x85\x82\x4a\x7e\xd8\x62\xd9\xc1\x3e\x31\x7f\xcb\xb6\ +\x4d\xa1\xa3\x30\xac\x8a\xc2\x10\xc3\xe9\x2b\x20\xbb\x6b\x06\x0f\ +\xd9\xa6\xf0\xb0\xf0\x49\x87\xf2\x02\x44\x1c\xd7\xbc\x4d\x98\xc2\ +\x45\xac\xb1\x49\x61\x1c\x77\x29\xd7\x03\xf8\x22\x0c\x15\x38\x45\ +\x36\x15\xb6\xdc\x9c\x66\xa9\xc2\xc1\x4b\x14\x22\x9e\xe2\xa2\xa7\ +\xe2\x6b\x68\x5c\xe4\xc9\xb0\xc5\xb2\x86\x28\xaf\x79\x2e\x7c\x3c\ +\x2d\x75\x31\x2a\x05\x86\x85\xbe\x89\xaf\xa1\xb1\x20\x6c\xf1\xcc\ +\x30\xda\x57\xf1\x35\x34\x8b\xe6\x75\x48\xc8\xe2\x98\xef\x0a\x38\ +\x41\x8b\xe0\x04\xb2\xfb\xc6\x4d\xb0\x6c\xfa\x3a\xc7\xcd\x3c\x1a\ +\x9c\x02\xec\x42\xba\xc3\x4e\x33\x9e\x36\xae\x28\xb6\x61\x02\xa7\ +\x05\xd5\xc2\xb0\xc5\x34\x42\x33\xae\x2a\x0d\xbd\xe4\x91\xf9\xda\ +\x93\x4a\x85\xee\x95\x04\x97\x49\x5a\xa1\x9f\x78\x52\xbf\xf7\x8c\ +\xf2\x0c\x41\x39\xb0\x58\x32\x77\xab\xc3\xde\xd9\xef\xd2\xaf\xd8\ +\x3e\x3d\x15\x20\x97\x3b\xbd\x6b\xa6\xa4\x14\xd8\xe2\xa8\x3d\x64\ +\x8b\x8e\xba\xd9\x85\x31\x95\x88\x88\xce\x33\x10\xe0\xea\xa0\x3d\ +\x1c\x15\x0d\x9d\x4b\x53\xd7\x34\x9b\xf2\x8a\xa0\x7a\x38\x38\x81\ +\xec\x46\x89\xfd\x8b\x67\x94\xeb\xa9\x8c\x64\x23\xa7\x5c\xb4\xa0\ +\x0b\x3f\xd2\x79\x82\xfe\x19\x9c\x02\xec\xa2\x9f\x70\x63\xfa\x91\ +\x6a\xd2\x3b\x9e\x78\x85\xef\x0a\x58\x16\xb6\x88\xa9\xd1\x56\x37\ +\x0e\x78\x9b\x8e\x05\xf7\x09\x04\x27\x3e\xc1\xe6\x94\xfa\xda\xa2\ +\x23\x1e\x0c\xab\x3e\x62\x0c\x67\x7d\x7f\x05\x2a\xd2\xd5\x33\xd4\ +\x81\xf7\x7d\x17\x3e\x9e\xfe\x9e\x7e\x21\xf5\x77\xfb\xfc\xee\xcb\ +\x54\xc6\xdd\x61\x8b\x9c\x88\xc7\xb9\x6e\xd0\xd0\x2a\x96\xba\x08\ +\x6c\xcd\x63\x01\x57\x0c\x28\x5f\xe1\xb1\xb0\xc5\x8e\x21\xaa\x9b\ +\xa9\xc5\xd3\x65\xe6\x78\xd0\x55\x73\x58\x64\xa8\x84\x97\x3c\x9a\ +\x6c\xbb\x40\x53\xd6\x25\x6d\x5a\x1d\xab\x3d\xf4\x0e\xe5\xb1\xc1\ +\x40\x05\x7f\x0e\xd7\x4f\x9c\xc9\xbb\x49\x9b\x55\xca\x0f\x3d\xe7\ +\x35\x86\xf2\xa4\xbc\x56\x85\xf7\x61\xcc\x34\x78\x2e\xaf\xb9\x74\ +\x83\x19\xa1\x23\x6f\x27\xe5\xb7\x36\x9c\x5e\x10\x65\x6d\xe0\x43\ +\x53\x84\x69\x22\x0e\x31\x96\xdc\x44\x1e\x3b\xc6\x92\x24\x0d\x39\ +\xca\x9d\xbe\xf3\xfd\x71\xd2\x9d\x87\x2f\xfa\xc3\xcc\x78\x8c\x7d\ +\x81\x57\x74\xf7\xf6\x32\xc2\xa6\xe9\xdb\x9f\x07\xd1\xd8\xc8\x01\ +\x5b\xb5\xf2\xd9\xa0\x73\xb7\x6a\xcc\xe7\xb4\x2b\x59\x2f\xf3\x35\ +\xbb\xc5\x36\x0e\x43\x0c\x4f\xd2\x11\xb7\xd8\x7e\xf3\x0b\xea\x43\ +\x20\xae\x33\xd5\x66\xcd\x5c\xbe\xf2\x69\x82\xb5\x87\xa7\xcd\x07\ +\xd5\x1e\x9c\x4b\x22\x7e\x96\x4d\x21\xee\x4b\x88\x25\xab\x63\x98\ +\xcd\xda\x79\xbe\xa9\x40\xe3\x4b\x06\xa7\x62\xdd\x84\x4f\x75\x55\ +\xb6\xd3\xda\xa6\x00\xb0\x5a\xa1\xb0\xd2\x76\xfd\x3c\x1f\x97\xe1\ +\xae\x32\xc1\x98\xf1\x0c\x5d\xf1\x03\x36\xb7\x4b\xc5\x50\x28\x7a\ +\x90\x5d\xe4\xe8\x56\x8c\xbc\x4c\x35\x8c\x48\xce\x76\x90\x08\x5d\ +\xd2\x38\x4f\x5f\x07\xe2\x4b\x05\xd8\x5d\xee\x6a\xc2\x6e\x1f\xc5\ +\xd7\xd0\xb8\x40\xae\x9e\x6d\x06\x9f\xeb\x34\x35\xdc\x91\xf8\x6e\ +\x15\xf0\x84\xcf\xe2\x6b\x68\xac\xd0\xb3\x9d\xac\x2b\xf4\x07\x87\ +\xe2\xbb\x53\x40\x94\xb2\x00\x14\x50\x2b\xed\xd8\x4e\xba\xc8\x9f\ +\xff\x38\xd8\x5e\xed\x85\x02\xbe\xab\x6b\xec\x6a\xf2\x5d\x4e\x88\ +\xbb\x32\x43\x67\xd4\x17\xa8\x45\x16\x88\xec\x6a\x8b\xbb\x41\x92\ +\xa1\x99\x78\x87\x77\xd9\x32\x69\x66\x8a\x96\x2c\x74\x25\x7a\x23\ +\xc6\x08\xba\xca\xd0\xdc\x8b\x6b\x22\xfb\x77\x0e\xd9\x34\x65\x5a\ +\x92\x11\xfc\x38\xcf\x58\x7e\x86\xcb\x94\x9a\x17\x3d\x3c\x7c\x45\ +\xdd\xc4\x7d\x34\x31\xeb\x0d\xd1\xe0\x62\x87\xab\xf4\x79\xec\x35\ +\x7c\xeb\xb6\x59\xdc\x12\xb5\x51\xbc\x88\xde\xe1\x65\x85\xf2\x95\ +\xc6\x8c\xee\xba\xe7\xef\x6c\xf4\xef\xc7\xc9\x94\x03\xcf\x11\x4b\ +\x9b\x62\xde\x51\xea\x78\xb9\x93\x58\x0d\xf1\x4b\xd8\xa1\x28\xdd\ +\x5e\xce\xc2\x54\xda\xa7\x38\x42\x27\x9e\x76\x5b\x08\x82\x5a\x2c\ +\xea\x0c\xb1\xc0\xdb\x0a\x3a\x71\x46\xa1\xdb\xb0\xc1\xbb\xa5\x6e\ +\xfc\xff\x9e\x23\x06\xaf\x5b\xfa\xfc\xcc\x31\xa5\xf3\x0b\x51\xa3\ +\xd4\x13\x33\xbc\x13\x9b\x04\xdd\xbf\xc6\xcd\xe1\x89\xbc\xa1\x14\ +\xdd\xc2\xfd\x0e\x18\xdc\xce\x3e\x4b\x4e\xcc\xeb\xf4\xe4\xeb\x94\ +\x25\xb2\xf9\x86\x0c\x51\xe7\x5d\x0e\x26\xbe\xb3\x36\xd1\x84\x5c\ +\x46\xe9\xe2\x0e\x1a\x62\x12\xe5\xde\x9d\x91\x8e\x98\xe8\x5d\x28\ +\x27\x78\x8b\xd5\x49\x7c\x7d\x33\x4d\x69\xad\xb2\xd4\x97\xdc\xa5\ +\xd2\xf8\x30\xdf\x5b\x64\x1c\x76\x38\xe5\x90\x87\x28\xbd\x5a\x6f\ +\x42\xdf\xc4\x1a\x91\xb3\xd7\x94\xd6\xad\xba\x41\xd9\xfb\x34\x31\ +\xce\xec\xb7\x22\x63\x9a\x23\xf1\x73\x05\x95\xad\x09\x6a\x6c\xc6\ +\x7f\x95\xbc\x5a\x5a\x99\xd2\x9b\xec\xb3\xf8\xef\x34\xbe\xae\xea\ +\x0b\x50\x97\xcc\x4a\xb2\x80\xc1\x82\x81\x1a\xf3\xfb\xb4\xc8\xed\ +\x6d\x4a\xaf\x87\xe1\x6a\x94\x37\x69\x3c\xc4\x96\xc7\xb3\x85\x7f\ +\xe4\x53\xab\x7e\x33\x01\xf9\x71\x53\x3d\x78\x47\x44\xae\xd9\xdc\ +\x2e\x93\xb5\x3e\xaf\x07\x2c\x24\x2f\xa6\x80\xa1\xe2\x8d\x77\xba\ +\x7d\x59\xba\x2c\xef\x50\xae\xf6\x53\x9b\x70\x55\x6d\x1a\x08\xf5\ +\x98\xef\x8b\xa3\xed\xe2\x9f\xe3\x45\xa2\x6b\x38\x3d\x13\xa2\xad\ +\xd8\x4f\xfa\x8d\x88\xf5\xf8\x63\x42\xde\x14\x13\x5a\x19\x94\xfa\ +\x3e\x04\x6a\xd4\xc4\xcc\xe1\xfd\xca\xcd\x32\x17\x3a\xdd\x2e\x18\ +\x14\x33\x24\x61\x5e\x90\x41\x01\x15\x68\x94\xf3\x2b\xd3\xd9\xc2\ +\xbd\xba\xc6\xfa\x63\x0e\xbf\x08\x2d\x44\xdc\xd7\x32\x17\x2c\xa6\ +\x25\xd1\x72\x25\x07\xf8\x98\x45\x3c\x40\x26\x90\x41\x3b\x4b\x53\ +\xa5\x00\xcd\xe1\x41\xe2\xd6\x6f\x5c\x90\x6f\x9d\xd2\x8d\x79\x82\ +\x27\x2d\xbb\x57\x96\x2b\x35\xfd\x33\x87\x4b\xa3\x0c\x10\x05\x8a\ +\x5c\x10\xaf\x64\x76\x8a\xdc\x9b\x59\xca\x26\x8b\x41\xb0\xea\x16\ +\xba\x62\xaa\x3d\x53\x80\x1a\xe0\xdd\x39\x2a\x82\x12\xdd\x6e\x60\ +\x5d\xcc\xaa\x94\xf9\x43\xd9\x61\x69\xa7\xf9\x65\xe5\xca\x89\x53\ +\xde\x08\xea\xc0\x5c\x2d\x0f\xc1\x74\x13\xf4\x1a\x43\x16\xef\x99\ +\x8c\xbd\x3b\x68\x6e\x41\x91\x6a\x1d\x1f\xcd\xe1\x42\xe5\x86\x17\ +\x3b\x00\xa2\xcc\x32\x99\xc3\xa5\x95\x39\x7c\x48\x71\x0d\xaf\x67\ +\xb4\x27\x9a\xee\xcd\x4c\x46\x19\x3a\x3e\x2e\xd3\x37\x7d\xcc\xe1\ +\x53\x8a\x46\x96\xba\x14\x3c\xca\x30\x26\xf1\x7d\x22\x40\x16\x3f\ +\x61\x0a\xf3\x92\x9a\xc3\xe6\x9b\x6d\x02\x33\x87\xd5\x60\x84\xf9\ +\xae\xc4\xef\xc6\xb6\x7a\x3a\x1b\x94\xc1\x46\x6f\x0e\xef\x32\xa5\ +\x15\x98\x39\xac\xb2\x79\xc9\x95\x02\x12\xdf\x30\xf5\x6b\x20\xcd\ +\xe1\x3a\x0b\x1e\xe7\xe7\x7c\x16\xff\x83\xd8\x94\x4c\x5d\x0a\x9d\ +\x6d\xda\x2c\x63\x0c\x10\x22\xaa\x0e\x70\x69\x0e\x9b\x9b\xdc\x01\ +\x99\xc3\xea\x46\x28\x37\x33\xae\x2e\xca\x55\x84\x9e\xca\xb5\xfc\ +\xc0\x9a\x7d\xdb\x03\x33\x87\xbd\x53\x80\xfc\x7b\xc0\x40\xe5\x4a\ +\x7e\xc6\xce\x9b\x50\x9b\x14\x94\x39\xac\x9e\xe6\x68\x3f\x8e\x23\ +\x91\xa0\x6a\x56\x25\x9a\xc3\xb7\x88\xc1\xb6\xca\xe4\xe9\x06\x68\ +\x0e\xab\x26\xac\xbb\xc3\xec\x8a\x04\x83\x98\x39\x1c\x65\x84\x4e\ +\x9c\x4d\x26\x94\x02\x34\x87\xd5\x60\x48\x77\x53\xe1\x82\x24\x5a\ +\xae\xa2\xda\xe8\x03\x94\x02\x01\x9a\xc3\x33\xc4\xad\xb6\x2e\xc8\ +\x77\x30\xf9\xb9\x42\x3c\x9d\x30\xb5\x06\x02\x34\x87\x8b\x45\x81\ +\x81\x8e\xc8\xc6\x70\x8e\xe9\x96\xca\x3d\x6f\x7a\x7e\x7c\x80\xe6\ +\xb0\x3c\xbb\xe7\x01\x57\xe4\x17\x5b\x38\x4d\xe8\x75\xd6\x99\x96\ +\x09\xd0\x1c\x6e\x2a\xba\x6d\x91\x4b\x06\x2d\xf8\x47\xca\xee\xbf\ +\xdc\x92\x57\x28\xd0\xd5\x61\x75\x18\xac\x73\x7d\xb2\x7b\x13\xe6\ +\x18\xcc\xe1\x2e\x33\xd5\xe2\x0e\x90\x40\x57\x87\x7f\xce\x72\xa1\ +\x7d\x37\x7e\xc1\x18\x06\x50\xc0\x58\x65\xbe\x7f\x8d\x37\x99\xab\ +\x5b\x1e\x31\x42\xa0\xab\xc3\xed\x85\x3d\x50\xed\xd1\x96\xb5\x42\ +\x85\xaa\xdd\x80\x8b\x80\xcc\xe1\x28\x70\x9e\x0f\x15\xd6\x2d\x3c\ +\x3c\x1a\xc3\x39\x66\xd9\x38\xca\xdf\x29\x66\xc6\x79\x0c\x13\x9a\ +\xa9\xe5\x5e\x0f\xc8\xbb\xeb\x01\x01\x99\xc3\xd1\xfa\xa6\xaa\x0e\ +\x8a\x28\x7f\x71\x10\x1d\x2e\xa1\x3e\xc1\x6b\xb6\xeb\xbf\xc7\x0d\ +\x0f\x1e\x83\x31\xd6\x90\xf0\xd3\x07\xfd\x01\x59\x1f\xba\x36\x46\ +\x67\x29\xf4\x66\xda\xac\x9d\xc9\x4e\x9f\x7b\xc0\x79\xf2\x1a\xd9\ +\x45\xd8\xac\x2b\xb0\xd2\x45\x98\x2c\x40\x6b\x8e\x34\xd0\x3a\x62\ +\x21\x20\x42\xc5\x53\x01\x0c\x82\x8a\xed\xdb\x83\x2a\x5d\x81\x4d\ +\x2e\xe7\x60\xd9\xac\xe0\x22\x17\x58\x4e\x27\x9b\x35\x03\x5d\x1d\ +\x8e\xe3\xd9\x24\x45\x0e\x89\x63\x2f\x83\x42\x80\xe6\x70\x23\xa2\ +\x22\x3e\x33\x96\x6e\x30\xcf\x76\xf7\x75\x8f\x50\x82\xa5\x21\xab\ +\xc1\xad\xad\xa6\xe3\xfc\xda\xf6\x96\x29\x77\x08\xcc\x1c\x96\xd9\ +\xed\x85\x8b\xac\x31\x9d\x62\x3a\xb7\x04\xa6\x80\x10\x82\xa5\xe3\ +\xe8\xa2\x8b\xf3\x68\x4c\xb5\x6c\xe6\x09\x9f\xff\x00\x90\xc3\xb3\ +\x6c\x13\xde\x45\xff\x82\xa5\xcf\x24\x2b\xd2\xdc\xf4\xb7\x89\xfb\ +\x99\xc7\x23\x1e\x9f\x04\xdf\x8b\x71\x2c\xe4\xa0\x01\x47\xbf\xcc\ +\xe1\x7d\xc9\x8d\xd3\x08\x05\xcc\xb2\xb0\x76\x73\x96\x7d\x94\x70\ +\x98\x12\xbe\xa2\x9c\x72\xec\xfc\x4e\x2b\x8b\xce\x74\x25\x9b\x3c\ +\xfa\x91\xcf\x1d\x26\x87\xe7\x1c\xe3\x21\xf6\x79\x20\xfe\x2a\xb1\ +\xed\xff\x6f\xc6\xd6\x79\x7f\x5e\xe5\x07\xb6\xc8\xd7\x50\xce\x19\ +\x2e\x51\x45\x15\x15\x68\x5c\x50\xc4\x6d\x4e\xcc\xb3\x93\xc5\x4d\ +\x74\xa2\x9b\xed\x6d\xb8\xfe\x98\xc3\x29\x0f\x04\x8f\x30\x29\x80\ +\x43\x73\xc2\x4c\xa5\x34\x33\xd3\x5b\x2b\x26\x5b\xf8\x8f\xec\xb7\ +\x35\x8d\xb7\xd6\x79\x9a\xf3\x38\x9f\x58\xfc\x9d\xea\xb7\x29\xad\ +\xb7\x77\x40\x4b\x1f\x66\x8b\x7f\x0a\xf8\x99\x3e\xa7\x80\x9e\xcc\ +\xf7\x95\x83\xa3\xf9\x6d\x77\x7e\xc9\x3a\x8e\xfb\xd6\xac\x32\x56\ +\x32\x31\x21\x8e\x6c\x46\xd2\x03\x35\xbc\x78\xfa\xad\xe2\x03\x9d\ +\x33\x74\xe3\x1e\x06\x91\x4f\x5f\xfa\x58\x88\xf9\x4a\x8d\xeb\x94\ +\x70\x80\x62\xf6\xb0\x33\xc9\x8f\x39\x07\xb2\x84\x7b\x5c\x72\x50\ +\x71\x8c\xe9\xac\x44\x73\xa7\x80\x46\x44\xc9\xa1\x3b\x5d\xe9\x46\ +\x57\xba\xd0\x86\xd6\xb4\xa3\x0d\xad\xeb\xc7\xd7\xb6\x44\x80\x4b\ +\xd4\x72\x95\x2b\xd4\x71\x11\xa8\xe4\x12\x67\x39\x4b\x39\x27\x39\ +\x46\x19\x27\xa9\x31\xe1\xf1\x1d\x1e\x65\x30\xb9\x74\x71\xe5\xa1\ +\x38\xc3\x29\x76\xf2\x3e\xff\x4e\xf4\x4e\xfd\x1f\x81\x10\xe8\x54\ +\x3e\x6d\xdd\xfd\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\x65\ +\x3a\x63\x72\x65\x61\x74\x65\x00\x32\x30\x32\x30\x2d\x30\x33\x2d\ +\x32\x36\x54\x30\x36\x3a\x34\x33\x3a\x32\x35\x2b\x30\x30\x3a\x30\ +\x30\xc3\x71\x26\xfe\x00\x00\x00\x25\x74\x45\x58\x74\x64\x61\x74\ +\x65\x3a\x6d\x6f\x64\x69\x66\x79\x00\x32\x30\x32\x30\x2d\x30\x33\ +\x2d\x32\x36\x54\x30\x36\x3a\x34\x33\x3a\x32\x35\x2b\x30\x30\x3a\ +\x30\x30\xb2\x2c\x9e\x42\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\ +\x66\x74\x77\x61\x72\x65\x00\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\ +\x61\x70\x65\x2e\x6f\x72\x67\x9b\xee\x3c\x1a\x00\x00\x00\x00\x49\ +\x45\x4e\x44\xae\x42\x60\x82\ +" + +qt_resource_name = b"\ +\x00\x02\ +\x00\x00\x07\xb9\ +\x00\x75\ +\x00\x69\ +\x00\x09\ +\x0a\x6c\x78\x43\ +\x00\x72\ +\x00\x65\x00\x73\x00\x6f\x00\x75\x00\x72\x00\x63\x00\x65\x00\x73\ +\x00\x17\ +\x0d\x8e\xe6\xc7\ +\x00\x65\ +\x00\x78\x00\x70\x00\x6f\x00\x72\x00\x74\x00\x5f\x00\x76\x00\x65\x00\x6e\x00\x64\x00\x6f\x00\x72\x00\x73\x00\x5f\x00\x69\x00\x63\ +\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0f\ +\x0f\x38\xe9\x27\ +\x00\x66\ +\x00\x6f\x00\x6c\x00\x64\x00\x65\x00\x72\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x13\ +\x01\xa4\xee\xa7\ +\x00\x61\ +\x00\x64\x00\x64\x00\x5f\x00\x76\x00\x65\x00\x6e\x00\x64\x00\x6f\x00\x72\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\ +\x00\x6e\x00\x67\ +\x00\x0d\ +\x02\xf7\xfd\x47\ +\x00\x75\ +\x00\x6e\x00\x64\x00\x6f\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x16\ +\x0c\x5c\x0d\x87\ +\x00\x72\ +\x00\x65\x00\x6d\x00\x6f\x00\x76\x00\x65\x00\x5f\x00\x76\x00\x65\x00\x6e\x00\x64\x00\x6f\x00\x72\x00\x5f\x00\x69\x00\x63\x00\x6f\ +\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0d\ +\x01\x61\xdd\x07\ +\x00\x6d\ +\x00\x61\x00\x69\x00\x6e\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x17\ +\x0a\xab\x66\xc7\ +\x00\x69\ +\x00\x6d\x00\x70\x00\x6f\x00\x72\x00\x74\x00\x5f\x00\x76\x00\x65\x00\x6e\x00\x64\x00\x6f\x00\x72\x00\x73\x00\x5f\x00\x69\x00\x63\ +\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0d\ +\x0d\x78\x3d\x47\ +\x00\x73\ +\x00\x61\x00\x76\x00\x65\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0d\ +\x07\xe7\x9d\x07\ +\x00\x68\ +\x00\x65\x00\x6c\x00\x70\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x09\ +\x08\x5f\x92\xb3\ +\x00\x74\ +\x00\x61\x00\x62\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x73\ +\x00\x16\ +\x0f\x13\x9c\x07\ +\x00\x69\ +\x00\x6d\x00\x70\x00\x6f\x00\x72\x00\x74\x00\x5f\x00\x72\x00\x65\x00\x70\x00\x6f\x00\x72\x00\x74\x00\x5f\x00\x69\x00\x63\x00\x6f\ +\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x1e\ +\x01\x33\x1c\x07\ +\x00\x66\ +\x00\x65\x00\x74\x00\x63\x00\x68\x00\x5f\x00\x73\x00\x70\x00\x65\x00\x63\x00\x69\x00\x61\x00\x6c\x00\x5f\x00\x72\x00\x65\x00\x70\ +\x00\x6f\x00\x72\x00\x74\x00\x73\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x17\ +\x0d\xf5\x6e\x67\ +\x00\x6d\ +\x00\x61\x00\x6e\x00\x61\x00\x67\x00\x65\x00\x5f\x00\x76\x00\x65\x00\x6e\x00\x64\x00\x6f\x00\x72\x00\x73\x00\x5f\x00\x69\x00\x63\ +\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x11\ +\x0b\x5b\x52\x67\ +\x00\x73\ +\x00\x65\x00\x74\x00\x74\x00\x69\x00\x6e\x00\x67\x00\x73\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\ +\x00\x0f\ +\x0f\x53\x68\x87\ +\x00\x76\ +\x00\x69\x00\x73\x00\x75\x00\x61\x00\x6c\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x16\ +\x04\xfa\x28\x27\ +\x00\x66\ +\x00\x65\x00\x74\x00\x63\x00\x68\x00\x5f\x00\x72\x00\x65\x00\x70\x00\x6f\x00\x72\x00\x74\x00\x73\x00\x5f\x00\x69\x00\x63\x00\x6f\ +\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0f\ +\x0b\x73\xfa\x47\ +\x00\x73\ +\x00\x65\x00\x61\x00\x72\x00\x63\x00\x68\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0e\ +\x07\x36\x66\x27\ +\x00\x63\ +\x00\x6f\x00\x73\x00\x74\x00\x73\x00\x5f\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +" + +qt_resource_struct_v1 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x03\ +\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x81\x92\ +\x00\x00\x00\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x2f\x3c\ +\x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x00\x56\xf3\ +\x00\x00\x01\x6c\x00\x00\x00\x00\x00\x01\x00\x01\x03\xbe\ +\x00\x00\x01\x8c\x00\x02\x00\x00\x00\x08\x00\x00\x00\x0d\ +\x00\x00\x01\x18\x00\x00\x00\x00\x00\x01\x00\x00\xd6\x45\ +\x00\x00\x00\xc6\x00\x00\x00\x00\x00\x01\x00\x00\x5d\x58\ +\x00\x00\x01\x4c\x00\x00\x00\x00\x00\x01\x00\x00\xfe\xd3\ +\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x00\x56\x00\x00\x00\x00\x00\x01\x00\x00\x2a\x1b\ +\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x01\x15\xd6\ +\x00\x00\x02\x98\x00\x00\x00\x00\x00\x01\x00\x01\x44\x39\ +\x00\x00\x02\xee\x00\x00\x00\x00\x00\x01\x00\x01\x56\x55\ +\x00\x00\x02\x4c\x00\x00\x00\x00\x00\x01\x00\x01\x2e\x81\ +\x00\x00\x02\xca\x00\x00\x00\x00\x00\x01\x00\x01\x4c\x1c\ +\x00\x00\x02\x18\x00\x00\x00\x00\x00\x01\x00\x01\x24\x9c\ +\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x01\x10\x5e\ +\x00\x00\x02\x74\x00\x00\x00\x00\x00\x01\x00\x01\x3c\x57\ +" + +qt_resource_struct_v2 = b"\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x0a\x00\x02\x00\x00\x00\x0a\x00\x00\x00\x03\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\xf8\x00\x00\x00\x00\x00\x01\x00\x00\x81\x92\ +\x00\x00\x01\x71\x96\x16\xc0\xc5\ +\x00\x00\x00\x7a\x00\x00\x00\x00\x00\x01\x00\x00\x2f\x3c\ +\x00\x00\x01\x71\x1d\xcd\x1e\x12\ +\x00\x00\x00\xa6\x00\x00\x00\x00\x00\x01\x00\x00\x56\xf3\ +\x00\x00\x01\x71\x1d\xcd\x1e\x1c\ +\x00\x00\x01\x6c\x00\x00\x00\x00\x00\x01\x00\x01\x03\xbe\ +\x00\x00\x01\x71\x65\x5c\x8a\x55\ +\x00\x00\x01\x8c\x00\x02\x00\x00\x00\x08\x00\x00\x00\x0d\ +\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x01\x18\x00\x00\x00\x00\x00\x01\x00\x00\xd6\x45\ +\x00\x00\x01\x71\x1d\xcd\x1e\x14\ +\x00\x00\x00\xc6\x00\x00\x00\x00\x00\x01\x00\x00\x5d\x58\ +\x00\x00\x01\x71\x1d\xcd\x1e\x15\ +\x00\x00\x01\x4c\x00\x00\x00\x00\x00\x01\x00\x00\xfe\xd3\ +\x00\x00\x01\x71\x1d\xcd\x1e\x15\ +\x00\x00\x00\x22\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ +\x00\x00\x01\x71\x1d\xcd\x1e\x13\ +\x00\x00\x00\x56\x00\x00\x00\x00\x00\x01\x00\x00\x2a\x1b\ +\x00\x00\x01\x71\x4e\xe3\xa7\x40\ +\x00\x00\x01\xd6\x00\x00\x00\x00\x00\x01\x00\x01\x15\xd6\ +\x00\x00\x01\x71\x1d\xcd\x1e\x18\ +\x00\x00\x02\x98\x00\x00\x00\x00\x00\x01\x00\x01\x44\x39\ +\x00\x00\x01\x71\x1d\xcd\x1e\x17\ +\x00\x00\x02\xee\x00\x00\x00\x00\x00\x01\x00\x01\x56\x55\ +\x00\x00\x01\x71\x1d\xcd\x1e\x16\ +\x00\x00\x02\x4c\x00\x00\x00\x00\x00\x01\x00\x01\x2e\x81\ +\x00\x00\x01\x71\x1d\xcd\x1e\x1b\ +\x00\x00\x02\xca\x00\x00\x00\x00\x00\x01\x00\x01\x4c\x1c\ +\x00\x00\x01\x71\x1d\xcd\x1e\x1a\ +\x00\x00\x02\x18\x00\x00\x00\x00\x00\x01\x00\x01\x24\x9c\ +\x00\x00\x01\x71\x1d\xcd\x1e\x19\ +\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x01\x10\x5e\ +\x00\x00\x01\x71\x1d\xcd\x1e\x18\ +\x00\x00\x02\x74\x00\x00\x00\x00\x00\x01\x00\x01\x3c\x57\ +\x00\x00\x01\x71\x1d\xcd\x1e\x1c\ +" + +qt_version = [int(v) for v in QtCore.qVersion().split('.')] +if qt_version < [5, 8, 0]: + rcc_version = 1 + qt_resource_struct = qt_resource_struct_v1 +else: + rcc_version = 2 + qt_resource_struct = qt_resource_struct_v2 + +def qInitResources(): + QtCore.qRegisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +def qCleanupResources(): + QtCore.qUnregisterResourceData(rcc_version, qt_resource_struct, qt_resource_name, qt_resource_data) + +qInitResources() diff --git a/Search.py b/Search.py new file mode 100644 index 0000000..a69e910 --- /dev/null +++ b/Search.py @@ -0,0 +1,301 @@ +import os +import sip +import json +from typing import Tuple, Dict +from PyQt5.QtGui import QIntValidator, QDoubleValidator +from PyQt5.QtWidgets import QFrame, QVBoxLayout, QComboBox, QLineEdit, QSpacerItem, QSizePolicy, QLabel + +import ManageDB +from Settings import SettingsModel +from ui import SearchTab, SearchAndFrame, SearchOrFrame +from Constants import * +from GeneralUtils import * + + +class SearchController: + """Controls the Search tab + + :param search_ui: the UI for the search_widget + :param settings: the user's settings""" + + def __init__(self, search_ui: SearchTab.Ui_search_tab, settings: SettingsModel): + self.main_window = search_ui + self.settings = settings + + # set up report types combobox + self.report_parameter = search_ui.search_report_parameter_combobox + self.report_parameter.addItems(ALL_REPORTS) + + # set up start year dateedit + self.start_year_parameter = search_ui.search_start_year_parameter_dateedit + self.start_year_parameter.setDate(QDate.currentDate()) + + # set up end year dateedit + self.end_year_parameter = search_ui.search_end_year_parameter_dateedit + self.end_year_parameter.setDate(QDate.currentDate()) + + # set up search button + self.search_button = search_ui.search_button + self.search_button.clicked.connect(self.search) + + self.open_results_file_checkbox = search_ui.search_open_file_checkbox + self.open_results_folder_checkbox = search_ui.search_open_folder_checkbox + + # set up export button + self.export_button = search_ui.search_export_button + self.export_button.clicked.connect(self.export_parameters) + + # set up import button + self.import_button = search_ui.search_import_button + self.import_button.clicked.connect(self.import_parameters) + + # set up add and clause button + def add_and_and_or_clause(): + """Invoked to add an and clause containing an or clause to the search""" + and_clause = self.add_and_clause() + self.add_or_clause(and_clause) + self.hide_or_label_in_first_or_clause(and_clause) + self.hide_and_label_in_first_and_clause() + + self.add_and_button = search_ui.search_add_and_button + self.add_and_button.clicked.connect(add_and_and_or_clause) + + # resets the search clauses when the report type is changed + def refresh_and_add_clauses(): + """Resets the search clauses, then adds an and clause containing an or clause""" + self.refresh_clauses() + add_and_and_or_clause() + + self.report_parameter.currentTextChanged.connect(refresh_and_add_clauses) + + self.and_clause_parameters_scrollarea = search_ui.search_and_clause_parameters_scrollarea + self.and_clause_parameters_frame = None + refresh_and_add_clauses() + + def update_settings(self, settings: SettingsModel): + """Called when the settings are saved + + :param settings: the new settings""" + self.settings = settings + + def refresh_clauses(self): + """Resets the search clauses""" + self.and_clause_parameters_frame = QFrame() + self.and_clause_parameters_frame.setLayout(QVBoxLayout()) + self.and_clause_parameters_frame.layout().addItem(QSpacerItem(0, 0, QSizePolicy.Expanding, + QSizePolicy.Expanding)) + self.and_clause_parameters_scrollarea.setWidget(self.and_clause_parameters_frame) + + def add_and_clause(self) -> SearchAndFrame.Ui_search_and_clause_parameter_frame: + """Adds an and clause to the search""" + and_clause = QFrame() + and_clause_ui = SearchAndFrame.Ui_search_and_clause_parameter_frame() + and_clause_ui.setupUi(and_clause) + + # set up add or clause button + def add_or_to_this_and(): + """Adds an or clause to this and clause""" + self.add_or_clause(and_clause_ui) + self.hide_or_label_in_first_or_clause(and_clause_ui) + + and_clause_ui.search_add_or_clause_button.clicked.connect(add_or_to_this_and) + + # set up remove current and clause button + def remove_this_and(): + """Removes this and clause""" + self.and_clause_parameters_frame.layout().removeWidget(and_clause) + sip.delete(and_clause) + self.hide_and_label_in_first_and_clause() + self.and_clause_parameters_frame.repaint() + + and_clause_ui.search_remove_and_clause_button.clicked.connect(remove_this_and) + + # add to the layout + self.and_clause_parameters_frame.layout().insertWidget(self.and_clause_parameters_frame.layout().count() - 1, + and_clause) + + return and_clause_ui + + def hide_and_label_in_first_and_clause(self): + """Hides the and label for the first and clause in the search""" + and_clause = self.and_clause_parameters_frame.findChild(QFrame, 'search_and_clause_parameter_frame') + if and_clause: + and_clause.findChild(QLabel, "search_and_label").hide() + + def add_or_clause(self, and_clause: SearchAndFrame.Ui_search_and_clause_parameter_frame) \ + -> SearchOrFrame.Ui_search_or_clause_parameter_frame: + """Adds an or clause to the search + + :param and_clause: the and clause the or clause is added to""" + or_clause = QFrame() + or_clause_ui = SearchOrFrame.Ui_search_or_clause_parameter_frame() + or_clause_ui.setupUi(or_clause) + + # fill field combobox + field_combobox = or_clause_ui.search_field_parameter_combobox + for field in ManageDB.get_view_report_fields_list(self.report_parameter.currentText()): + if field[NAME_KEY] not in FIELDS_NOT_IN_SEARCH_DROPDOWN: + field_combobox.addItem(field[NAME_KEY], field['type']) + + type_label = or_clause_ui.search_type_label + + value_lineedit = or_clause_ui.search_value_parameter_lineedit + + def on_field_changed(): + """Invoked when the field parameter is changed""" + type_label.setText(field_combobox.currentData().capitalize() + " Input") + value_lineedit.setText(None) + if field_combobox.currentData() == 'INTEGER': + value_lineedit.setValidator(QIntValidator()) + elif field_combobox.currentData() == 'REAL': + value_lineedit.setValidator(QDoubleValidator()) + else: + value_lineedit.setValidator(None) + + field_combobox.currentTextChanged.connect(on_field_changed) + on_field_changed() + + # fill comparison operator combobox + comparison_combobox = or_clause_ui.search_comparison_parameter_combobox + comparison_combobox.addItems(COMPARISON_OPERATORS) + comparison_combobox.addItems(NON_COMPARISONS) + + def on_comparison_changed(): + """Invoked when the comparison parameter is changed""" + if comparison_combobox.currentText() in NON_COMPARISONS: + value_lineedit.setText(None) + value_lineedit.setEnabled(False) + else: + value_lineedit.setEnabled(True) + + comparison_combobox.currentTextChanged.connect(on_comparison_changed) + + # set up remove current or clause button + def remove_this_or(): + """Removes this or clause""" + and_clause.search_or_clause_parameters_frame.layout().removeWidget(or_clause) + sip.delete(or_clause) + self.hide_or_label_in_first_or_clause(and_clause) + and_clause.search_or_clause_parameters_frame.repaint() + + or_clause_ui.search_remove_or_clause_button.clicked.connect(remove_this_or) + + # add to parent and clause's layout + and_clause.search_or_clause_parameters_frame.layout().addWidget(or_clause) + + return or_clause_ui + + def hide_or_label_in_first_or_clause(self, and_clause: SearchAndFrame.Ui_search_and_clause_parameter_frame): + """Hides the or label for the first or clause in an and clause + + :param and_clause: the and clause""" + or_clause = and_clause.search_or_clause_parameters_frame.findChild(QFrame, 'search_or_clause_parameter_frame') + if or_clause: + or_clause.findChild(QLabel, "search_or_label").hide() + + def export_parameters(self): + """Exports the current search parameters to the selected file""" + file_name = choose_save(JSON_FILTER) + if file_name != '': + if not file_name.lower().endswith('.dat'): + file_name += '.dat' + report, start_year, end_year, search_parameters = self.get_search_parameters() + file = open(file_name, 'w', encoding='utf-8-sig') + if file.mode == 'w': + json.dump({'report': report, 'start_year': start_year, 'end_year': end_year, + 'search_parameters': search_parameters}, file) + show_message('Search saved to ' + file_name) + else: + print('Error, no file location selected') + + def import_parameters(self): + """Imports a new set of search parameters from the selected file""" + file_name = choose_file(JSON_FILTER) + if file_name != '': + fields = json.loads(read_json_file(file_name)) + self.report_parameter.setCurrentText(fields['report']) + self.start_year_parameter.setDate(QDate(fields['start_year'], 1, 1)) + self.end_year_parameter.setDate(QDate(fields['end_year'], 1, 1)) + clauses = fields['search_parameters'] + self.refresh_clauses() + for clause in clauses: + and_clause = self.add_and_clause() + for sub_clause in clause: + or_clause = self.add_or_clause(and_clause) + or_clause.search_field_parameter_combobox.setCurrentText(sub_clause[FIELD_KEY]) + or_clause.search_comparison_parameter_combobox.setCurrentText(sub_clause[COMPARISON_KEY]) + or_clause.search_value_parameter_lineedit.setText(str(sub_clause[VALUE_KEY])) + + def search(self): + """Queries the database based on the current search parameters and saves the results to the selected file""" + report, start_year, end_year, search_parameters = self.get_search_parameters() + + # sql query to get search results + sql_text, data = ManageDB.search_sql_text(report, start_year, end_year, search_parameters) + + headers = [] + for field in ManageDB.get_view_report_fields_list(report): + headers.append(field[NAME_KEY]) + + file_name = choose_save(TSV_FILTER) + if file_name != '': + if not file_name.lower().endswith('.tsv'): + file_name += '.tsv' + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + results = ManageDB.run_select_sql(connection, sql_text, data) + connection.close() + results.insert(0, headers) + if self.settings.show_debug_messages: print(results) + save_data_as_tsv(file_name, results) + if self.open_results_folder_checkbox.isChecked(): + open_file_or_dir(os.path.dirname(file_name)) + if self.open_results_file_checkbox.isChecked(): + open_file_or_dir(file_name) + if not self.open_results_file_checkbox.isChecked(): + show_message('Results saved to ' + file_name) + else: + print('Error, no connection') + else: + print('Error, no file location selected') + + def get_search_parameters(self) -> Tuple[str, int, int, Sequence[Sequence[Dict[str, Any]]]]: + """Reads the current search parameters from the UI + + :returns: (report, start_year, end_year, search_parameters) a Tuple with the kind of report selected, the + starting year selected, the ending year selected, and a list of the search parameters in POS form (and of + ors)""" + # get report type + report = self.report_parameter.currentText() + # get start year + start_year = int(self.start_year_parameter.text()) + # get end year + end_year = int(self.end_year_parameter.text()) + + search_parameters = [] + for and_widget in self.and_clause_parameters_frame.findChildren(QFrame, 'search_and_clause_parameter_frame'): + # iterate over and clauses + or_clause_parameters = and_widget.findChild(QFrame, 'search_or_clause_parameters_frame') + or_clauses = [] + for or_widget in or_clause_parameters.findChildren(QFrame, 'search_or_clause_parameter_frame'): + # iterate over child or clauses + # get parameters for clause + field_parameter_combobox = or_widget.findChild(QComboBox, 'search_field_parameter_combobox') + field_parameter = field_parameter_combobox.currentText() + comparison_parameter_combobox = or_widget.findChild(QComboBox, 'search_comparison_parameter_combobox') + comparison_parameter = comparison_parameter_combobox.currentText() + value_parameter_lineedit = or_widget.findChild(QLineEdit, 'search_value_parameter_lineedit') + value_parameter = None + if comparison_parameter in NON_COMPARISONS: + pass + elif field_parameter_combobox.currentData() == 'INTEGER': + value_parameter = int(value_parameter_lineedit.text()) + elif field_parameter_combobox.currentData() == 'REAL': + value_parameter = float(value_parameter_lineedit.text()) + else: + value_parameter = value_parameter_lineedit.text() + or_clauses.append( + {FIELD_KEY: field_parameter, COMPARISON_KEY: comparison_parameter, VALUE_KEY: value_parameter}) + search_parameters.append(or_clauses) + + return report, start_year, end_year, search_parameters diff --git a/Settings.py b/Settings.py new file mode 100644 index 0000000..b733112 --- /dev/null +++ b/Settings.py @@ -0,0 +1,213 @@ +"""This module handles all operations involving the user's settings.""" + +import json +from os import path +from PyQt5.QtWidgets import QWidget +from PyQt5.QtCore import QObject, pyqtSignal +from ui import SettingsTab +import ManageDB +from Constants import * +import GeneralUtils +from GeneralUtils import JsonModel + + +class Setting(Enum): + """An enum of all settings""" + YEARLY_DIR = 0 + OTHER_DIR = 1 + REQUEST_INTERVAL = 2 + REQUEST_TIMEOUT = 3 + CONCURRENT_VENDORS = 4 + CONCURRENT_REPORTS = 5 + USER_AGENT = 6 + + +class SettingsModel(JsonModel): + """This holds the user's settings. + + :param yearly_directory: The directory where yearly reports are saved. Yearly reports are reports that include all + the available data for a year. + :param other_directory: The default directory where non-yearly reports are saved. + :param request_interval: The time to wait between each report request, per vendor. + :param request_timeout: The time to wait before timing out a connection (seconds). + :param concurrent_vendors: The max number of vendors to work on at a time. + :param concurrent_reports: The max number of reports to work on at a time, per vendor. + :param user_agent: The user-agent that's included in the header when making requests. + """ + def __init__(self, show_debug_messages: bool, yearly_directory: str, other_directory: str, request_interval: int, + request_timeout: int, concurrent_vendors: int, concurrent_reports: int, user_agent: str, + default_currency: str): + self.show_debug_messages = show_debug_messages + self.yearly_directory = path.abspath(yearly_directory) + path.sep + self.other_directory = path.abspath(other_directory) + path.sep + self.request_interval = request_interval + self.request_timeout = request_timeout + self.concurrent_vendors = concurrent_vendors + self.concurrent_reports = concurrent_reports + self.user_agent = user_agent + self.default_currency = default_currency + + @classmethod + def from_json(cls, json_dict: dict): + show_debug_messages = json_dict["show_debug_messages"]\ + if "show_debug_messages" in json_dict else SHOW_DEBUG_MESSAGES + yearly_directory = json_dict["yearly_directory"]\ + if "yearly_directory" in json_dict else YEARLY_DIR + other_directory = json_dict["other_directory"]\ + if "other_directory" in json_dict else OTHER_DIR + request_interval = int(json_dict["request_interval"])\ + if "request_interval" in json_dict else REQUEST_INTERVAL + request_timeout = int(json_dict["request_timeout"])\ + if "request_timeout" in json_dict else REQUEST_TIMEOUT + concurrent_vendors = int(json_dict["concurrent_vendors"])\ + if "concurrent_vendors" in json_dict else CONCURRENT_VENDORS + concurrent_reports = int(json_dict["concurrent_reports"])\ + if "concurrent_reports" in json_dict else CONCURRENT_REPORTS + user_agent = json_dict["user_agent"]\ + if "user_agent" in json_dict else USER_AGENT + default_currency = json_dict["default_currency"]\ + if "default_currency" in json_dict else DEFAULT_CURRENCY + + return cls(show_debug_messages, yearly_directory, other_directory, request_interval, request_timeout, + concurrent_vendors, concurrent_reports, user_agent, default_currency) + + +class SettingsController(QObject): + """Controls the Settings tab + + :param settings_widget: The settings widget. + :param settings_ui: The UI for settings_widget. + """ + settings_changed_signal = pyqtSignal(SettingsModel) + + def __init__(self, settings_widget: QWidget, settings_ui: SettingsTab.Ui_settings_tab): + # region General + super().__init__() + self.settings_widget = settings_widget + + json_string = GeneralUtils.read_json_file(SETTINGS_FILE_DIR + SETTINGS_FILE_NAME) + json_dict = json.loads(json_string) + self.settings = SettingsModel.from_json(json_dict) + + self.show_debug_checkbox = settings_ui.show_debug_check_box + self.show_debug_checkbox.setChecked(self.settings.show_debug_messages) + # endregion + + # region Reports + self.yearly_dir_edit = settings_ui.yearly_directory_edit + self.other_dir_edit = settings_ui.other_directory_edit + self.request_interval_spin_box = settings_ui.request_interval_spin_box + self.request_timeout_spin_box = settings_ui.request_timeout_spin_box + self.concurrent_vendors_spin_box = settings_ui.concurrent_vendors_spin_box + self.concurrent_reports_spin_box = settings_ui.concurrent_reports_spin_box + self.user_agent_edit = settings_ui.user_agent_edit + + self.yearly_dir_edit.setText(self.settings.yearly_directory) + self.other_dir_edit.setText(self.settings.other_directory) + self.request_interval_spin_box.setValue(self.settings.request_interval) + self.request_timeout_spin_box.setValue(self.settings.request_timeout) + self.concurrent_vendors_spin_box.setValue(self.settings.concurrent_vendors) + self.concurrent_reports_spin_box.setValue(self.settings.concurrent_reports) + self.user_agent_edit.setText(self.settings.user_agent) + + settings_ui.yearly_directory_button.clicked.connect( + lambda: self.on_directory_setting_clicked(Setting.YEARLY_DIR)) + settings_ui.other_directory_button.clicked.connect( + lambda: self.on_directory_setting_clicked(Setting.OTHER_DIR)) + + # Reports Help Messages + settings_ui.yearly_directory_help_button.clicked.connect( + lambda: GeneralUtils.show_message("This is where the calendar-year reports will be saved")) + settings_ui.other_directory_help_button.clicked.connect( + lambda: GeneralUtils.show_message("This is where the special and non-calendar-year date range reports will " + "be saved by default")) + settings_ui.request_interval_help_button.clicked.connect( + lambda: GeneralUtils.show_message("The number of seconds the program will wait between sending each report " + "request to a given vendor")) + settings_ui.request_timeout_help_button.clicked.connect( + lambda: GeneralUtils.show_message("The number of seconds the program will allow a vendor to respond to " + "each report request before canceling it")) + settings_ui.concurrent_vendors_help_button.clicked.connect( + lambda: GeneralUtils.show_message("The maximum number of vendors to work on at the same time. " + "If set too high, the UI might freeze while fetching reports but the " + "fetch process will continue")) + settings_ui.concurrent_reports_help_button.clicked.connect( + lambda: GeneralUtils.show_message("The maximum number of reports to work on at the same time (per vendor). " + "If set too high, the UI might freeze while fetching reports but the " + "fetch process will continue")) + settings_ui.user_agent_help_button.clicked.connect( + lambda: GeneralUtils.show_message("How program identifies itself to the SUSHI servers. Some vendors will " + "reject some particular user agents. Only change this if there is a " + "known problem as it will affect all requests to all vendors. " + "See Help for more information.")) + settings_ui.default_currency_help_button.clicked.connect( + lambda: GeneralUtils.show_message("The currency shown first in the Costs pulldown and also by Visual to " + "label the local currency in the spreadsheets generated with the Cost " + "Ratio option. Note: This doesn't have to be one of the pre-loaded " + "currencies.")) + + # endregion + + # region Costs + self.default_currency_combobox = settings_ui.settings_costs_default_currency_combobox + self.default_currency_combobox.addItems(CURRENCY_LIST) + self.default_currency_combobox.setCurrentText(self.settings.default_currency) + # endregion + + # region Search + # set up restore database button + self.is_rebuilding_database = False + self.update_database_dialog = ManageDB.UpdateDatabaseProgressDialogController(self.settings_widget) + self.rebuild_database_button = settings_ui.settings_rebuild_database_button + self.rebuild_database_button.clicked.connect(self.on_rebuild_database_clicked) + # endregion + + settings_ui.save_button.clicked.connect(self.on_save_button_clicked) + + def on_directory_setting_clicked(self, setting: Setting): + """Handles the signal emitted when a choose folder button is clicked + + :param setting: The setting to be changed + """ + dir_path = GeneralUtils.choose_directory() + if dir_path: + if setting == Setting.YEARLY_DIR: + self.yearly_dir_edit.setText(dir_path) + elif setting == Setting.OTHER_DIR: + self.other_dir_edit.setText(dir_path) + + def on_save_button_clicked(self): + """Handles the signal emitted when the save button is clicked""" + self.update_settings() + self.save_settings_to_disk() + self.settings_changed_signal.emit(self.settings) + GeneralUtils.show_message("Changes saved!") + + def on_rebuild_database_clicked(self): + """Restores the database when the restore database button is clicked""" + if not self.is_rebuilding_database: # check if already running + if GeneralUtils.ask_confirmation('Are you sure you want to rebuild the database?'): + self.is_rebuilding_database = True + self.update_database_dialog.update_database(ManageDB.get_all_report_files() + + ManageDB.get_all_cost_files(), + True) + self.is_rebuilding_database = False + else: + if self.settings.show_debug_messages: print('Database is already being rebuilt') + + def update_settings(self): + """Updates the app's settings using the values entered on the UI""" + self.settings.show_debug_messages = self.show_debug_checkbox.isChecked() + self.settings.yearly_directory = self.yearly_dir_edit.text() + self.settings.other_directory = self.other_dir_edit.text() + self.settings.request_interval = self.request_interval_spin_box.value() + self.settings.request_timeout = self.request_timeout_spin_box.value() + self.settings.concurrent_vendors = self.concurrent_vendors_spin_box.value() + self.settings.concurrent_reports = self.concurrent_reports_spin_box.value() + self.settings.user_agent = self.user_agent_edit.text() + self.settings.default_currency = self.default_currency_combobox.currentText() + + def save_settings_to_disk(self): + """Saves all settings to disk""" + json_string = json.dumps(self.settings, default=lambda o: o.__dict__) + GeneralUtils.save_json_file(SETTINGS_FILE_DIR, SETTINGS_FILE_NAME, json_string) \ No newline at end of file diff --git a/Visual.py b/Visual.py new file mode 100644 index 0000000..23880ad --- /dev/null +++ b/Visual.py @@ -0,0 +1,757 @@ +"""This module handles all operations involving the visual tab.""" + +import calendar +import datetime +import json +import os +from _operator import itemgetter +from typing import Sequence + +from PyQt5.QtGui import QStandardItemModel, QStandardItem, QFont + +import xlsxwriter +from datetime import date + +from PyQt5.QtCore import Qt +from PyQt5.QtWidgets import QFileDialog, QDialog + +import GeneralUtils +from GeneralUtils import * +import ManageDB +import ManageVendors +from Settings import SettingsModel +from ui import MessageDialog, VisualTab +from Constants import * + + +class VisualController: + """Controls the Visual tab + + :param visual_ui: The UI for visual_widget. + """ + + def __init__(self, visual_ui: VisualTab.Ui_visual_tab, settings: SettingsModel): + self.settings = settings + + # set up and configure report type combobox + self.report_parameter = visual_ui.search_report_parameter_combobox_2 + self.report_parameter.addItems(ALL_REPORTS) + self.report_parameter.currentTextChanged[str].connect(self.on_report_parameter_changed) + + # set up and configure cost ratio combobox + self.cost_parameter = visual_ui.cost_ratio_option_combobox + COST_TYPE_ALL = ('Cost in Local Currency with Tax', 'Cost in Local Currency', 'Cost in Original Currency') + self.cost_parameter.addItems(COST_TYPE_ALL) + + # set up and configure cost ratio frame + self.frame_cost = visual_ui.edit_cost_ratio_frame + self.frame_cost.setEnabled(False) + + # set up and configure top num frame + self.top_num_frame = visual_ui.edit_top_num_frame + self.top_num_frame.setEnabled(False) + + # set up top num spinbox and configure lower and upper bounds + self.top_num_edit = visual_ui.top_num_spinbox + + # set up chart type radio buttons + self.h_bar_radio = visual_ui.radioButton + self.v_bar_radio = visual_ui.radioButton_3 + self.line_radio = visual_ui.radioButton_4 + self.v_bar_radio.setChecked(True) + + # set up calculations type radio buttons + self.monthly_radio = visual_ui.monthly_radioButton + self.yearly_radio = visual_ui.yearly_radioButton + self.topNum_radio = visual_ui.topnum_radioButton + self.costRatio_radio = visual_ui.costratio_radioButton + + # configure calculation type radio buttons and connect with method + self.monthly_radio.setChecked(True) + self.monthly_radio.toggled.connect(self.on_calculation_type_changed) + self.yearly_radio.toggled.connect(self.on_calculation_type_changed) + self.topNum_radio.toggled.connect(self.on_calculation_type_changed) + self.costRatio_radio.toggled.connect(self.on_calculation_type_changed) + + # set up start year dateedit + self.start_year_parameter = visual_ui.search_start_year_parameter_dateedit_2 + self.start_year_parameter.setDate(date.today()) + + # set up end year dateedit + self.end_year_parameter = visual_ui.search_end_year_parameter_dateedit_2 + self.end_year_parameter.setDate(date.today()) + + # set up name label + self.name_label = visual_ui.visual_name_label + + # set up name combobox + self.name_combobox = visual_ui.visual_name_parameter_combobox + self.name = None + self.metric = visual_ui.metric_Type_comboBox + self.metric.addItems(DATABASE_REPORTS_METRIC) + + self.vendor = visual_ui.visual_vendor_parameter_combobox + self.vendor.currentTextChanged.connect(self.on_vendor_changed) + self.vendor_parameter = None + self.vendor_parameter = self.vendor.currentText() + vendors_json_string = GeneralUtils.read_json_file(ManageVendors.VENDORS_FILE_PATH) + vendor_dicts = json.loads(vendors_json_string) + self.vendor.clear() + self.vendor.addItem("") + self.vendor.addItems([vendor_dict['name'] for vendor_dict in vendor_dicts]) + + # set up the search clauses + self.and_clause_parameters = None + + # set up create chart button + self.create_chart_button = visual_ui.create_chart_button + self.create_chart_button.clicked.connect(self.createChart) + + # set up customize chart field + self.chart_title_edit = visual_ui.chart_title_lineEdit + self.horizontal_axis_edit = visual_ui.horizontal_axis_lineEdit + self.vertical_axis_edit = visual_ui.vertical_axis_lineEdit + + # set up open file and open folder check box + self.open_file = visual_ui.open_file_checkBox + self.open_folder = visual_ui.open_folder_checkBox + self.file_name = None + + self.data = [] + self.temp_results = [] + self.top_num = None + self.results = None + self.names = [] + self.costs_names = [] + + def update_settings(self, settings: SettingsModel): + """Called when the settings are saved + + :param settings: the new settings""" + self.settings = settings + self.local_currency = self.settings.default_currency + #self.load_currency_list() + + def database_updated(self, code: int): + """Called when the database is updated + + :param code: the exit code of the update""" + self.fill_names() + + def load_vendor_list(self, vendors: Sequence[ManageVendors.Vendor]): + """Updates the vendor list combobox + + :param vendors: the new list of vendors""" + self.vendor.clear() + self.vendor.addItem("") + self.vendor.addItems([vendor.name for vendor in vendors]) + + def on_calculation_type_changed(self): + """Invoke when calculation type is changed""" + if self.topNum_radio.isChecked(): + self.top_num_frame.setEnabled(True) + self.frame_cost.setEnabled(False) + self.name_combobox.setEnabled(False) + self.name_label.setEnabled(False) + if self.costRatio_radio.isChecked(): + self.top_num_frame.setEnabled(False) + self.frame_cost.setEnabled(True) + self.name_combobox.setEnabled(True) + self.name_label.setEnabled(True) + if self.monthly_radio.isChecked() or self.yearly_radio.isChecked(): + self.top_num_frame.setEnabled(False) + self.frame_cost.setEnabled(False) + self.name_combobox.setEnabled(True) + self.name_label.setEnabled(True) + + def on_report_parameter_changed(self, text): + """Invoke when report type is changed""" + self.metric.clear() + if text in DATABASE_REPORTS: + self.metric.addItems(DATABASE_REPORTS_METRIC) + self.name_label.setText('Database') + if text in ITEM_REPORTS: + self.metric.addItems(ITEM_REPORTS_METRIC) + self.name_label.setText('Item') + if text in PLATFORM_REPORTS: + self.metric.addItems(PLATFORM_REPORTS_METRIC) + self.name_label.setText('Platform') + if text in TITLE_REPORTS: + self.metric.addItems(TITLE_REPORTS_METRIC) + self.name_label.setText('Title') + if self.vendor.currentText(): + self.fill_names() + + def on_vendor_changed(self): + """Invoke when vendor is changed""" + self.vendor_parameter = self.vendor.currentText() + if self.report_parameter.currentText(): + self.fill_names() + + def fill_names(self, only_get_costs_names: bool = False): + """Fill name field combobox""" + self.name_combobox.clear() + + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + if not only_get_costs_names: + names_sql_text, names_data = ManageDB.get_names_sql_text(self.report_parameter.currentText(), self.vendor_parameter) + names_results = ManageDB.run_select_sql(connection, names_sql_text, names_data) + if names_results: + self.names = [result[0] for result in names_results] + else: + self.names = [] + if self.settings.show_debug_messages: print(names_results) + + costs_sql_text, costs_data = ManageDB.get_names_with_costs_sql_text(self.report_parameter.currentText(), + self.vendor_parameter, + int(self.start_year_parameter.text()), + int(self.end_year_parameter.text())) + costs_results = ManageDB.run_select_sql(connection, costs_sql_text, costs_data) + if costs_results: + self.costs_names = [result[0] for result in costs_results] + else: + self.costs_names = [] + if self.settings.show_debug_messages: print(costs_results) + + connection.close() + model = QStandardItemModel() + for name in self.names: + item = QStandardItem(name) + if name in self.costs_names: + font = QFont() + font.setBold(True) + item.setFont(font) + model.appendRow(item) + self.name_combobox.setModel(model) + else: + print('Error, no connection') + + # submit search result to database and open results + def createChart(self): + """Invoke when user click on create chart""" + + # get report type + report = self.report_parameter.currentText() + # get start year + start_year = self.start_year_parameter.text() + # get end year + end_year = self.end_year_parameter.text() + # get name + name = self.name_combobox.currentText() + # get metric + metric = self.metric.currentText() + # get vendor + vendor = self.vendor.currentText() + self.top_num = -1 + + self.temp_results = [] + message = "" + message1 = "" + message2 = "" + message3 = "" + message4 = "" + if name == "" and self.topNum_radio.isChecked() == False: + message4 = "- Enter/Choose " + self.name_label.text() + "\n" + if vendor == "" and self.topNum_radio.isChecked() == False: + message1 = "- Choose a Vendor \n" + if start_year > end_year or (int(start_year) > datetime.datetime.now().year or int(end_year) > datetime.datetime.now().year) : + currentYear = datetime.datetime.now().year + message3 = "- Start Year must be less than End Year and they cannot be greater than " + str( + currentYear) + "\n" + message = message1 + message4 + message3 + if message != "": + message = "To Create Chart check the following: \n" + message + GeneralUtils.show_message(message) + + if self.monthly_radio.isChecked() and message == "": + # sql query to get search results + sql_text, data = ManageDB.monthly_chart_search_sql_text(report, start_year, end_year, name, metric, vendor) + print(sql_text) # testing + headers = tuple([field['name'] for field in ManageDB.get_monthly_chart_report_fields_list(report)]) + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + self.results = ManageDB.run_select_sql(connection, sql_text, data) + print(self.results) + + self.results.insert(0, headers) + print(self.results) + connection.close() + else: + print('Error, no connection') + if len(self.results) > 1 and self.monthly_radio.isChecked(): + self.process_default_data() + self.open_file_folder() + if name != "" and start_year <= end_year and len(self.results) <= 1: + message4 = name + " of " + metric + " NOT FOUND in " + report + " for the chosen year range!" + GeneralUtils.show_message(message4) + + if self.yearly_radio.isChecked() and message == "": + # sql query to get search results + sql_text, data = ManageDB.yearly_chart_search_sql_text(report, start_year, end_year, name, metric, vendor) + print(sql_text) # testing + headers = tuple([field['name'] for field in ManageDB.get_yearly_chart_report_fields_list(report)]) + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + self.results = ManageDB.run_select_sql(connection, sql_text, data) + print(self.results) + + self.results.insert(0, headers) + print(self.results) + connection.close() + else: + print('Error, no connection') + if len(self.results) > 1 and self.yearly_radio.isChecked(): + self.process_yearly_data() + self.open_file_folder() + if name != "" and start_year <= end_year and len(self.results) <= 1: + message4 = name + " of " + metric + " NOT FOUND in " + report + " for the chosen year range!" + GeneralUtils.show_message(message4) + + if self.costRatio_radio.isChecked() and message == "": + # sql query to get search results + sql_text, data = ManageDB.cost_chart_search_sql_text(report, start_year, end_year, name, metric, vendor) + print(sql_text) # testing + headers = tuple([field['name'] for field in ManageDB.get_cost_chart_report_fields_list(report)]) + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + self.results = ManageDB.run_select_sql(connection, sql_text, data) + print(self.results) + + self.results.insert(0, headers) + print(self.results) + connection.close() + else: + print('Error, no connection') + + if len(self.results) > 1 and self.costRatio_radio.isChecked(): + self.process_cost_ratio_data() + self.open_file_folder() + if name != "" and start_year <= end_year and len(self.results) <= 1: + message4 = name + " of " + metric + " NOT FOUND in " + report + " for the chosen year range!" + GeneralUtils.show_message(message4) + + if self.topNum_radio.isChecked() and message == "": + self.top_num = int(self.top_num_edit.text()) + if self.top_num == 0: + self.top_num = None + if self.vendor.currentText() == "": + vendor = None + sql_text, data = ManageDB.top_number_chart_search_sql_text(report, start_year, end_year, metric, vendor, + self.top_num) + headers = tuple([field['name'] for field in ManageDB.get_top_number_chart_report_fields_list(report)]) + connection = ManageDB.create_connection(DATABASE_LOCATION) + if connection is not None: + self.results = ManageDB.run_select_sql(connection, sql_text, data) + + self.results.insert(0, headers) + connection.close() + else: + print('Error, no connection') + if len(self.results) > 1: + self.process_top_X_data() + self.open_file_folder() + elif start_year <= end_year: + message5 = self.name_label.text() + " of " + metric + " Not Found in " + report + " for the chosen year range!" + GeneralUtils.show_message(message5) + + def open_file_folder(self): + """Invoke to open file or folder""" + if self.open_folder.isChecked(): + open_file_or_dir(os.path.dirname(self.file_name)) + if self.open_file.isChecked(): + open_file_or_dir(self.file_name) + if not self.open_file.isChecked(): + show_message('Results saved to ' + self.file_name) + + # process_data distributes the usage data for monthly in an array accordingly + def process_default_data(self): + """Invoked when calculation type: monthly is selected""" + m = len(self.results) + self.legendEntry = [] # legend entry data + for i in range(1, m): + self.legendEntry.append(self.results[i][3]) + + # data is an array with the sorted usage figures + self.data = [] + for i in range(0, m): + data1 = [] + n = len(self.results[i]) + for j in range(4, n): # from jan to dec only + data1.append(self.results[i][j]) + self.data.append(data1) + # testing to make sure its working good + print(self.data[0]) # this is the first column in the excel file/vertical axis data in the chart + print(self.data[1]) + print(len(self.data)) + self.chart_type() + + def process_yearly_data(self): + """Invoked when calculation type: yearly is selected""" + m = len(self.results) + self.legendEntry = [] # legend entry data + self.legendEntry.append(self.metric.currentText()) + + # data is an array with the sorted usage figures + self.data = [] + data1 = [] # year + data2 = [] # reporting_period_total + for i in range(1, m): + data1.append(self.results[i][3]) + self.data.append(data1) + for i in range(1, m): + data2.append(self.results[i][4]) + self.data.append(data2) + + # testing to make sure its working good + print(self.data[0]) # this is the first column in the excel file/vertical axis data in the chart + print(self.data[1]) + print(len(self.data)) + self.chart_type() + + def process_cost_ratio_data(self): + """Invoked when calculation type: cost ratio is selected""" + m = len(self.results) #length of self.results + self.legendEntry = [] # legend entry data contains column names + + # data is an array of array with year, cost per metric, total and cost in separate arrays + self.data = [] + + data1 = [] # year + data2 = [] # cost per metric + data3 = [] # reporting_period_total + data4 = [] # cost + + # retrieve year and add it to array + for i in range(1, m): + data1.append(self.results[i][3]) + self.data.append(data1) + + # retrieve cost and total and finding cost per metric and adding it to array + if self.cost_parameter.currentText() == 'Cost in Local Currency with Tax': + self.legendEntry.append('Cost in Local Currency with Tax Per Metric') + self.legendEntry.append('Cost in Local Currency with Tax') + for i in range(1, m): + cost = self.results[i][7] + if self.results[i][7] is None: + cost = 0 + data4.append(cost) + data2.append(cost / self.results[i][8]) + self.data.append(data2) + self.data.append(data4) + if self.cost_parameter.currentText() == 'Cost in Local Currency': + self.legendEntry.append('Cost in Local Currency Per Metric') + self.legendEntry.append('Cost in Local Currency') + for i in range(1, m): + cost = self.results[i][6] + if self.results[i][6] is None: + cost = 0 + data4.append(cost) + data2.append(cost / self.results[i][8]) + self.data.append(data2) + self.data.append(data4) + if self.cost_parameter.currentText() == 'Cost in Original Currency': + self.legendEntry.append('Cost in Original Currency Per Metric') + self.legendEntry.append('Cost in Original Currency') + for i in range(1, m): + cost = self.results[i][4] + if self.results[i][4] is None: + cost = 0 + data4.append(cost) + data2.append(cost / self.results[i][8]) + self.data.append(data2) + self.data.append(data4) + + # retrieve reporting_period_total and add it to array + for i in range(1, m): + data3.append(self.results[i][8]) + self.data.append(data3) + + # add column header to legend entry + self.legendEntry.append(self.metric.currentText()) + + # testing to see data in array of array + print(self.data[0]) # first column in excel (year) + print(self.data[1]) # second column (cost per metric) + # print(self.data[2]) # third column (cost) + # print(self.data[4]) # fourth column (total) + # print(len(self.data)) #testing + self.chart_type() + + def process_top_X_data(self): + """Invoked when calculation type: top # is selected""" + m = len(self.results) + #print(self.results) + print(m) + self.temp_results = [] + + self.legendEntry = [] # legend entry data + self.legendEntry.append(self.results[0][3]) + self.legendEntry.append(self.results[0][4]) + for i in range(1, m): + self.temp_results.append(self.results[i]) + self.temp_results = sorted(self.temp_results, key=itemgetter(4)) + print(len(self.temp_results)) + print(self.temp_results) + n = len(self.temp_results) + + # data is an array with the sorted usage figures + self.data = [] + data1 = [] # name(database, title,...) + data2 = [] # reporting total + data3 = [] # rankings + data4 = [] # optional vendor column + + # get all name(database,title,...) + data = self.temp_results[0][0] + data1.append(data) + for i in range(1, n): # get database + data = self.temp_results[i][0] + data1.append(data) + self.data.append(data1) + print(data1) + + # get all reporting total + metri = self.temp_results[0][3] + data2.append(metri) + for i in range(1, n): # get reporting total + metri = self.temp_results[i][3] + data2.append(metri) + self.data.append(data2) + print(data2) + + # get all ranking + rank = self.temp_results[0][4] + data3.append(rank) + for i in range(1, n): + rank = self.temp_results[i][4] + data3.append(rank) + self.data.append(data3) + + # will add vendor column to chart if the user do not enter anything in vendor + if self.vendor.currentText() == "": + self.legendEntry.append(self.results[0][2]) + for i in range(0, n): + rank = self.temp_results[i][2] + data4.append(rank) + self.data.append(data4) + + self.chart_type() + + # get chart type checked + def chart_type(self): + """Invoked to determine which chart type is selected by user""" + if self.h_bar_radio.isChecked(): + self.horizontal_bar_chart() + + if self.v_bar_radio.isChecked(): + self.vertical_bar_chart() + + if self.line_radio.isChecked(): + self.line_chart() + + # get file name and titles from user + def customizeChart(self): + """Invoked to get information from user to customize chart. + + It is highly recommended that user write the details like year, report type, calculation type,.. in chart title""" + chart_title = self.chart_title_edit.text() + horizontal_axis_title = self.horizontal_axis_edit.text() + vertical_axis_title = self.vertical_axis_edit.text() + return chart_title, horizontal_axis_title, vertical_axis_title + + # add titles to chart and styles + @staticmethod + def add_Customize(chart1, chart_title, horizontal_axis_title, vertical_axis_title): + """Invoked to add information to customize the chart + + :param chart1: the chart to add details to + :param chart_title: the chart title + :param horizontal_axis_title: the horizontal axis title + :param vertical_axis_title: the vertical axis title""" + + # Add a chart title and some axis labels. + chart1.set_title({'name': chart_title}) + chart1.set_x_axis({'name': horizontal_axis_title}) + chart1.set_y_axis({'name': vertical_axis_title}) + # Set an Excel chart style. + chart1.set_style(11) + + # create file with ext and add sheet to file + def createFile(self): + """Invoked to create xlsx file""" + + self.file_name = choose_save(EXCEL_FILTER) + if self.file_name != '': + if not self.file_name.lower().endswith('.xlsx'): + self.file_name += '.xlsx' + + workbook = xlsxwriter.Workbook(self.file_name) + # add sheet to xlsx file + worksheet = workbook.add_worksheet() + return workbook, worksheet + + # Add the worksheet data that the charts will refer to. + def populateData(self, vertical_axis_title, worksheet, workbook): + """Invoked to create xlsx file + + :param vertical_axis_title: the vertical axis title + :param worksheet: the worksheet in the xlsx file + :param workbook: the workbook""" + + bold = workbook.add_format({'bold': 1}) + #headings = [vertical_axis_title] + headings = [""] + for i in range(0, len(self.legendEntry)): + headings.append(self.legendEntry[i]) + worksheet.write_row('A1', headings, bold) + worksheet.write_column('A2', self.data[0]) + n = ord('A') + 1 + if self.costRatio_radio.isChecked() == False: + for i in range(1, len(self.data)): + worksheet.write_column(chr(n) + '2', self.data[i]) + n = n + 1 + if self.costRatio_radio.isChecked() == True: + # Add a number format for cells with money. + currency = self.process_currency() + money = workbook.add_format({'num_format': currency}) + for i in range(1, len(self.data)-1): + worksheet.write_column(chr(n) + '2', self.data[i], money) + n = n + 1 + worksheet.write_column(chr(n) + '2', self.data[len(self.data)-1]) + + # process currency + def process_currency(self): + """Invoke to determine between local or original currency for cost""" + if self.cost_parameter.currentText() == 'Cost in Local Currency with Tax' or self.cost_parameter.currentText() == 'Cost in Local Currency': + local_currency = self.settings.default_currency + currency = self.get_currency_code(local_currency) + if self.cost_parameter.currentText() == 'Cost in Original Currency': + original_currency = self.results[1][5] + currency = self.get_currency_code(original_currency) + return currency + + # return currency code for excel + def get_currency_code(self, local_currency): + """Invoke to find currency being used""" + if local_currency == 'CAD': + currency = '[$CAD] #,###.#########################_)' + if local_currency == 'USD': + currency = '[$USD] #,###.#########################_)' + if local_currency == 'EUR': + currency = '[$EUR] #,###.#########################_)' + if local_currency == 'JPY': + currency = '[$JPY] #,###.#########################_)' + if local_currency == 'GBP': + currency = '[$GBP] #,###.#########################_)' + if local_currency == 'CHF': + currency = '[$CHF] #,###.#########################_)' + if local_currency == 'AUD': + currency = '[$AUD] #,###.#########################_)' + return currency + # create chart and add series to it + def configureSeries(self, workbook, chart_type): + """Invoked to create xlsx file + + :param workbook: the workbook + :param chart_type: the chart type""" + + chart1 = workbook.add_chart({'type': chart_type}) + n = len(self.data[0]) + # Configure the first series. + chart1.add_series({ + 'name': '=Sheet1!$B$1', + 'categories': '=Sheet1!$A$2:$A$'+str(n+1), + 'values': '=Sheet1!$B$2:$B$'+str(n+1), + }) + + # Configure any subsequent series. Note use of alternative syntax to define ranges. + # no more series will be added if top # and cost ratio are not selected + if self.top_num == -1 and self.costRatio_radio.isChecked() == False: + m = 2 + for i in range(2, len(self.data)): + chart1.add_series({ + 'name': ['Sheet1', 0, m], + 'categories': ['Sheet1', 1, 0, n, 0], + 'values': ['Sheet1', 1, m, n, m], + }) + m = m + 1 + return chart1 + + def horizontal_bar_chart(self): + """Invoked to create a horizontal bar chart""" + + # get titles from customizeChart + chart_title, horizontal_axis_title, vertical_axis_title = self.customizeChart() + + # create xlsx file and add sheet file + workbook, worksheet = self.createFile() + + # add data to worksheet + self.populateData(vertical_axis_title, worksheet, workbook) + + # create horizontal bar chart and add series to it + chart1 = self.configureSeries(workbook, 'bar') + + # Add a chart title and some axis labels. + self.add_Customize(chart1, chart_title, horizontal_axis_title, vertical_axis_title) + + # Insert the chart into the worksheet (with an offset). + worksheet.insert_chart('F2', chart1, {'x_scale': 2, 'y_scale': 2}) + workbook.close() + + # Completion message + message_completion = "Done!" + GeneralUtils.show_message(message_completion) + + def vertical_bar_chart(self): + """Invoked to create a vertical bar chart""" + + # get titles from customizeChart + chart_title, horizontal_axis_title, vertical_axis_title = self.customizeChart() + + # create xlsx file and add sheet file + workbook, worksheet = self.createFile() + + # add data to worksheet + self.populateData(vertical_axis_title, worksheet, workbook) + + # create horizontal bar chart and add series to it + chart1 = self.configureSeries(workbook, 'column') + + # Add a chart title and some axis labels. + self.add_Customize(chart1, chart_title, horizontal_axis_title, vertical_axis_title) + + # Insert the chart into the worksheet (with an offset). + worksheet.insert_chart('F2', chart1, {'x_scale': 2, 'y_scale': 2}) + workbook.close() + + # Completion message + message_completion = "Done!" + GeneralUtils.show_message(message_completion) + + def line_chart(self): + """Invoked to create a line chart""" + + # get titles from customizeChart + chart_title, horizontal_axis_title, vertical_axis_title = self.customizeChart() + + # create xlsx file and add sheet file + workbook, worksheet = self.createFile() + + # Add data to worksheet + self.populateData(vertical_axis_title, worksheet, workbook) + + # create horizontal bar chart and add series to it + chart1 = self.configureSeries(workbook, 'line') + + # Add a chart title and some axis labels. + self.add_Customize(chart1, chart_title, horizontal_axis_title, vertical_axis_title) + + # Insert the chart into the worksheet (with an offset). + worksheet.insert_chart('F2', chart1, {'x_scale': 2, 'y_scale': 2}) + workbook.close() + + # Completion message + message_completion = "Done!" + GeneralUtils.show_message(message_completion) diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..c419263 --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/dev_notes.md b/dev_notes.md new file mode 100644 index 0000000..5b8d7da --- /dev/null +++ b/dev_notes.md @@ -0,0 +1,89 @@ +There are 2 levels of reports, Master Reports (e.g. PR, DR) which show all possible columns and Standard Views (e.g. PR_P1, DR_D1, DR_D2) which only show some of the columns from the master reports. + +## Report Fetching +All reports are fetched via the COUNTER_SUSHI API, data is returned as JSON. The JSON models and all other details can be found at: https://app.swaggerhub.com/apis/COUNTER/counter-sushi_5_0_api/1.0.0 + +### Authentication +SUSHI requests can use these parameters for authentication: +- customer_id (required) +- requestor_id (optional) +- api_key (optional) +There are also other optional parametrs like "platform" + +### URL Building +Request supported reports: [base_url]/reports?[authentication],[parameters] + +Request report: [base_url]/reports/[report_type]?[authentication],[parameters],[begin_date],[end_date] + +## TSV Report Formatting +All information about report formatting can be found at: https://www.projectcounter.org/code-of-practice-five-sections/3-0-technical-specifications/#formats + +All reports should be formatted like in this image: https://www.projectcounter.org/wp-content/uploads/2017/07/image3.png + +### Report Header +All reports should have a header that takes the first 12 rows of the report. The header should be formatted like in this image: https://www.projectcounter.org/wp-content/uploads/2018/09/FIG-3D.png + +A blank row should then be added to separate the report header from the column headings and body of the report. + +### Report Column Headings and Body +The column headings for each report type are as follows: + +PR +["Platform", "Data_Type", "Access_Method"] + +PR_P1 +["Platform"] + +DR +["Database", "Publisher", "Publisher_ID", "Platform", "Propriety_ID", "Data_Type", "Access_Method"] + +DR_D1 and DR_D2 +["Database", "Publisher", "Publisher_ID", "Platform", "Propriety_ID"] + +TR +["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Propriety_ID", "ISBN", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI", "Data_Type", "Section_Type", "YOP", "Access_Type", "Access_Method"] + +TR_B1 and TR_B2 +["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Propriety_ID", "ISBN", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI", "YOP"] + +TR_B3 +["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Propriety_ID", "ISBN", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI", "YOP", "Access_Type"] + +TR_J1 and TR_J2 +["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Propriety_ID", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI"] + +TR_J3 +["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Propriety_ID", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI", "Access_Type"] + +TR_J4 +["Title", "Publisher", "Publisher_ID", "Platform", "DOI", "Propriety_ID", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI", "YOP"] + +IR +["Item", "Publisher", "Publisher_ID", "Platform", "Authors", "Publication_Date", "Article_version", "DOI", "Propriety_ID", "ISBN", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI", "Parent_Title", "Parent_Authors", "Parent_Publication_Date", "Parent_Article_Version", "Parent_Data_Type", "Parent_DOI", "Parent_Proprietary_ID", "Parent_ISBN", "Parent_Print_ISSN", "Parent_Online_ISSN", "Parent_URI", "Data_Type", "YOP", "Access_Type", "Access_Method"] + +IR_A1 +["Item", "Publisher", "Publisher_ID", "Platform", "Authors", "Publication_Date", "Article_version", "DOI", "Propriety_ID", "Print_ISSN", "Online_ISSN", "Linking_ISSN", "URI", "Parent_Title", "Parent_Authors", "Parent_Article_Version", "Parent_DOI", "Parent_Proprietary_ID", "Parent_Print_ISSN", "Parent_Online_ISSN", "Parent_URI", "Access_Type"] + +IR_M1 +["Item", "Publisher", "Publisher_ID", "Platform", "DOI", "Propriety_ID", "URI"] + + +All report types end with these headings: +[ + "Metric_Type", + "Reporting_Period_Total", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +] + +The report body is then populated with the data received diff --git a/docs/Constants.py.rst b/docs/Constants.py.rst new file mode 100644 index 0000000..73b65cd --- /dev/null +++ b/docs/Constants.py.rst @@ -0,0 +1,7 @@ +Constants module +======================== + +.. automodule:: Constants + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/Costs.py.rst b/docs/Costs.py.rst new file mode 100644 index 0000000..39819bf --- /dev/null +++ b/docs/Costs.py.rst @@ -0,0 +1,7 @@ +Costs module +============ + +.. automodule:: Costs + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/FetchData.py.rst b/docs/FetchData.py.rst new file mode 100644 index 0000000..9c14339 --- /dev/null +++ b/docs/FetchData.py.rst @@ -0,0 +1,7 @@ +FetchData module +================ + +.. automodule:: FetchData + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/GeneralUtils.py.rst b/docs/GeneralUtils.py.rst new file mode 100644 index 0000000..097f3c5 --- /dev/null +++ b/docs/GeneralUtils.py.rst @@ -0,0 +1,7 @@ +GeneralUtils module +=================== + +.. automodule:: GeneralUtils + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/ImportFile.py.rst b/docs/ImportFile.py.rst new file mode 100644 index 0000000..b24dbd6 --- /dev/null +++ b/docs/ImportFile.py.rst @@ -0,0 +1,7 @@ +ImportFile module +================= + +.. automodule:: ImportFile + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/MainDriver.py.rst b/docs/MainDriver.py.rst new file mode 100644 index 0000000..8c07c5b --- /dev/null +++ b/docs/MainDriver.py.rst @@ -0,0 +1,7 @@ +MainDriver module +================= + +.. automodule:: MainDriver + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/ManageDB.py.rst b/docs/ManageDB.py.rst new file mode 100644 index 0000000..2fddb92 --- /dev/null +++ b/docs/ManageDB.py.rst @@ -0,0 +1,7 @@ +ManageDB module +=============== + +.. automodule:: ManageDB + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/ManageVendors.py.rst b/docs/ManageVendors.py.rst new file mode 100644 index 0000000..5a85453 --- /dev/null +++ b/docs/ManageVendors.py.rst @@ -0,0 +1,7 @@ +ManageVendors module +==================== + +.. automodule:: ManageVendors + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/Search.py.rst b/docs/Search.py.rst new file mode 100644 index 0000000..5f5647b --- /dev/null +++ b/docs/Search.py.rst @@ -0,0 +1,7 @@ +Search module +============= + +.. automodule:: Search + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/Settings.py.rst b/docs/Settings.py.rst new file mode 100644 index 0000000..bf6a998 --- /dev/null +++ b/docs/Settings.py.rst @@ -0,0 +1,7 @@ +Settings module +=============== + +.. automodule:: Settings + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/Visual.py.rst b/docs/Visual.py.rst new file mode 100644 index 0000000..ac000c1 --- /dev/null +++ b/docs/Visual.py.rst @@ -0,0 +1,7 @@ +Visual module +============= + +.. automodule:: Visual + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..c04fd7c --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,58 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys +sys.path.insert(0, os.path.abspath('..')) + + +# -- Project information ----------------------------------------------------- + +project = 'COUNTER 5 Report Tool' +copyright = '2020, Adam McGuigan, Chandler Acorn, Samuel Esan, Urvesh Boodhoon, Ziheng Huang' +author = 'Adam McGuigan, Chandler Acorn, Samuel Esan, Urvesh Boodhoon, Ziheng Huang' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx_autodoc_typehints', + 'sphinx_rtd_theme' +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The master document +master_doc = 'index' + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'sphinx_rtd_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] \ No newline at end of file diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..11721a1 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,31 @@ +.. COUNTER 5 Report Tool documentation master file, created by + sphinx-quickstart on Sat Mar 21 00:21:37 2020. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to COUNTER 5 Report Tool's documentation! +================================= + +.. toctree:: + :maxdepth: 2 + :caption: Modules: + + Constants.py + Costs.py + FetchData.py + GeneralUtils.py + ImportFile.py + MainDriver.py + ManageDB.py + ManageVendors.py + Search.py + Settings.py + Visual.py + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..922152e --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/pyinstaller-how-to.md b/docs/pyinstaller-how-to.md new file mode 100644 index 0000000..7f8dfa3 --- /dev/null +++ b/docs/pyinstaller-how-to.md @@ -0,0 +1,26 @@ +# How to create COUNTER 5 Report Tool's executable + +* Install python +* Install pip +* Install pyinstaller (pip install pyinstaller) + + +Before starting the process below, make sure to delete your alldata folder as anything included in it will be included in the EXE file. + + +### Creating the executable in terminal +1. Open terminal/Command Prompt +2. cd to your projectDirectory +3. Run: *pip install -r requirements.txt* +4. Run: *pip show validators* +This will show the validators package location. +5. Run: pyi-makespec --paths=DirectoryWithValidatorsPackage maindriver.py + +6. Run pyinstaller +* Windows: Run: pyinstaller --onefile MainDriver.py -n "Counter 5 Report Tool" -i main_icon.ico +* macOS: Run: pyinstaller --onefile --windowed -n "Counter 5 Report Tool" -i main_icon.ico +* UNIX: Run: pyinstaller --onefile MainDriver.py -n "Counter 5 Report Tool" -i main_icon.ico + +Check in the project directory, under the folder named dist. There should now be an .exe or .app file. Double click the file to open. + +If you receive an error about missing modules make sure that you ran pyi-makespec and gave the right path. If there are other modules other than validators that the exe says it is missing you will need to include their paths as well. diff --git a/executables/Linux/Counter 5 Report Tool.zip b/executables/Linux/Counter 5 Report Tool.zip new file mode 100644 index 0000000..ed5c54e Binary files /dev/null and b/executables/Linux/Counter 5 Report Tool.zip differ diff --git a/executables/Windows/COUNTER 5 Report Tool.zip b/executables/Windows/COUNTER 5 Report Tool.zip new file mode 100644 index 0000000..fcd6b66 Binary files /dev/null and b/executables/Windows/COUNTER 5 Report Tool.zip differ diff --git a/executables/macOS/Counter 5 Report Tool.zip b/executables/macOS/Counter 5 Report Tool.zip new file mode 100644 index 0000000..5410f30 Binary files /dev/null and b/executables/macOS/Counter 5 Report Tool.zip differ diff --git a/images/CostsHelp.png b/images/CostsHelp.png new file mode 100644 index 0000000..8f895b7 Binary files /dev/null and b/images/CostsHelp.png differ diff --git a/images/FetchReportsHelp.png b/images/FetchReportsHelp.png new file mode 100644 index 0000000..67a845f Binary files /dev/null and b/images/FetchReportsHelp.png differ diff --git a/images/FetchSpecialReportsHelp.png b/images/FetchSpecialReportsHelp.png new file mode 100644 index 0000000..580d920 Binary files /dev/null and b/images/FetchSpecialReportsHelp.png differ diff --git a/images/ManageVendorsHelp.png b/images/ManageVendorsHelp.png new file mode 100644 index 0000000..4d56f48 Binary files /dev/null and b/images/ManageVendorsHelp.png differ diff --git a/images/SearchHelp.png b/images/SearchHelp.png new file mode 100644 index 0000000..d49de2c Binary files /dev/null and b/images/SearchHelp.png differ diff --git a/images/SettingsHelp.PNG b/images/SettingsHelp.PNG new file mode 100644 index 0000000..cb2e5d7 Binary files /dev/null and b/images/SettingsHelp.PNG differ diff --git a/images/VisualHelp.png b/images/VisualHelp.png new file mode 100644 index 0000000..6ea5888 Binary files /dev/null and b/images/VisualHelp.png differ diff --git a/images/importReportsHelp.png b/images/importReportsHelp.png new file mode 100644 index 0000000..8d36b34 Binary files /dev/null and b/images/importReportsHelp.png differ diff --git a/main_icon.ico b/main_icon.ico new file mode 100644 index 0000000..f0a7902 Binary files /dev/null and b/main_icon.ico differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dc105e4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,13 @@ +certifi==2020.4.5.1 +chardet==3.0.4 +decorator==4.4.2 +idna==2.9 +PyQt5==5.12.3 +PyQt5-sip==12.7.2 +requests==2.23.0 +six==1.14.0 +urllib3==1.25.9 +validators==0.14.3 +XlsxWriter==1.2.8 +sphinx==2.4.4 +sphinx_autodoc_typehints diff --git a/tests/data/test_file_for_import.tsv b/tests/data/test_file_for_import.tsv new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_AddVendorDialog.py b/tests/test_AddVendorDialog.py new file mode 100644 index 0000000..5c0803c --- /dev/null +++ b/tests/test_AddVendorDialog.py @@ -0,0 +1,34 @@ +import sys +import unittest +from PyQt5.QtWidgets import QApplication, QDialog + + +from ui import AddVendorDialog + +app = QApplication(sys.argv) + +vendor_dialog = QDialog() +vendor_dialog_ui = AddVendorDialog.Ui_addVendorDialog() +vendor_dialog_ui.setupUi(vendor_dialog) + +class AddVendorDialogTests(unittest.TestCase): + + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(vendor_dialog_ui.nameEdit.text(), "") + self.assertEqual(vendor_dialog_ui.customerIdEdit.text(), "") + self.assertEqual(vendor_dialog_ui.baseUrlEdit.text(), "") + self.assertEqual(vendor_dialog_ui.requestorIdEdit.text(), "") + self.assertEqual(vendor_dialog_ui.apiKeyEdit.text(), "") + self.assertEqual(vendor_dialog_ui.platformEdit.text(), "") + self.assertEqual(vendor_dialog_ui.local_only_check_box.checkState(), False) + self.assertEqual(vendor_dialog_ui.descriptionEdit.toPlainText(), "") + + def test_ok_button(self): + okWidget = vendor_dialog_ui.buttonBox.Ok + self.assertIsNotNone(okWidget) + cancelWidget = vendor_dialog_ui.buttonBox.Cancel + self.assertIsNotNone(cancelWidget) + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/tests/test_CostsTab.py b/tests/test_CostsTab.py new file mode 100644 index 0000000..2bbe780 --- /dev/null +++ b/tests/test_CostsTab.py @@ -0,0 +1,53 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import CostsTab + +app = QApplication(sys.argv) +costTab_widget = QWidget() +costTab_widget_ui = CostsTab.Ui_costs_tab() +costTab_widget_ui.setupUi(costTab_widget) + +class CostsTabTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(costTab_widget_ui.costs_name_parameter_label.text(),"Name") + self.assertEqual(costTab_widget_ui.costs_report_parameter_label.text(), "Report") + self.assertEqual(costTab_widget_ui.costs_vendor_parameter_label.text(),"Vendor") + self.assertEqual(costTab_widget_ui.costs_year_parameter_label.text(),"Year") + self.assertEqual(costTab_widget_ui.costs_cost_in_local_currency_label.text(),"Cost in Local Currency") + self.assertEqual(costTab_widget_ui.costs_cost_in_local_currency_with_tax_label.text(),"Cost in Local Currency with Tax") + self.assertEqual(costTab_widget_ui.costs_cost_in_original_currency_label.text(),"Cost in Original Currency") + self.assertEqual(costTab_widget_ui.costs_original_currency_label.text(),"Original Currency") + + def test_doublesPinBox(self): + '''Test the defaults''' + self.assertEqual(costTab_widget_ui.costs_cost_in_local_currency_doublespinbox.text(),"") + self.assertEqual(costTab_widget_ui.costs_cost_in_local_currency_with_tax_doublespinbox.text(),"") + self.assertEqual(costTab_widget_ui.costs_cost_in_original_currency_doublespinbox.text(),"") + + def test_combobox(self): + '''Test the defaults''' + self.assertEqual(costTab_widget_ui.costs_name_parameter_combobox.isEditable(),True) + self.assertEqual(costTab_widget_ui.costs_report_parameter_combobox.isEditable(),False) + self.assertEqual(costTab_widget_ui.costs_vendor_parameter_combobox.isEditable(),False) + self.assertEqual(costTab_widget_ui.costs_original_currency_value_combobox.isEditable(),True) + self.assertEqual(costTab_widget_ui.costs_name_parameter_combobox.currentText(), "") + self.assertEqual(costTab_widget_ui.costs_report_parameter_combobox.currentText(), "") + self.assertEqual(costTab_widget_ui.costs_vendor_parameter_combobox.currentText(), "") + self.assertEqual(costTab_widget_ui.costs_original_currency_value_combobox.currentText(), "") + + def test_dateEdit(self): + '''Test the defaults''' + self.assertEqual(costTab_widget_ui.costs_year_parameter_dateedit.text(),"2000") + + def test_button(self): + '''Test the defaults''' + self.assertEqual(costTab_widget_ui.costs_clear_button.text(), "Clear") + self.assertEqual(costTab_widget_ui.costs_insert_button.text(), "Insert") + self.assertEqual(costTab_widget_ui.costs_load_button.text(), "Load") + self.assertEqual(costTab_widget_ui.costs_load_from_disk_button.text(), "Load From Disk") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_DisclaimerDialog.py b/tests/test_DisclaimerDialog.py new file mode 100644 index 0000000..328692b --- /dev/null +++ b/tests/test_DisclaimerDialog.py @@ -0,0 +1,22 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QDialog + +from ui import DisclaimerDialog + +app = QApplication(sys.argv) +disclaimer_dialog = QDialog() +disclaimer_dialog_ui = DisclaimerDialog.Ui_dialog() +disclaimer_dialog_ui.setupUi(disclaimer_dialog) + +class DisclaimerDialogTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(disclaimer_dialog_ui.label.text(),"Only reports supported by selected vendor will be retrieved!") + + def test_button(self): + okWidget = disclaimer_dialog_ui.buttonBox.Ok + self.assertIsNotNone(okWidget) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_FetchProgressDialog.py b/tests/test_FetchProgressDialog.py new file mode 100644 index 0000000..f0e2b61 --- /dev/null +++ b/tests/test_FetchProgressDialog.py @@ -0,0 +1,28 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QDialog + +from ui import FetchProgressDialog + +app = QApplication(sys.argv) +fetch_progress_dialog = QDialog() +fetch_progress_dialog_ui = FetchProgressDialog.Ui_FetchProgressDialog() +fetch_progress_dialog_ui.setupUi(fetch_progress_dialog) + + +class FetchProgressDialogTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(fetch_progress_dialog_ui.status_label.text(), "Fetching...") + self.assertEqual(fetch_progress_dialog_ui.progress_bar.text(), "24%") + + def test_button(self): + okWidget = fetch_progress_dialog_ui.buttonBox.Ok + self.assertIsNotNone(okWidget) + retryWidget = fetch_progress_dialog_ui.buttonBox.Retry + self.assertIsNotNone(retryWidget) + cancelWidget = fetch_progress_dialog_ui.buttonBox.Cancel + self.assertIsNotNone(cancelWidget) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_FetchReportsTab.py b/tests/test_FetchReportsTab.py new file mode 100644 index 0000000..a7e3333 --- /dev/null +++ b/tests/test_FetchReportsTab.py @@ -0,0 +1,50 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import FetchReportsTab + +app = QApplication(sys.argv) +fetchReportsTab_widget = QWidget() +fetchReportsTab_widget_ui = FetchReportsTab.Ui_fetch_reports_tab() +fetchReportsTab_widget_ui.setupUi(fetchReportsTab_widget) + +class FetchReportsTabTest(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(fetchReportsTab_widget_ui.Adv_Fetch_text.text(),"Advanced Fetch Reports") + self.assertEqual(fetchReportsTab_widget_ui.label_11.text(),"Select Vendors") + self.assertEqual(fetchReportsTab_widget_ui.label_12.text(), "Select Report Types") + self.assertEqual(fetchReportsTab_widget_ui.label_38.text(), "Not a yearly date range") + self.assertEqual(fetchReportsTab_widget_ui.label_41.text(), "Report(s) will be saved to:") + self.assertEqual(fetchReportsTab_widget_ui.label_10.text(), "End Date") + self.assertEqual(fetchReportsTab_widget_ui.label_9.text(), "Begin Date") + self.assertEqual(fetchReportsTab_widget_ui.label_8.text(), "Date Range") + self.assertEqual(fetchReportsTab_widget_ui.label_34.text(), "Year") + self.assertEqual(fetchReportsTab_widget_ui.label_35.text(), "Fetch All Reports") + + def test_dateEdit(self): + '''Test the defaults''' + self.assertEqual(fetchReportsTab_widget_ui.begin_date_edit_fetch_month.text(),"01") + self.assertEqual(fetchReportsTab_widget_ui.begin_date_edit_fetch_year.text(),"2020") + self.assertEqual(fetchReportsTab_widget_ui.end_date_edit_fetch_month.text(),"01") + self.assertEqual(fetchReportsTab_widget_ui.end_date_edit_fetch_year.text(),"2020") + self.assertEqual(fetchReportsTab_widget_ui.All_reports_edit_fetch.text(),"2020") + + def test_pushButton(self): + '''Test the defaults''' + self.assertEqual(fetchReportsTab_widget_ui.deselect_vendors_button_fetch.text(),"Deselect All") + self.assertEqual(fetchReportsTab_widget_ui.select_vendors_button_fetch.text(),"Select All") + self.assertEqual(fetchReportsTab_widget_ui.deselect_report_types_button_fetch.text(), "Deselect All") + self.assertEqual(fetchReportsTab_widget_ui.select_report_types_button_fetch.text(), "Select All") + self.assertEqual(fetchReportsTab_widget_ui.report_types_help_button.text(),"?") + self.assertEqual(fetchReportsTab_widget_ui.fetch_advanced_button.text(),"Fetch Selected Reports") + self.assertEqual(fetchReportsTab_widget_ui.custom_dir_button.text(),"Change") + self.assertEqual(fetchReportsTab_widget_ui.fetch_all_data_button.text(),"Fetch All Reports") + + def test_lineEdit(self): + '''Test the defaults''' + self.assertEqual(fetchReportsTab_widget_ui.custom_dir_edit.text(),"") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_FetchSpecialReportsTab.py b/tests/test_FetchSpecialReportsTab.py new file mode 100644 index 0000000..99a9200 --- /dev/null +++ b/tests/test_FetchSpecialReportsTab.py @@ -0,0 +1,43 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import FetchSpecialReportsTab + +app = QApplication(sys.argv) +fetchSpecialReportsTab_widget = QWidget() +fetchSpecialReportsTab_widget_ui = FetchSpecialReportsTab.Ui_fetch_special_reports_tab() +fetchSpecialReportsTab_widget_ui.setupUi(fetchSpecialReportsTab_widget) + +class FetchSpecialReportsTabTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(fetchSpecialReportsTab_widget_ui.label_20.text(),"Select Report Type") + self.assertEqual(fetchSpecialReportsTab_widget_ui.label_21.text(), "Select Vendors") + self.assertEqual(fetchSpecialReportsTab_widget_ui.label_14.text(),"Options") + self.assertEqual(fetchSpecialReportsTab_widget_ui.label_25.text(),"End Date") + self.assertEqual(fetchSpecialReportsTab_widget_ui.label_24.text(),"Begin Date") + self.assertEqual(fetchSpecialReportsTab_widget_ui.label_15.text(),"Date Range") + + def test_radioButrton(self): + '''Test the defaults''' + self.assertEqual(fetchSpecialReportsTab_widget_ui.dr_radio_button.text(),"DR") + self.assertEqual(fetchSpecialReportsTab_widget_ui.pr_radio_button.text(), "PR") + self.assertEqual(fetchSpecialReportsTab_widget_ui.tr_radio_button.text(), "TR") + self.assertEqual(fetchSpecialReportsTab_widget_ui.ir_radio_button.text(), "IR") + + def test_pushButton(self): + '''Test the defaults''' + self.assertEqual(fetchSpecialReportsTab_widget_ui.deselect_vendors_button_special.text(),"Deselect All") + self.assertEqual(fetchSpecialReportsTab_widget_ui.select_vendors_button_special.text(),"Select All") + self.assertEqual(fetchSpecialReportsTab_widget_ui.fetch_special_data_button.text(),"Fetch Special Report") + + def test_dateEdit(self): + '''Test the defaults''' + self.assertEqual(fetchSpecialReportsTab_widget_ui.begin_date_edit_special_month.text(),"01") + self.assertEqual(fetchSpecialReportsTab_widget_ui.begin_date_edit_special_year.text(),"2020") + self.assertEqual(fetchSpecialReportsTab_widget_ui.end_date_edit_special_month.text(),"01") + self.assertEqual(fetchSpecialReportsTab_widget_ui.end_date_edit_special_year.text(),"2020") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_ImportFile.py b/tests/test_ImportFile.py new file mode 100644 index 0000000..38d476c --- /dev/null +++ b/tests/test_ImportFile.py @@ -0,0 +1,184 @@ +import sys +import pytest +from os import path +from PyQt5.QtWidgets import QMainWindow +from PyQt5.QtCore import QDate +from PyQt5.QtGui import QStandardItemModel +from ui import MainWindow +from ImportFile import ImportReportController +from ManageVendors import Vendor +from FetchData import ALL_REPORTS, CompletionStatus +import Settings +import GeneralUtils +import json + + +@pytest.fixture(scope='session') +def qapp_args(): + return sys.argv + + +@pytest.fixture(scope='session') +def vendors() -> list: + assert path.exists("./data/vendor_manager/vendors.dat"), \ + "test vendor.dat should be placed in ./tests/data/vendor_manager/" + + vendor_list = [] + vendors_json_string = GeneralUtils.read_json_file("./data/vendor_manager/vendors.dat") + vendor_dicts = json.loads(vendors_json_string) + for json_dict in vendor_dicts: + vendor = Vendor.from_json(json_dict) + vendor_list.append(vendor) + + return vendor_list + + +@pytest.fixture(scope='session') +def settings() -> Settings.SettingsModel: + return Settings.SettingsModel(Settings.YEARLY_DIR, + Settings.OTHER_DIR, + Settings.REQUEST_INTERVAL, + Settings.REQUEST_TIMEOUT, + Settings.CONCURRENT_VENDORS, + Settings.CONCURRENT_REPORTS, + Settings.EMPTY_CELL, + Settings.USER_AGENT) + + +@pytest.fixture +def controller(qtbot, settings): # ImportFileController without populated vendor list + window = QMainWindow() + window_ui = MainWindow.Ui_mainWindow() + window_ui.setupUi(window) + + c = ImportReportController([], settings, window_ui) + yield c + + +@pytest.fixture +def controller_v(qtbot, vendors, settings): # ImportFileController with populated vendor list + window = QMainWindow() + window_ui = MainWindow.Ui_mainWindow() + window_ui.setupUi(window) + + c = ImportReportController(vendors, settings, window_ui) + yield c + + +def test_on_vendors_changed(controller, vendors): + controller.on_vendors_changed(vendors) + + assert controller.selected_vendor_index == -1 + assert len(controller.vendors) == len(vendors) + assert controller.vendor_list_model.rowCount() == len(vendors) + + +def test_update_vendors(controller, vendors): + controller.update_vendors(vendors) + + assert len(controller.vendors) == len(vendors) + for i in range(len(controller.vendors)): + assert controller.vendors[i].name == vendors[i].name + + +def test_update_vendors_ui(controller, vendors): + controller.update_vendors(vendors) + + vendor_list_model = controller.vendor_list_model + assert vendor_list_model.rowCount() == 0 + + controller.update_vendors_ui() + assert vendor_list_model.rowCount() == len(controller.vendors) + for i in range(len(controller.vendors)): + assert vendor_list_model.item(i, 0).text() == controller.vendors[i].name + + +def test_on_vendor_selected(controller_v, vendors, settings): + index_to_select = 5 + model_index = QStandardItemModel.createIndex(QStandardItemModel(), index_to_select, 0) + controller_v.on_vendor_selected(model_index) + + assert controller_v.selected_vendor_index == index_to_select + + +def test_on_report_type_selected(controller_v): + + index_to_select = 3 + model_index = QStandardItemModel.createIndex(QStandardItemModel(), index_to_select, 0) + controller_v.on_report_type_selected(model_index) + + assert controller_v.selected_report_type_index == index_to_select + + +def test_on_date_changed(controller_v): + test_date = QDate.currentDate() + controller_v.on_date_changed(test_date) + + assert controller_v.date == test_date + + +def test_on_import_clicked(controller_v): + # No vendor selected + controller_v.on_import_clicked() + controller_v.selected_vendor_index = 1 + + # No report type selected + controller_v.on_import_clicked() + controller_v.selected_report_type_index = 1 + + vendor = controller_v.vendors[controller_v.selected_vendor_index] + report_type = ALL_REPORTS[controller_v.selected_report_type_index] + file_dir = f"{controller_v.settings.yearly_directory}{controller_v.date.toString('yyyy')}/{vendor.name}/" + file_name = f"{controller_v.date.toString('yyyy')}_{vendor.name}_{report_type}.tsv" + file_path = file_dir + file_name + + # No file selected + controller_v.on_import_clicked() + + # Invalid file selected + controller_v.selected_file_path = "./data/invalid_file" + controller_v.on_import_clicked() + + # Valid file selected + controller_v.selected_file_path = "./data/test_file_for_import.tsv" + controller_v.on_import_clicked() + assert path.isfile(file_path) + + +def test_import_file(controller_v): + controller_v.selected_vendor_index = 1 + controller_v.selected_report_type_index = 1 + + vendor = controller_v.vendors[controller_v.selected_vendor_index] + report_type = ALL_REPORTS[controller_v.selected_report_type_index] + file_dir = f"{controller_v.settings.yearly_directory}{controller_v.date.toString('yyyy')}/{vendor.name}/" + file_name = f"{controller_v.date.toString('yyyy')}_{vendor.name}_{report_type}.tsv" + file_path = file_dir + file_name + + # No file selected + assert controller_v.import_report(vendor, report_type).completion_status == CompletionStatus.FAILED + + # Invalid file selected + controller_v.selected_file_path = "./data/invalid_file" + assert controller_v.import_report(vendor, report_type).completion_status == CompletionStatus.FAILED + + # Valid file selected + controller_v.selected_file_path = "./data/test_file_for_import.tsv" + assert controller_v.import_report(vendor, report_type).completion_status == CompletionStatus.SUCCESSFUL + assert path.isfile(file_path) + + +def test_open_file(controller_v): + # Invalid file/folder + controller_v.open_explorer("./data/invalid_file") + + # Valid file + controller_v.open_explorer("./data/test_file_for_import.tsv") + + # Valid folder + controller_v.open_explorer("./data/") + + + + + diff --git a/tests/test_ImportReportTab.py b/tests/test_ImportReportTab.py new file mode 100644 index 0000000..70310f7 --- /dev/null +++ b/tests/test_ImportReportTab.py @@ -0,0 +1,35 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import ImportReportTab + +app = QApplication(sys.argv) +importReportTab_widget = QWidget() +importReportTab_widget_ui = ImportReportTab.Ui_import_report_tab() +importReportTab_widget_ui.setupUi(importReportTab_widget) + +class ImportReportTabTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(importReportTab_widget_ui.label_17.text(),"Select Report Type") + self.assertEqual(importReportTab_widget_ui.label_18.text(),"Select Vendor") + self.assertEqual(importReportTab_widget_ui.label_19.text(),"Report Year") + self.assertEqual(importReportTab_widget_ui.label_16.text(), "Date") + self.assertEqual(importReportTab_widget_ui.label_36.text(),"Target Report File") + + def test_dateEdit(self): + '''Test the defaults''' + self.assertEqual(importReportTab_widget_ui.report_year_date_edit.text(),"2000") + + def test_pushButton(self): + '''Test the defaults''' + self.assertEqual(importReportTab_widget_ui.select_file_button.text(),"Select File") + self.assertEqual(importReportTab_widget_ui.import_report_button.text(),"Import Selected Report") + + def test_lineEdit(self): + '''Test the defaults''' + self.assertEqual(importReportTab_widget_ui.selected_file_edit.text(),"") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_MainWindow.py b/tests/test_MainWindow.py new file mode 100644 index 0000000..057bef7 --- /dev/null +++ b/tests/test_MainWindow.py @@ -0,0 +1,160 @@ +import sys +import unittest +from PyQt5.QtWidgets import QApplication, QMainWindow + +from ui import MainWindow +app = QApplication(sys.argv) + +main_window = QMainWindow() +main_window_ui = MainWindow.Ui_mainWindow() +main_window_ui.setupUi(main_window) + +class MyTestCase(unittest.TestCase): + + def test_addVendorButton(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.addVendorButton.text(), "Add New Vendor") + + def test_edit_vendor_options_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.removeVendorButton.text(), "Remove Vendor") + self.assertEqual(main_window_ui.saveVendorChangesButton.text(), "Save Changes") + self.assertEqual(main_window_ui.undoVendorChangesButton.text(), "Undo Changes") + + def test_select_vendor_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.deselect_vendors_button_fetch.text(), "Deselect All") + self.assertEqual(main_window_ui.select_vendors_button_fetch.text(), "Select All") + self.assertEqual(main_window_ui.deselect_vendors_button_special.text(), "Deselect All") + self.assertEqual(main_window_ui.select_vendors_button_special.text(), "Select All") + + def test_select_report_types_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.deselect_report_types_button_fetch.text(), "Deselect All") + self.assertEqual(main_window_ui.select_report_types_button_fetch.text(), "Select All") + self.assertEqual(main_window_ui.toolButton.text(), "?") + + def test_fetch_advanced_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.fetch_advanced_button.text(), "Fetch Selected Reports") + + def test_fetch_all_data_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.fetch_all_data_button.text(), "Fetch All Reports") + + def test_fetch_speical_data_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.fetch_special_data_button.text(), "Fetch Special Report") + + def test_select_file_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.select_file_button.text(), "Select File") + self.assertEqual(main_window_ui.import_file_button.text(), "Import Selected File") + + def test_search_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.search_button.text(), "Search") + + def test_setting_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.concurrent_reports_help_button.text(), "?") + self.assertEqual(main_window_ui.concurrent_vendors_help_button.text(), "?") + self.assertEqual(main_window_ui.empty_cell_help_button.text(), "?") + self.assertEqual(main_window_ui.other_directory_button.text(), "Choose Folder...") + self.assertEqual(main_window_ui.other_directory_help_button.text(), "?") + self.assertEqual(main_window_ui.request_interval_help_button.text(), "?") + self.assertEqual(main_window_ui.request_timeout_help_button.text(), "?") + self.assertEqual(main_window_ui.yearly_directory_button.text(), "Choose Folder...") + self.assertEqual(main_window_ui.yearly_directory_help_button.text(), "?") + + def test_setting_spin_box(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.concurrent_reports_spin_box.value(),0) + self.assertEqual(main_window_ui.concurrent_vendors_spin_box.value(), 0) + self.assertEqual(main_window_ui.request_interval_spin_box.value(), 0) + self.assertEqual(main_window_ui.request_timeout_spin_box.value(), 0) + + def test_date_edit(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.begin_date_edit_fetch.text(), "2020-01") + self.assertEqual(main_window_ui.end_date_edit_fetch.text(), "2020-01") + self.assertEqual(main_window_ui.All_reports_edit_fetch.text(), "2020") + self.assertEqual(main_window_ui.begin_date_edit_special.text(), "2000-01") + self.assertEqual(main_window_ui.end_date_edit_special.text(), "2000-01") + self.assertEqual(main_window_ui.report_year_date_edit.text(), "2000") + + def test_radio_button(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.dr_radio_button.isChecked(), False) + self.assertEqual(main_window_ui.ir_radio_button.isChecked(), False) + self.assertEqual(main_window_ui.tr_radio_button.isChecked(), False) + self.assertEqual(main_window_ui.pr_radio_button.isChecked(), True) + + def test_label(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.label.text(), "Name") + self.assertEqual(main_window_ui.label_2.text(), "Customer ID") + self.assertEqual(main_window_ui.label_3.text(), "Base URL") + self.assertEqual(main_window_ui.label_4.text(), "Requestor ID") + self.assertEqual(main_window_ui.label_5.text(), "API Key") + self.assertEqual(main_window_ui.label_6.text(), "Platform") + self.assertEqual(main_window_ui.label_7.text(), "Edit Vendor") + + self.assertEqual(main_window_ui.label_9.text(), "Begin Date") + self.assertEqual(main_window_ui.label_10.text(), "End Date") + self.assertEqual(main_window_ui.label_11.text(), "Select Vendors") + self.assertEqual(main_window_ui.label_12.text(), "Select Report Types") + self.assertEqual(main_window_ui.label_13.text(), "Select Vendor") + self.assertEqual(main_window_ui.label_14.text(), "Search Keyword(s)") + self.assertEqual(main_window_ui.label_15.text(), "Search Results") + self.assertEqual(main_window_ui.label_16.text(), "Search by:") + self.assertEqual(main_window_ui.label_17.text(), "Select Report Type") + self.assertEqual(main_window_ui.label_18.text(), "Select Vendor") + self.assertEqual(main_window_ui.label_19.text(), "Report Year") + self.assertEqual(main_window_ui.label_20.text(), "Select Report Type") + self.assertEqual(main_window_ui.label_21.text(), "Select Vendors") + self.assertEqual(main_window_ui.label_22.text(), "Begin Date") + self.assertEqual(main_window_ui.label_23.text(), "End Date") + self.assertEqual(main_window_ui.label_24.text(), "Reports") + self.assertEqual(main_window_ui.label_25.text(), "Yearly Reports Directory") + self.assertEqual(main_window_ui.label_26.text(), "Search") + + self.assertEqual(main_window_ui.label_28.text(), "Description") + self.assertEqual(main_window_ui.label_29.text(), "Other Reports Directory") + self.assertEqual(main_window_ui.label_30.text(), "Report Request Interval") + self.assertEqual(main_window_ui.label_31.text(), "Concurrent Vendors") + self.assertEqual(main_window_ui.label_32.text(), "Concurrent Reports") + self.assertEqual(main_window_ui.label_33.text(), "Empty Cell") + self.assertEqual(main_window_ui.label_34.text(), "Year") + self.assertEqual(main_window_ui.label_35.text(), "Fetch All Reports") + + self.assertEqual(main_window_ui.label_37.text(), "Request Timeout") + self.assertEqual(main_window_ui.label_39.text(), "Local Only Vendor") + + self.assertEqual(main_window_ui.Adv_Fetch_text.text(), "Advanced Fetch Reports") + self.assertEqual(main_window_ui.selected_file_label.text(), "None") + + def test_check_box(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.local_only_check_box.checkState(), False) + self.assertEqual(main_window_ui.isbn_checkbox.checkState(), False) + self.assertEqual(main_window_ui.issn_checkbox.checkState(), False) + self.assertEqual(main_window_ui.title_checkbox.checkState(), False) + + def test_line_edit(self): + '''Test the defaults''' + self.assertEqual(main_window_ui.apiKeyEdit.text(),"") + self.assertEqual(main_window_ui.baseUrlEdit.text(), "") + self.assertEqual(main_window_ui.customerIdEdit.text(), "") + self.assertEqual(main_window_ui.descriptionEdit.toPlainText(), "") + self.assertEqual(main_window_ui.nameEdit.text(), "") + self.assertEqual(main_window_ui.platformEdit.text(), "") + self.assertEqual(main_window_ui.requestorIdEdit.text(), "") + self.assertEqual(main_window_ui.search_term_edit.text(), "") + self.assertEqual(main_window_ui.other_directory_edit.text(), "") + self.assertEqual(main_window_ui.yearly_directory_edit.text(), "") + self.assertEqual(main_window_ui.empty_cell_edit.text(), "") + self.assertEqual(main_window_ui.save_location_edit_fetch_2.text(), "[In Development]") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_ManageVendorsTab.py b/tests/test_ManageVendorsTab.py new file mode 100644 index 0000000..ff6358b --- /dev/null +++ b/tests/test_ManageVendorsTab.py @@ -0,0 +1,53 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import ManageVendorsTab + +app = QApplication(sys.argv) +manageVendorsTab_widget = QWidget() +manageVendorsTab_widget_ui = ManageVendorsTab.Ui_manage_vendors_tab() +manageVendorsTab_widget_ui.setupUi(manageVendorsTab_widget) + +class ManageVendorsTabTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(manageVendorsTab_widget_ui.label_13.text(),"Select Vendor") + self.assertEqual(manageVendorsTab_widget_ui.companiesText.text(),"External Companies") + self.assertEqual(manageVendorsTab_widget_ui.label.text(),"Name") + self.assertEqual(manageVendorsTab_widget_ui.label_2.text(),"Customer ID") + self.assertEqual(manageVendorsTab_widget_ui.label_28.text(), "Description") + self.assertEqual(manageVendorsTab_widget_ui.label_3.text(),"Base URL") + self.assertEqual(manageVendorsTab_widget_ui.label_39.text(), "Local Only Vendor") + self.assertEqual(manageVendorsTab_widget_ui.label_4.text(),"Requestor ID") + self.assertEqual(manageVendorsTab_widget_ui.label_5.text(), "API Key") + self.assertEqual(manageVendorsTab_widget_ui.label_6.text(), "Platform") + self.assertEqual(manageVendorsTab_widget_ui.name_validation_label.text(),"Validation label") + self.assertEqual(manageVendorsTab_widget_ui.url_validation_label.text(),"Validation label") + + def test_pushButton(self): + '''Test the defaults''' + self.assertEqual(manageVendorsTab_widget_ui.addVendorButton.text(),"Add New Vendor") + self.assertEqual(manageVendorsTab_widget_ui.exportVendorsButton.text(),"Export Vendors") + self.assertEqual(manageVendorsTab_widget_ui.importVendorsButton.text(),"Import Vendors") + self.assertEqual(manageVendorsTab_widget_ui.removeVendorButton.text(), "Remove Vendor") + self.assertEqual(manageVendorsTab_widget_ui.saveVendorChangesButton.text(), "Save Changes") + self.assertEqual(manageVendorsTab_widget_ui.undoVendorChangesButton.text(), "Undo Changes") + + def test_lineEdit(self): + '''Test the defaults''' + self.assertEqual(manageVendorsTab_widget_ui.apiKeyEdit.text(),"") + self.assertEqual(manageVendorsTab_widget_ui.baseUrlEdit.text(), "") + self.assertEqual(manageVendorsTab_widget_ui.companiesEdit.toPlainText(), "") + self.assertEqual(manageVendorsTab_widget_ui.customerIdEdit.text(), "") + self.assertEqual(manageVendorsTab_widget_ui.descriptionEdit.toPlainText(), "") + self.assertEqual(manageVendorsTab_widget_ui.nameEdit.text(), "") + self.assertEqual(manageVendorsTab_widget_ui.platformEdit.text(), "") + self.assertEqual(manageVendorsTab_widget_ui.requestorIdEdit.text(), "") + + def test_checkBox(self): + '''Test the defaults''' + self.assertEqual(manageVendorsTab_widget_ui.local_only_check_box.isChecked(),False) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_MessageDialog.py b/tests/test_MessageDialog.py new file mode 100644 index 0000000..0400ecb --- /dev/null +++ b/tests/test_MessageDialog.py @@ -0,0 +1,22 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QDialog + +from ui import MessageDialog + +app = QApplication(sys.argv) +message_dialog = QDialog() +message_dialog_ui = MessageDialog.Ui_message_dialog() +message_dialog_ui.setupUi(message_dialog) + +class MessageDialogTest(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(message_dialog_ui.message_label.text(),"Message!") + + def test_button(self): + okWidget = message_dialog_ui.buttonBox.Ok + self.assertIsNotNone(okWidget) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_RemoveVendorDialog.py b/tests/test_RemoveVendorDialog.py new file mode 100644 index 0000000..8fb56e7 --- /dev/null +++ b/tests/test_RemoveVendorDialog.py @@ -0,0 +1,23 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QDialog + +from ui import RemoveVendorDialog + +app = QApplication(sys.argv) +remove_vendor_dialog = QDialog() +remove_vendor_dialog_ui = RemoveVendorDialog.Ui_dialog_remove() +remove_vendor_dialog_ui.setupUi(remove_vendor_dialog) + +class RemoveVendorDialogTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(remove_vendor_dialog_ui.label.text(), + "Are you sure you want to remove this vendor?") + + def test_button(self): + okWidget = remove_vendor_dialog_ui.buttonBox.Ok + self.assertIsNotNone(okWidget) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_ReportResultWidget.py b/tests/test_ReportResultWidget.py new file mode 100644 index 0000000..214e84a --- /dev/null +++ b/tests/test_ReportResultWidget.py @@ -0,0 +1,28 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import ReportResultWidget + +app = QApplication(sys.argv) +report_result_widget = QWidget() +report_result_ui = ReportResultWidget.Ui_ReportResultWidget() +report_result_ui.setupUi(report_result_widget) + +class ReportResultWidgetTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(report_result_ui.file_label.text(),"Saved as: Bleh.tsv") + self.assertEqual(report_result_ui.message_label.text(),"No exception messages") + self.assertEqual(report_result_ui.label_6.text(),"Retry") + self.assertEqual(report_result_ui.success_label.text(),"Failed!") + self.assertEqual(report_result_ui.report_type_label.text(),"TR_J1") + + def test_button(self): + self.assertEqual(report_result_ui.folder_button.text(),"") + + def test_checkBox(self): + self.assertEqual(report_result_ui.retry_check_box.checkState(),False) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_ReportTypeDialog.py b/tests/test_ReportTypeDialog.py new file mode 100644 index 0000000..ad1c8fc --- /dev/null +++ b/tests/test_ReportTypeDialog.py @@ -0,0 +1,26 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QDialog + +from ui import ReportTypeDialog + +app = QApplication(sys.argv) +report_type_dialog = QDialog() +report_type_dialog_ui = ReportTypeDialog.Ui_report_type_dialog() +report_type_dialog_ui.setupUi(report_type_dialog) + +class ReportTypeDialogTests(unittest.TestCase): + def test_comboBox(self): + '''Test the defaults''' + self.assertEqual(report_type_dialog_ui.report_type_combobox.isEditable(),False) + self.assertEqual(report_type_dialog_ui.report_type_combobox.currentText(),"") + + def test_buttonBox(self): + '''Test the defaults''' + okWidget = report_type_dialog_ui.buttonBox.Ok + self.assertIsNotNone(okWidget) + cancelWidget = report_type_dialog_ui.buttonBox.Cancel + self.assertIsNotNone(cancelWidget) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_SearchAndFrame.py b/tests/test_SearchAndFrame.py new file mode 100644 index 0000000..ffd2968 --- /dev/null +++ b/tests/test_SearchAndFrame.py @@ -0,0 +1,19 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QFrame + +from ui import SearchAndFrame + +app = QApplication(sys.argv) + +search_and_clause_frame = QFrame() +search_and_clause_frame_ui = SearchAndFrame.Ui_search_and_clause_parameter_frame() +search_and_clause_frame_ui.setupUi(search_and_clause_frame) + +class SearchAndClauseFrameTests(unittest.TestCase): + def test_button(self): + self.assertEqual(search_and_clause_frame_ui.search_add_or_clause_button.text(),"Add \"Or\" Clause") + self.assertEqual(search_and_clause_frame_ui.search_remove_and_clause_button.text(),"Remove \"And\" Clause") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_SearchOrFrame.py b/tests/test_SearchOrFrame.py new file mode 100644 index 0000000..4d96742 --- /dev/null +++ b/tests/test_SearchOrFrame.py @@ -0,0 +1,26 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QFrame + +from ui import SearchOrFrame + +app = QApplication(sys.argv) + +search_or_clause_frame = QFrame() +search_or_clause_frame_ui = SearchOrFrame.Ui_search_or_clause_parameter_frame() +search_or_clause_frame_ui.setupUi(search_or_clause_frame) + +class SearchOrClauseFrameTests(unittest.TestCase): + def test_button(self): + self.assertEqual(search_or_clause_frame_ui.search_remove_or_clause_button.text(),"Remove \"Or\" Clause") + + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(search_or_clause_frame_ui.search_value_parameter_lineedit.text(), "") + + def test_combo_box(self): + self.assertEqual(search_or_clause_frame_ui.search_comparison_parameter_combobox.currentText(),"") + self.assertEqual(search_or_clause_frame_ui.search_field_parameter_combobox.currentText(),"") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_SearchTab.py b/tests/test_SearchTab.py new file mode 100644 index 0000000..39da463 --- /dev/null +++ b/tests/test_SearchTab.py @@ -0,0 +1,44 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import SearchTab + +app = QApplication(sys.argv) +searchTab_widget = QWidget() +searchTab_widget_ui = SearchTab.Ui_search_tab() +searchTab_widget_ui.setupUi(searchTab_widget) + +class SearchTabTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(searchTab_widget_ui.search_end_year_parameter_label.text(),"End year") + + def test_radioButton(self): + '''Test the defaults''' + self.assertEqual(searchTab_widget_ui.dont_open_radioButton.text(),"Don't Open") + self.assertEqual(searchTab_widget_ui.open_both_radioButton.text(), "Open Both") + self.assertEqual(searchTab_widget_ui.open_file_radioButton.text(), "Open File") + self.assertEqual(searchTab_widget_ui.open_folder_radioButton.text(), "Open Folder") + self.assertEqual(searchTab_widget_ui.search_report_parameter_label.text(),"Report") + self.assertEqual(searchTab_widget_ui.search_start_year_parameter_label.text(),"Start Year") + + def test_pushButton(self): + '''Test the defaults''' + self.assertEqual(searchTab_widget_ui.search_button.text(),"Search") + self.assertEqual(searchTab_widget_ui.search_export_button.text(),"Export Search") + self.assertEqual(searchTab_widget_ui.search_import_button.text(),"Import Search") + self.assertEqual(searchTab_widget_ui.search_add_and_button.text(),"Add \"And\" Clause") + + def test_dateEdit(self): + '''Test the defaults''' + self.assertEqual(searchTab_widget_ui.search_end_year_parameter_dateedit.text(),"2000") + self.assertEqual(searchTab_widget_ui.search_start_year_parameter_dateedit.text(),"2000") + + def test_comboBox(self): + '''Test the defaults''' + self.assertEqual(searchTab_widget_ui.search_report_parameter_combobox.isEditable(), False) + self.assertEqual(searchTab_widget_ui.search_report_parameter_combobox.currentText(), "") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_SettingTab.py b/tests/test_SettingTab.py new file mode 100644 index 0000000..ee8c3ca --- /dev/null +++ b/tests/test_SettingTab.py @@ -0,0 +1,56 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import SettingsTab + +app = QApplication(sys.argv) +settingsTab_widget = QWidget() +settingsTab_widget_ui = SettingsTab.Ui_settings_tab() +settingsTab_widget_ui.setupUi(settingsTab_widget) + +class SettingsTabTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(settingsTab_widget_ui.label_25.text(), "Yearly Reports Directory") + self.assertEqual(settingsTab_widget_ui.label_29.text(),"Other Reports Directory") + self.assertEqual(settingsTab_widget_ui.label_30.text(), "Report Request Interval") + self.assertEqual(settingsTab_widget_ui.label_31.text(), "Concurrent Vendors") + self.assertEqual(settingsTab_widget_ui.label_32.text(), "Concurrent Reports") + self.assertEqual(settingsTab_widget_ui.label_33.text(), "Empty Cell") + self.assertEqual(settingsTab_widget_ui.label_37.text(), "Request Timeout") + self.assertEqual(settingsTab_widget_ui.label_73.text(), "User Agent") + self.assertEqual(settingsTab_widget_ui.label_24.text(),"Reports") + self.assertEqual(settingsTab_widget_ui.label_26.text(),"Search") + + def test_pushButton(self): + '''Test the defaults''' + self.assertEqual(settingsTab_widget_ui.save_button.text(),"Save Changes") + self.assertEqual(settingsTab_widget_ui.concurrent_reports_help_button.text(),"?") + self.assertEqual(settingsTab_widget_ui.concurrent_vendors_help_button.text(), "?") + self.assertEqual(settingsTab_widget_ui.empty_cell_help_button.text(), "?") + self.assertEqual(settingsTab_widget_ui.other_directory_help_button.text(), "?") + self.assertEqual(settingsTab_widget_ui.request_interval_help_button.text(), "?") + self.assertEqual(settingsTab_widget_ui.request_timeout_help_button.text(), "?") + self.assertEqual(settingsTab_widget_ui.other_directory_button.text(),"Choose") + self.assertEqual(settingsTab_widget_ui.yearly_directory_button.text(), "Choose") + self.assertEqual(settingsTab_widget_ui.user_agent_help_button.text(), "?") + self.assertEqual(settingsTab_widget_ui.yearly_directory_help_button.text(), "?") + self.assertEqual(settingsTab_widget_ui.settings_restore_database_button.text(),"Restore Database") + + def test_spinBox(self): + '''Test the defaults''' + self.assertEqual(settingsTab_widget_ui.concurrent_reports_spin_box.value(),0) + self.assertEqual(settingsTab_widget_ui.concurrent_vendors_spin_box.value(), 0) + self.assertEqual(settingsTab_widget_ui.request_interval_spin_box.value(), 0) + self.assertEqual(settingsTab_widget_ui.request_timeout_spin_box.value(), 0) + + def test_lineEdit(self): + '''Test the defaults''' + self.assertEqual(settingsTab_widget_ui.empty_cell_edit.text(),"") + self.assertEqual(settingsTab_widget_ui.other_directory_edit.text(),"") + self.assertEqual(settingsTab_widget_ui.user_agent_edit.text(),"") + self.assertEqual(settingsTab_widget_ui.yearly_directory_edit.text(),"") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_UpdateDatabaseProgressDialog.py b/tests/test_UpdateDatabaseProgressDialog.py new file mode 100644 index 0000000..d76bb34 --- /dev/null +++ b/tests/test_UpdateDatabaseProgressDialog.py @@ -0,0 +1,30 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QDialog + +from ui import UpdateDatabaseProgressDialog + +app = QApplication(sys.argv) +update_database_progress_dialog = QDialog() +update_database_progress_dialog_ui = UpdateDatabaseProgressDialog.Ui_restore_database_dialog() +update_database_progress_dialog_ui.setupUi(update_database_progress_dialog) + +class UpdateDatabaseProgressDialogTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(update_database_progress_dialog_ui.status_label.text(),"Status") + + def test_buttonBox(self): + '''Test the defaults''' + okWidget = update_database_progress_dialog_ui.buttonBox.Ok + self.assertIsNotNone(okWidget) + cancelWidget = update_database_progress_dialog_ui.buttonBox.Cancel + self.assertIsNotNone(cancelWidget) + + def test_progressBar(self): + '''Test the defaults''' + self.assertEqual(update_database_progress_dialog_ui.progressbar.text(),"0%") + self.assertEqual(update_database_progress_dialog_ui.progressbar.value(), 0) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_VendorResultsWidget.py b/tests/test_VendorResultsWidget.py new file mode 100644 index 0000000..1d68999 --- /dev/null +++ b/tests/test_VendorResultsWidget.py @@ -0,0 +1,24 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import VendorResultsWidget + +app = QApplication(sys.argv) +vendor_results_widget = QWidget() +vendor_results_ui = VendorResultsWidget.Ui_VendorResultsWidget() +vendor_results_ui.setupUi(vendor_results_widget) + +class VendorResultsWidgetTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(vendor_results_ui.status_label.text(),"Success") + self.assertEqual(vendor_results_ui.vendor_label.text(),"Bioone") + + + def test_button(self): + self.assertEqual(vendor_results_ui.collapse_button.text(), "Collapse") + self.assertEqual(vendor_results_ui.expand_button.text(), "Expand") + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_VisualTab.py b/tests/test_VisualTab.py new file mode 100644 index 0000000..156e4bf --- /dev/null +++ b/tests/test_VisualTab.py @@ -0,0 +1,65 @@ +import unittest +import sys +from PyQt5.QtWidgets import QApplication, QWidget + +from ui import VisualTab + +app = QApplication(sys.argv) +visualTab_widget = QWidget() +visualTab_widget_ui = VisualTab.Ui_visual_tab() +visualTab_widget_ui.setupUi(visualTab_widget) + +class VisualTabTests(unittest.TestCase): + def test_defaults(self): + '''Test the defaults''' + self.assertEqual(visualTab_widget_ui.label_46.text(), "IMPORTANT") + self.assertEqual(visualTab_widget_ui.label_47.text(), "Name must correspond to Report:") + self.assertEqual(visualTab_widget_ui.label_48.text(), "Examples : Report is PR, Name must be a Platform") + self.assertEqual(visualTab_widget_ui.label_49.text(), "Report is DR, Name must be a Database") + self.assertEqual(visualTab_widget_ui.label_50.text(), "Report is TR, Name must be a Title") + self.assertEqual(visualTab_widget_ui.label_45.text(), "Select Chart Type") + self.assertEqual(visualTab_widget_ui.label.text(), "Create Chart") + self.assertEqual(visualTab_widget_ui.label_14.text(), "Metric Type * :") + self.assertEqual(visualTab_widget_ui.label_15.text(), "Required fields*") + self.assertEqual(visualTab_widget_ui.label_8.text(), "Name * :") + self.assertEqual(visualTab_widget_ui.label_16.text(), "File Name") + self.assertEqual(visualTab_widget_ui.label_36.text(), "Chart Title") + self.assertEqual(visualTab_widget_ui.label_42.text(), "Customize Chart") + self.assertEqual(visualTab_widget_ui.label_43.text(), "Horizontal Axis Title") + self.assertEqual(visualTab_widget_ui.label_44.text(), "Vertical Axis Title") + self.assertEqual(visualTab_widget_ui.search_end_year_parameter_label_2.text(), "End year") + self.assertEqual(visualTab_widget_ui.search_report_parameter_label_2.text(), "Report") + self.assertEqual(visualTab_widget_ui.search_start_year_parameter_label_2.text(),"Start Year") + + def test_combobox(self): + '''Test the defaults''' + self.assertEqual(visualTab_widget_ui.metric_Type_comboBox.isEditable(),False) + self.assertEqual(visualTab_widget_ui.metric_Type_comboBox.currentText(),"") + self.assertEqual(visualTab_widget_ui.search_report_parameter_combobox_2.isEditable(), False) + self.assertEqual(visualTab_widget_ui.search_report_parameter_combobox_2.currentText(), "") + + def test_radioButrton(self): + '''Test the defaults''' + self.assertEqual(visualTab_widget_ui.radioButton.text(),"Horizontal Bar") + self.assertEqual(visualTab_widget_ui.radioButton_3.text(), "Vertical Bar") + self.assertEqual(visualTab_widget_ui.radioButton_4.text(), "Line") + + def test_lineEdit(self): + '''Test the defaults''' + self.assertEqual(visualTab_widget_ui.name_lineEdit.text(),"") + self.assertEqual(visualTab_widget_ui.chart_title_lineEdit.text(), "") + self.assertEqual(visualTab_widget_ui.file_name_lineEdit.text(), "") + self.assertEqual(visualTab_widget_ui.horizontal_axis_lineEdit.text(), "") + self.assertEqual(visualTab_widget_ui.vertical_axis_lineEdit.text(), "") + + def test_pushButton(self): + '''Test the defaults''' + self.assertEqual(visualTab_widget_ui.create_chart_button.text(),"Create Chart") + + def test_dateEdit(self): + '''Test the defaults''' + self.assertEqual(visualTab_widget_ui.search_end_year_parameter_dateedit_2.text(),"2000") + self.assertEqual(visualTab_widget_ui.search_start_year_parameter_dateedit_2.text(),"2000") + +if __name__ == '__main__': + unittest.main() diff --git a/ui/AddVendorDialog.py b/ui/AddVendorDialog.py new file mode 100644 index 0000000..839b2a3 --- /dev/null +++ b/ui/AddVendorDialog.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'AddVendorDialog.ui' +# +# Created by: PyQt5 UI code generator 5.12.3 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_addVendorDialog(object): + def setupUi(self, addVendorDialog): + addVendorDialog.setObjectName("addVendorDialog") + addVendorDialog.resize(600, 620) + font = QtGui.QFont() + font.setPointSize(11) + addVendorDialog.setFont(font) + self.gridLayout = QtWidgets.QGridLayout(addVendorDialog) + self.gridLayout.setObjectName("gridLayout") + self.buttonBox = QtWidgets.QDialogButtonBox(addVendorDialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.gridLayout.addWidget(self.buttonBox, 1, 0, 1, 1) + self.frame = QtWidgets.QFrame(addVendorDialog) + self.frame.setObjectName("frame") + self.gridLayout_2 = QtWidgets.QGridLayout(self.frame) + self.gridLayout_2.setSpacing(20) + self.gridLayout_2.setObjectName("gridLayout_2") + self.label_7 = QtWidgets.QLabel(self.frame) + self.label_7.setObjectName("label_7") + self.gridLayout_2.addWidget(self.label_7, 9, 0, 1, 1, QtCore.Qt.AlignTop) + self.label_3 = QtWidgets.QLabel(self.frame) + self.label_3.setObjectName("label_3") + self.gridLayout_2.addWidget(self.label_3, 2, 0, 1, 1) + self.label_5 = QtWidgets.QLabel(self.frame) + self.label_5.setObjectName("label_5") + self.gridLayout_2.addWidget(self.label_5, 6, 0, 1, 1) + self.companiesEdit = QtWidgets.QPlainTextEdit(self.frame) + self.companiesEdit.setObjectName("companiesEdit") + self.gridLayout_2.addWidget(self.companiesEdit, 10, 1, 1, 1) + self.name_validation_label = QtWidgets.QLabel(self.frame) + self.name_validation_label.setObjectName("name_validation_label") + self.gridLayout_2.addWidget(self.name_validation_label, 1, 1, 1, 1) + self.label_6 = QtWidgets.QLabel(self.frame) + self.label_6.setObjectName("label_6") + self.gridLayout_2.addWidget(self.label_6, 7, 0, 1, 1) + self.frame_2 = QtWidgets.QFrame(self.frame) + self.frame_2.setObjectName("frame_2") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame_2) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.non_Sushi_check_box = QtWidgets.QCheckBox(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.non_Sushi_check_box.sizePolicy().hasHeightForWidth()) + self.non_Sushi_check_box.setSizePolicy(sizePolicy) + self.non_Sushi_check_box.setText("") + self.non_Sushi_check_box.setObjectName("non_Sushi_check_box") + self.horizontalLayout.addWidget(self.non_Sushi_check_box) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.non_sushi_help_button = QtWidgets.QPushButton(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.non_sushi_help_button.sizePolicy().hasHeightForWidth()) + self.non_sushi_help_button.setSizePolicy(sizePolicy) + self.non_sushi_help_button.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/help_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.non_sushi_help_button.setIcon(icon) + self.non_sushi_help_button.setObjectName("non_sushi_help_button") + self.horizontalLayout.addWidget(self.non_sushi_help_button) + self.gridLayout_2.addWidget(self.frame_2, 8, 1, 1, 1) + self.label_8 = QtWidgets.QLabel(self.frame) + self.label_8.setObjectName("label_8") + self.gridLayout_2.addWidget(self.label_8, 8, 0, 1, 1) + self.label_2 = QtWidgets.QLabel(self.frame) + self.label_2.setObjectName("label_2") + self.gridLayout_2.addWidget(self.label_2, 4, 0, 1, 1) + self.requestorIdEdit = QtWidgets.QLineEdit(self.frame) + self.requestorIdEdit.setObjectName("requestorIdEdit") + self.gridLayout_2.addWidget(self.requestorIdEdit, 5, 1, 1, 1) + self.customerIdEdit = QtWidgets.QLineEdit(self.frame) + self.customerIdEdit.setObjectName("customerIdEdit") + self.gridLayout_2.addWidget(self.customerIdEdit, 4, 1, 1, 1) + self.nameEdit = QtWidgets.QLineEdit(self.frame) + self.nameEdit.setObjectName("nameEdit") + self.gridLayout_2.addWidget(self.nameEdit, 0, 1, 1, 1) + self.label_9 = QtWidgets.QLabel(self.frame) + self.label_9.setObjectName("label_9") + self.gridLayout_2.addWidget(self.label_9, 10, 0, 1, 1, QtCore.Qt.AlignTop) + self.url_validation_label = QtWidgets.QLabel(self.frame) + self.url_validation_label.setObjectName("url_validation_label") + self.gridLayout_2.addWidget(self.url_validation_label, 3, 1, 1, 1) + self.baseUrlEdit = QtWidgets.QLineEdit(self.frame) + self.baseUrlEdit.setObjectName("baseUrlEdit") + self.gridLayout_2.addWidget(self.baseUrlEdit, 2, 1, 1, 1) + self.apiKeyEdit = QtWidgets.QLineEdit(self.frame) + self.apiKeyEdit.setObjectName("apiKeyEdit") + self.gridLayout_2.addWidget(self.apiKeyEdit, 6, 1, 1, 1) + self.descriptionEdit = QtWidgets.QPlainTextEdit(self.frame) + self.descriptionEdit.setObjectName("descriptionEdit") + self.gridLayout_2.addWidget(self.descriptionEdit, 9, 1, 1, 1) + self.label = QtWidgets.QLabel(self.frame) + self.label.setObjectName("label") + self.gridLayout_2.addWidget(self.label, 0, 0, 1, 1) + self.platformEdit = QtWidgets.QLineEdit(self.frame) + self.platformEdit.setObjectName("platformEdit") + self.gridLayout_2.addWidget(self.platformEdit, 7, 1, 1, 1) + self.label_4 = QtWidgets.QLabel(self.frame) + self.label_4.setObjectName("label_4") + self.gridLayout_2.addWidget(self.label_4, 5, 0, 1, 1) + self.gridLayout.addWidget(self.frame, 0, 0, 1, 1) + + self.retranslateUi(addVendorDialog) + QtCore.QMetaObject.connectSlotsByName(addVendorDialog) + addVendorDialog.setTabOrder(self.nameEdit, self.baseUrlEdit) + addVendorDialog.setTabOrder(self.baseUrlEdit, self.customerIdEdit) + addVendorDialog.setTabOrder(self.customerIdEdit, self.requestorIdEdit) + addVendorDialog.setTabOrder(self.requestorIdEdit, self.apiKeyEdit) + addVendorDialog.setTabOrder(self.apiKeyEdit, self.platformEdit) + addVendorDialog.setTabOrder(self.platformEdit, self.non_Sushi_check_box) + addVendorDialog.setTabOrder(self.non_Sushi_check_box, self.non_sushi_help_button) + addVendorDialog.setTabOrder(self.non_sushi_help_button, self.descriptionEdit) + addVendorDialog.setTabOrder(self.descriptionEdit, self.companiesEdit) + + def retranslateUi(self, addVendorDialog): + _translate = QtCore.QCoreApplication.translate + addVendorDialog.setWindowTitle(_translate("addVendorDialog", "Add Vendor")) + self.label_7.setText(_translate("addVendorDialog", "Description")) + self.label_3.setText(_translate("addVendorDialog", "Base URL")) + self.label_5.setText(_translate("addVendorDialog", "API Key")) + self.name_validation_label.setText(_translate("addVendorDialog", "Validation label")) + self.label_6.setText(_translate("addVendorDialog", "Platform")) + self.label_8.setText(_translate("addVendorDialog", "Non-SUSHI Vendor")) + self.label_2.setText(_translate("addVendorDialog", "Customer ID")) + self.label_9.setText(_translate("addVendorDialog", "Third party provider")) + self.url_validation_label.setText(_translate("addVendorDialog", "Validation label")) + self.label.setText(_translate("addVendorDialog", "Name")) + self.label_4.setText(_translate("addVendorDialog", "Requestor ID")) +import Resources_rc diff --git a/ui/AddVendorDialog.ui b/ui/AddVendorDialog.ui new file mode 100644 index 0000000..6bb56cd --- /dev/null +++ b/ui/AddVendorDialog.ui @@ -0,0 +1,221 @@ + + + addVendorDialog + + + + 0 + 0 + 600 + 620 + + + + + 11 + + + + Add Vendor + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + 20 + + + + + Description + + + + + + + Base URL + + + + + + + API Key + + + + + + + + + + Validation label + + + + + + + Platform + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + Non-SUSHI Vendor + + + + + + + Customer ID + + + + + + + + + + + + + + + + Third party provider + + + + + + + Validation label + + + + + + + + + + + + + + + + Name + + + + + + + + + + Requestor ID + + + + + + + + + + nameEdit + baseUrlEdit + customerIdEdit + requestorIdEdit + apiKeyEdit + platformEdit + non_Sushi_check_box + non_sushi_help_button + descriptionEdit + companiesEdit + + + + + + diff --git a/ui/CostsTab.py b/ui/CostsTab.py new file mode 100644 index 0000000..3f0a24c --- /dev/null +++ b/ui/CostsTab.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'CostsTab.ui' +# +# Created by: PyQt5 UI code generator 5.14.1 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_costs_tab(object): + def setupUi(self, costs_tab): + costs_tab.setObjectName("costs_tab") + costs_tab.resize(898, 671) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/costs_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + costs_tab.setWindowIcon(icon) + self.horizontalLayout = QtWidgets.QHBoxLayout(costs_tab) + self.horizontalLayout.setObjectName("horizontalLayout") + spacerItem = QtWidgets.QSpacerItem(120, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.frame = QtWidgets.QFrame(costs_tab) + self.frame.setObjectName("frame") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame) + self.verticalLayout_2.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.costs_parameters_frame = QtWidgets.QFrame(self.frame) + self.costs_parameters_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.costs_parameters_frame.setObjectName("costs_parameters_frame") + self.formLayout_3 = QtWidgets.QFormLayout(self.costs_parameters_frame) + self.formLayout_3.setObjectName("formLayout_3") + self.costs_report_parameter_label = QtWidgets.QLabel(self.costs_parameters_frame) + self.costs_report_parameter_label.setObjectName("costs_report_parameter_label") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.costs_report_parameter_label) + self.costs_report_parameter_combobox = QtWidgets.QComboBox(self.costs_parameters_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.costs_report_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.costs_report_parameter_combobox.setSizePolicy(sizePolicy) + self.costs_report_parameter_combobox.setMinimumSize(QtCore.QSize(300, 0)) + self.costs_report_parameter_combobox.setObjectName("costs_report_parameter_combobox") + self.formLayout_3.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.costs_report_parameter_combobox) + self.costs_vendor_parameter_label = QtWidgets.QLabel(self.costs_parameters_frame) + self.costs_vendor_parameter_label.setObjectName("costs_vendor_parameter_label") + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.costs_vendor_parameter_label) + self.costs_vendor_parameter_combobox = QtWidgets.QComboBox(self.costs_parameters_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.costs_vendor_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.costs_vendor_parameter_combobox.setSizePolicy(sizePolicy) + self.costs_vendor_parameter_combobox.setMinimumSize(QtCore.QSize(300, 0)) + self.costs_vendor_parameter_combobox.setEditable(False) + self.costs_vendor_parameter_combobox.setObjectName("costs_vendor_parameter_combobox") + self.formLayout_3.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.costs_vendor_parameter_combobox) + self.costs_year_parameter_label = QtWidgets.QLabel(self.costs_parameters_frame) + self.costs_year_parameter_label.setObjectName("costs_year_parameter_label") + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.costs_year_parameter_label) + self.costs_year_parameter_dateedit = QtWidgets.QDateEdit(self.costs_parameters_frame) + self.costs_year_parameter_dateedit.setObjectName("costs_year_parameter_dateedit") + self.formLayout_3.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.costs_year_parameter_dateedit) + self.costs_name_parameter_label = QtWidgets.QLabel(self.costs_parameters_frame) + self.costs_name_parameter_label.setObjectName("costs_name_parameter_label") + self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.costs_name_parameter_label) + self.costs_name_parameter_combobox = QtWidgets.QComboBox(self.costs_parameters_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.costs_name_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.costs_name_parameter_combobox.setSizePolicy(sizePolicy) + self.costs_name_parameter_combobox.setMinimumSize(QtCore.QSize(300, 0)) + self.costs_name_parameter_combobox.setEditable(True) + self.costs_name_parameter_combobox.setObjectName("costs_name_parameter_combobox") + self.formLayout_3.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.costs_name_parameter_combobox) + self.verticalLayout_2.addWidget(self.costs_parameters_frame) + self.costs_values_frame = QtWidgets.QFrame(self.frame) + self.costs_values_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.costs_values_frame.setObjectName("costs_values_frame") + self.formLayout = QtWidgets.QFormLayout(self.costs_values_frame) + self.formLayout.setObjectName("formLayout") + self.costs_cost_in_original_currency_label = QtWidgets.QLabel(self.costs_values_frame) + self.costs_cost_in_original_currency_label.setObjectName("costs_cost_in_original_currency_label") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.LabelRole, self.costs_cost_in_original_currency_label) + self.costs_cost_in_original_currency_doublespinbox = QtWidgets.QDoubleSpinBox(self.costs_values_frame) + self.costs_cost_in_original_currency_doublespinbox.setEnabled(False) + self.costs_cost_in_original_currency_doublespinbox.setMaximum(999999999.99) + self.costs_cost_in_original_currency_doublespinbox.setObjectName("costs_cost_in_original_currency_doublespinbox") + self.formLayout.setWidget(0, QtWidgets.QFormLayout.FieldRole, self.costs_cost_in_original_currency_doublespinbox) + self.costs_original_currency_label = QtWidgets.QLabel(self.costs_values_frame) + self.costs_original_currency_label.setObjectName("costs_original_currency_label") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.LabelRole, self.costs_original_currency_label) + self.costs_original_currency_value_combobox = QtWidgets.QComboBox(self.costs_values_frame) + self.costs_original_currency_value_combobox.setEnabled(False) + self.costs_original_currency_value_combobox.setEditable(True) + self.costs_original_currency_value_combobox.setObjectName("costs_original_currency_value_combobox") + self.formLayout.setWidget(1, QtWidgets.QFormLayout.FieldRole, self.costs_original_currency_value_combobox) + self.costs_cost_in_local_currency_label = QtWidgets.QLabel(self.costs_values_frame) + self.costs_cost_in_local_currency_label.setObjectName("costs_cost_in_local_currency_label") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.LabelRole, self.costs_cost_in_local_currency_label) + self.costs_cost_in_local_currency_doublespinbox = QtWidgets.QDoubleSpinBox(self.costs_values_frame) + self.costs_cost_in_local_currency_doublespinbox.setEnabled(False) + self.costs_cost_in_local_currency_doublespinbox.setMaximum(999999999.99) + self.costs_cost_in_local_currency_doublespinbox.setObjectName("costs_cost_in_local_currency_doublespinbox") + self.formLayout.setWidget(2, QtWidgets.QFormLayout.FieldRole, self.costs_cost_in_local_currency_doublespinbox) + self.costs_cost_in_local_currency_with_tax_label = QtWidgets.QLabel(self.costs_values_frame) + self.costs_cost_in_local_currency_with_tax_label.setObjectName("costs_cost_in_local_currency_with_tax_label") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.LabelRole, self.costs_cost_in_local_currency_with_tax_label) + self.costs_cost_in_local_currency_with_tax_doublespinbox = QtWidgets.QDoubleSpinBox(self.costs_values_frame) + self.costs_cost_in_local_currency_with_tax_doublespinbox.setEnabled(False) + self.costs_cost_in_local_currency_with_tax_doublespinbox.setMaximum(999999999.99) + self.costs_cost_in_local_currency_with_tax_doublespinbox.setObjectName("costs_cost_in_local_currency_with_tax_doublespinbox") + self.formLayout.setWidget(3, QtWidgets.QFormLayout.FieldRole, self.costs_cost_in_local_currency_with_tax_doublespinbox) + self.verticalLayout_2.addWidget(self.costs_values_frame) + self.costs_control_frame = QtWidgets.QFrame(self.frame) + self.costs_control_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.costs_control_frame.setObjectName("costs_control_frame") + self.gridLayout = QtWidgets.QGridLayout(self.costs_control_frame) + self.gridLayout.setObjectName("gridLayout") + self.costs_save_button = QtWidgets.QPushButton(self.costs_control_frame) + self.costs_save_button.setObjectName("costs_save_button") + self.gridLayout.addWidget(self.costs_save_button, 0, 0, 1, 1) + self.costs_clear_button = QtWidgets.QPushButton(self.costs_control_frame) + self.costs_clear_button.setObjectName("costs_clear_button") + self.gridLayout.addWidget(self.costs_clear_button, 0, 2, 1, 1) + self.costs_load_button = QtWidgets.QPushButton(self.costs_control_frame) + self.costs_load_button.setObjectName("costs_load_button") + self.gridLayout.addWidget(self.costs_load_button, 0, 4, 1, 1) + self.costs_import_costs_button = QtWidgets.QPushButton(self.costs_control_frame) + self.costs_import_costs_button.setObjectName("costs_import_costs_button") + self.gridLayout.addWidget(self.costs_import_costs_button, 0, 5, 1, 1) + self.verticalLayout_2.addWidget(self.costs_control_frame) + self.horizontalLayout.addWidget(self.frame) + spacerItem1 = QtWidgets.QSpacerItem(120, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem1) + + self.retranslateUi(costs_tab) + QtCore.QMetaObject.connectSlotsByName(costs_tab) + + def retranslateUi(self, costs_tab): + _translate = QtCore.QCoreApplication.translate + costs_tab.setWindowTitle(_translate("costs_tab", "Costs")) + self.costs_report_parameter_label.setText(_translate("costs_tab", "Report")) + self.costs_vendor_parameter_label.setText(_translate("costs_tab", "Vendor")) + self.costs_year_parameter_label.setText(_translate("costs_tab", "Year")) + self.costs_year_parameter_dateedit.setDisplayFormat(_translate("costs_tab", "yyyy")) + self.costs_name_parameter_label.setText(_translate("costs_tab", "Name")) + self.costs_cost_in_original_currency_label.setText(_translate("costs_tab", "Cost in Original Currency")) + self.costs_original_currency_label.setText(_translate("costs_tab", "Original Currency")) + self.costs_cost_in_local_currency_label.setText(_translate("costs_tab", "Cost in Local Currency")) + self.costs_cost_in_local_currency_with_tax_label.setText(_translate("costs_tab", "Cost in Local Currency with Tax")) + self.costs_save_button.setText(_translate("costs_tab", "Save Costs")) + self.costs_clear_button.setText(_translate("costs_tab", "Clear")) + self.costs_load_button.setText(_translate("costs_tab", "Refresh Costs")) + self.costs_import_costs_button.setText(_translate("costs_tab", "Import Costs File")) +import Resources_rc diff --git a/ui/CostsTab.ui b/ui/CostsTab.ui new file mode 100644 index 0000000..b375ced --- /dev/null +++ b/ui/CostsTab.ui @@ -0,0 +1,289 @@ + + + costs_tab + + + + 0 + 0 + 898 + 671 + + + + Costs + + + + :/ui/resources/tab_icons/costs_icon.png:/ui/resources/tab_icons/costs_icon.png + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 120 + 20 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + + + + Report + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + + + + + Vendor + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + false + + + + + + + Year + + + + + + + yyyy + + + + + + + Name + + + + + + + + 0 + 0 + + + + + 300 + 0 + + + + true + + + + + + + + + + QFrame::StyledPanel + + + + + + Cost in Original Currency + + + + + + + false + + + 999999999.990000009536743 + + + + + + + Original Currency + + + + + + + false + + + true + + + + + + + Cost in Local Currency + + + + + + + false + + + 999999999.990000009536743 + + + + + + + Cost in Local Currency with Tax + + + + + + + false + + + 999999999.990000009536743 + + + + + + + + + + QFrame::StyledPanel + + + + + + Save Costs + + + + + + + Clear + + + + + + + Refresh Costs + + + + + + + Import Costs File + + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 120 + 20 + + + + + + + + + + + diff --git a/ui/DisclaimerDialog.py b/ui/DisclaimerDialog.py new file mode 100644 index 0000000..c85c50d --- /dev/null +++ b/ui/DisclaimerDialog.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'DisclaimerDialog.ui' +# +# Created by: PyQt5 UI code generator 5.14.1 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_dialog(object): + def setupUi(self, dialog): + dialog.setObjectName("dialog") + dialog.resize(403, 80) + self.buttonBox = QtWidgets.QDialogButtonBox(dialog) + self.buttonBox.setGeometry(QtCore.QRect(40, 50, 341, 32)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.frame = QtWidgets.QFrame(dialog) + self.frame.setGeometry(QtCore.QRect(10, 10, 381, 41)) + self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame.setObjectName("frame") + self.label = QtWidgets.QLabel(self.frame) + self.label.setGeometry(QtCore.QRect(10, 0, 371, 41)) + self.label.setObjectName("label") + + self.retranslateUi(dialog) + self.buttonBox.accepted.connect(dialog.accept) + self.buttonBox.rejected.connect(dialog.reject) + QtCore.QMetaObject.connectSlotsByName(dialog) + + def retranslateUi(self, dialog): + _translate = QtCore.QCoreApplication.translate + dialog.setWindowTitle(_translate("dialog", "Disclaimer")) + self.label.setText(_translate("dialog", "Only reports supported by selected vendor will be retrieved!")) diff --git a/ui/DisclaimerDialog.ui b/ui/DisclaimerDialog.ui new file mode 100644 index 0000000..cd077e6 --- /dev/null +++ b/ui/DisclaimerDialog.ui @@ -0,0 +1,97 @@ + + + dialog + + + + 0 + 0 + 403 + 80 + + + + Disclaimer + + + + + 40 + 50 + 341 + 32 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + 10 + 10 + 381 + 41 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 0 + 371 + 41 + + + + Only reports supported by selected vendor will be retrieved! + + + + + + + + buttonBox + accepted() + dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/FetchProgressDialog.py b/ui/FetchProgressDialog.py new file mode 100644 index 0000000..d5fd929 --- /dev/null +++ b/ui/FetchProgressDialog.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'FetchProgressDialog.ui' +# +# Created by: PyQt5 UI code generator 5.12.3 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_FetchProgressDialog(object): + def setupUi(self, FetchProgressDialog): + FetchProgressDialog.setObjectName("FetchProgressDialog") + FetchProgressDialog.resize(800, 600) + FetchProgressDialog.setMinimumSize(QtCore.QSize(680, 0)) + font = QtGui.QFont() + font.setPointSize(9) + FetchProgressDialog.setFont(font) + self.verticalLayout = QtWidgets.QVBoxLayout(FetchProgressDialog) + self.verticalLayout.setObjectName("verticalLayout") + self.fetching_frame = QtWidgets.QFrame(FetchProgressDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fetching_frame.sizePolicy().hasHeightForWidth()) + self.fetching_frame.setSizePolicy(sizePolicy) + self.fetching_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.fetching_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.fetching_frame.setObjectName("fetching_frame") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.fetching_frame) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.status_label = QtWidgets.QLabel(self.fetching_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth()) + self.status_label.setSizePolicy(sizePolicy) + self.status_label.setAlignment(QtCore.Qt.AlignCenter) + self.status_label.setObjectName("status_label") + self.verticalLayout_2.addWidget(self.status_label) + self.progress_bar = QtWidgets.QProgressBar(self.fetching_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.progress_bar.sizePolicy().hasHeightForWidth()) + self.progress_bar.setSizePolicy(sizePolicy) + self.progress_bar.setProperty("value", 24) + self.progress_bar.setObjectName("progress_bar") + self.verticalLayout_2.addWidget(self.progress_bar) + self.verticalLayout.addWidget(self.fetching_frame) + self.results_scroll_area = QtWidgets.QScrollArea(FetchProgressDialog) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.results_scroll_area.sizePolicy().hasHeightForWidth()) + self.results_scroll_area.setSizePolicy(sizePolicy) + self.results_scroll_area.setWidgetResizable(True) + self.results_scroll_area.setObjectName("results_scroll_area") + self.scroll_area_widget_contents = QtWidgets.QWidget() + self.scroll_area_widget_contents.setGeometry(QtCore.QRect(0, 0, 780, 480)) + self.scroll_area_widget_contents.setObjectName("scroll_area_widget_contents") + self.scroll_area_vertical_layout = QtWidgets.QVBoxLayout(self.scroll_area_widget_contents) + self.scroll_area_vertical_layout.setObjectName("scroll_area_vertical_layout") + self.results_scroll_area.setWidget(self.scroll_area_widget_contents) + self.verticalLayout.addWidget(self.results_scroll_area) + self.buttonBox = QtWidgets.QDialogButtonBox(FetchProgressDialog) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok|QtWidgets.QDialogButtonBox.Retry) + self.buttonBox.setCenterButtons(True) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(FetchProgressDialog) + QtCore.QMetaObject.connectSlotsByName(FetchProgressDialog) + + def retranslateUi(self, FetchProgressDialog): + _translate = QtCore.QCoreApplication.translate + FetchProgressDialog.setWindowTitle(_translate("FetchProgressDialog", "Fetch Progress")) + self.status_label.setText(_translate("FetchProgressDialog", "Fetching...")) diff --git a/ui/FetchProgressDialog.ui b/ui/FetchProgressDialog.ui new file mode 100644 index 0000000..8635f6b --- /dev/null +++ b/ui/FetchProgressDialog.ui @@ -0,0 +1,113 @@ + + + FetchProgressDialog + + + + 0 + 0 + 800 + 600 + + + + + 680 + 0 + + + + + 9 + + + + Fetch Progress + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + Fetching... + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + 24 + + + + + + + + + + + 0 + 0 + + + + true + + + + + 0 + 0 + 780 + 480 + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry + + + true + + + + + + + + diff --git a/ui/FetchProgressDialog_bak.ui b/ui/FetchProgressDialog_bak.ui new file mode 100644 index 0000000..934712c --- /dev/null +++ b/ui/FetchProgressDialog_bak.ui @@ -0,0 +1,113 @@ + + + FetchProgressDialog + + + + 0 + 0 + 680 + 353 + + + + + 680 + 0 + + + + + 9 + + + + Fetch Progress + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + Fetching... + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + 24 + + + + + + + + + + + 0 + 0 + + + + true + + + + + 0 + 0 + 660 + 236 + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Retry + + + true + + + + + + + + diff --git a/ui/FetchReportsTab.py b/ui/FetchReportsTab.py new file mode 100644 index 0000000..258c3c9 --- /dev/null +++ b/ui/FetchReportsTab.py @@ -0,0 +1,422 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'FetchReportsTab.ui' +# +# Created by: PyQt5 UI code generator 5.12.3 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_fetch_reports_tab(object): + def setupUi(self, fetch_reports_tab): + fetch_reports_tab.setObjectName("fetch_reports_tab") + fetch_reports_tab.resize(1014, 746) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(fetch_reports_tab.sizePolicy().hasHeightForWidth()) + fetch_reports_tab.setSizePolicy(sizePolicy) + fetch_reports_tab.setMinimumSize(QtCore.QSize(800, 0)) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/fetch_reports_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + fetch_reports_tab.setWindowIcon(icon) + self.verticalLayout = QtWidgets.QVBoxLayout(fetch_reports_tab) + self.verticalLayout.setObjectName("verticalLayout") + self.frame_4 = QtWidgets.QFrame(fetch_reports_tab) + self.frame_4.setMouseTracking(True) + self.frame_4.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_4.setObjectName("frame_4") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.frame_4) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.label_35 = QtWidgets.QLabel(self.frame_4) + self.label_35.setMinimumSize(QtCore.QSize(15, 15)) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(16) + font.setBold(True) + font.setWeight(75) + self.label_35.setFont(font) + self.label_35.setAlignment(QtCore.Qt.AlignCenter) + self.label_35.setObjectName("label_35") + self.verticalLayout_3.addWidget(self.label_35) + self.frame_9 = QtWidgets.QFrame(self.frame_4) + self.frame_9.setMinimumSize(QtCore.QSize(0, 60)) + self.frame_9.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_9.setObjectName("frame_9") + self.horizontalLayout_10 = QtWidgets.QHBoxLayout(self.frame_9) + self.horizontalLayout_10.setObjectName("horizontalLayout_10") + self.frame_28 = QtWidgets.QFrame(self.frame_9) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_28.sizePolicy().hasHeightForWidth()) + self.frame_28.setSizePolicy(sizePolicy) + self.frame_28.setObjectName("frame_28") + self.horizontalLayout_12 = QtWidgets.QHBoxLayout(self.frame_28) + self.horizontalLayout_12.setSpacing(10) + self.horizontalLayout_12.setObjectName("horizontalLayout_12") + self.label_34 = QtWidgets.QLabel(self.frame_28) + self.label_34.setObjectName("label_34") + self.horizontalLayout_12.addWidget(self.label_34) + self.All_reports_edit_fetch = QtWidgets.QDateEdit(self.frame_28) + self.All_reports_edit_fetch.setDateTime(QtCore.QDateTime(QtCore.QDate(2020, 1, 1), QtCore.QTime(0, 0, 0))) + self.All_reports_edit_fetch.setObjectName("All_reports_edit_fetch") + self.horizontalLayout_12.addWidget(self.All_reports_edit_fetch) + self.horizontalLayout_10.addWidget(self.frame_28) + self.fetch_all_data_button = QtWidgets.QPushButton(self.frame_9) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fetch_all_data_button.sizePolicy().hasHeightForWidth()) + self.fetch_all_data_button.setSizePolicy(sizePolicy) + self.fetch_all_data_button.setMaximumSize(QtCore.QSize(300, 16777215)) + self.fetch_all_data_button.setObjectName("fetch_all_data_button") + self.horizontalLayout_10.addWidget(self.fetch_all_data_button) + self.verticalLayout_3.addWidget(self.frame_9) + spacerItem = QtWidgets.QSpacerItem(20, 45, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Minimum) + self.verticalLayout_3.addItem(spacerItem) + self.Adv_Fetch_text = QtWidgets.QLabel(self.frame_4) + self.Adv_Fetch_text.setMaximumSize(QtCore.QSize(16777215, 20)) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(16) + font.setBold(True) + font.setItalic(False) + font.setUnderline(False) + font.setWeight(75) + self.Adv_Fetch_text.setFont(font) + self.Adv_Fetch_text.setAlignment(QtCore.Qt.AlignCenter) + self.Adv_Fetch_text.setObjectName("Adv_Fetch_text") + self.verticalLayout_3.addWidget(self.Adv_Fetch_text) + self.frame_5 = QtWidgets.QFrame(self.frame_4) + self.frame_5.setObjectName("frame_5") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_5) + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.frame_10 = QtWidgets.QFrame(self.frame_5) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_10.sizePolicy().hasHeightForWidth()) + self.frame_10.setSizePolicy(sizePolicy) + self.frame_10.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_10.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_10.setObjectName("frame_10") + self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.frame_10) + self.verticalLayout_4.setObjectName("verticalLayout_4") + self.label_11 = QtWidgets.QLabel(self.frame_10) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_11.setFont(font) + self.label_11.setObjectName("label_11") + self.verticalLayout_4.addWidget(self.label_11) + self.horizontalFrame = QtWidgets.QFrame(self.frame_10) + self.horizontalFrame.setObjectName("horizontalFrame") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.horizontalFrame) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.select_vendors_button_fetch = QtWidgets.QPushButton(self.horizontalFrame) + self.select_vendors_button_fetch.setObjectName("select_vendors_button_fetch") + self.horizontalLayout_4.addWidget(self.select_vendors_button_fetch) + self.deselect_vendors_button_fetch = QtWidgets.QPushButton(self.horizontalFrame) + self.deselect_vendors_button_fetch.setObjectName("deselect_vendors_button_fetch") + self.horizontalLayout_4.addWidget(self.deselect_vendors_button_fetch) + self.verticalLayout_4.addWidget(self.horizontalFrame) + self.vendors_list_view_fetch = QtWidgets.QListView(self.frame_10) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + self.vendors_list_view_fetch.setFont(font) + self.vendors_list_view_fetch.setAlternatingRowColors(True) + self.vendors_list_view_fetch.setObjectName("vendors_list_view_fetch") + self.verticalLayout_4.addWidget(self.vendors_list_view_fetch) + self.horizontalLayout_3.addWidget(self.frame_10) + self.frame_11 = QtWidgets.QFrame(self.frame_5) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_11.sizePolicy().hasHeightForWidth()) + self.frame_11.setSizePolicy(sizePolicy) + self.frame_11.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_11.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_11.setObjectName("frame_11") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame_11) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.label_12 = QtWidgets.QLabel(self.frame_11) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_12.setFont(font) + self.label_12.setObjectName("label_12") + self.verticalLayout_5.addWidget(self.label_12) + self.horizontalFrame_2 = QtWidgets.QFrame(self.frame_11) + self.horizontalFrame_2.setObjectName("horizontalFrame_2") + self.horizontalLayout_6 = QtWidgets.QHBoxLayout(self.horizontalFrame_2) + self.horizontalLayout_6.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_6.setObjectName("horizontalLayout_6") + self.select_report_types_button_fetch = QtWidgets.QPushButton(self.horizontalFrame_2) + self.select_report_types_button_fetch.setObjectName("select_report_types_button_fetch") + self.horizontalLayout_6.addWidget(self.select_report_types_button_fetch) + self.deselect_report_types_button_fetch = QtWidgets.QPushButton(self.horizontalFrame_2) + self.deselect_report_types_button_fetch.setObjectName("deselect_report_types_button_fetch") + self.horizontalLayout_6.addWidget(self.deselect_report_types_button_fetch) + self.report_types_help_button = QtWidgets.QPushButton(self.horizontalFrame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.report_types_help_button.sizePolicy().hasHeightForWidth()) + self.report_types_help_button.setSizePolicy(sizePolicy) + self.report_types_help_button.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/ui/resources/help_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.report_types_help_button.setIcon(icon1) + self.report_types_help_button.setObjectName("report_types_help_button") + self.horizontalLayout_6.addWidget(self.report_types_help_button) + self.verticalLayout_5.addWidget(self.horizontalFrame_2) + self.report_types_list_view = QtWidgets.QListView(self.frame_11) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + self.report_types_list_view.setFont(font) + self.report_types_list_view.setAlternatingRowColors(True) + self.report_types_list_view.setObjectName("report_types_list_view") + self.verticalLayout_5.addWidget(self.report_types_list_view) + self.horizontalLayout_3.addWidget(self.frame_11) + self.frame_7 = QtWidgets.QFrame(self.frame_5) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_7.sizePolicy().hasHeightForWidth()) + self.frame_7.setSizePolicy(sizePolicy) + self.frame_7.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_7.setObjectName("frame_7") + self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.frame_7) + self.verticalLayout_7.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_7.setObjectName("verticalLayout_7") + self.frame_14 = QtWidgets.QFrame(self.frame_7) + self.frame_14.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_14.setObjectName("frame_14") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame_14) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label_8 = QtWidgets.QLabel(self.frame_14) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_8.sizePolicy().hasHeightForWidth()) + self.label_8.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_8.setFont(font) + self.label_8.setObjectName("label_8") + self.verticalLayout_2.addWidget(self.label_8) + self.frame_6 = QtWidgets.QFrame(self.frame_14) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_6.sizePolicy().hasHeightForWidth()) + self.frame_6.setSizePolicy(sizePolicy) + self.frame_6.setMinimumSize(QtCore.QSize(0, 0)) + self.frame_6.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_6.setObjectName("frame_6") + self.gridLayout = QtWidgets.QGridLayout(self.frame_6) + self.gridLayout.setSpacing(20) + self.gridLayout.setObjectName("gridLayout") + self.end_date_edit_fetch_year = QtWidgets.QDateEdit(self.frame_6) + self.end_date_edit_fetch_year.setDateTime(QtCore.QDateTime(QtCore.QDate(2020, 1, 1), QtCore.QTime(0, 0, 0))) + self.end_date_edit_fetch_year.setObjectName("end_date_edit_fetch_year") + self.gridLayout.addWidget(self.end_date_edit_fetch_year, 3, 1, 1, 1) + self.begin_month_combo_box = QtWidgets.QComboBox(self.frame_6) + self.begin_month_combo_box.setObjectName("begin_month_combo_box") + self.gridLayout.addWidget(self.begin_month_combo_box, 0, 2, 1, 1) + self.label_9 = QtWidgets.QLabel(self.frame_6) + self.label_9.setObjectName("label_9") + self.gridLayout.addWidget(self.label_9, 0, 0, 1, 1) + self.end_month_combo_box = QtWidgets.QComboBox(self.frame_6) + self.end_month_combo_box.setObjectName("end_month_combo_box") + self.gridLayout.addWidget(self.end_month_combo_box, 3, 2, 1, 1) + self.begin_date_edit_fetch_year = QtWidgets.QDateEdit(self.frame_6) + self.begin_date_edit_fetch_year.setDate(QtCore.QDate(2020, 1, 1)) + self.begin_date_edit_fetch_year.setObjectName("begin_date_edit_fetch_year") + self.gridLayout.addWidget(self.begin_date_edit_fetch_year, 0, 1, 1, 1) + self.label_10 = QtWidgets.QLabel(self.frame_6) + self.label_10.setObjectName("label_10") + self.gridLayout.addWidget(self.label_10, 3, 0, 1, 1) + self.verticalLayout_2.addWidget(self.frame_6) + self.custom_dir_frame = QtWidgets.QFrame(self.frame_14) + self.custom_dir_frame.setMinimumSize(QtCore.QSize(0, 0)) + self.custom_dir_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.custom_dir_frame.setObjectName("custom_dir_frame") + self.verticalLayout_26 = QtWidgets.QVBoxLayout(self.custom_dir_frame) + self.verticalLayout_26.setObjectName("verticalLayout_26") + self.frame = QtWidgets.QFrame(self.custom_dir_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.frame.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame.setFrameShadow(QtWidgets.QFrame.Plain) + self.frame.setObjectName("frame") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setSpacing(0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_38 = QtWidgets.QLabel(self.frame) + self.label_38.setAlignment(QtCore.Qt.AlignCenter) + self.label_38.setObjectName("label_38") + self.horizontalLayout.addWidget(self.label_38) + self.date_range_help_button = QtWidgets.QPushButton(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.date_range_help_button.sizePolicy().hasHeightForWidth()) + self.date_range_help_button.setSizePolicy(sizePolicy) + self.date_range_help_button.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.date_range_help_button.setText("") + self.date_range_help_button.setIcon(icon1) + self.date_range_help_button.setObjectName("date_range_help_button") + self.horizontalLayout.addWidget(self.date_range_help_button) + self.verticalLayout_26.addWidget(self.frame) + self.frame_2 = QtWidgets.QFrame(self.custom_dir_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) + self.frame_2.setSizePolicy(sizePolicy) + self.frame_2.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame_2.setFrameShadow(QtWidgets.QFrame.Plain) + self.frame_2.setObjectName("frame_2") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setSpacing(0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.label = QtWidgets.QLabel(self.frame_2) + self.label.setObjectName("label") + self.horizontalLayout_2.addWidget(self.label) + self.date_range_help_button2 = QtWidgets.QPushButton(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.date_range_help_button2.sizePolicy().hasHeightForWidth()) + self.date_range_help_button2.setSizePolicy(sizePolicy) + self.date_range_help_button2.setText("") + self.date_range_help_button2.setIcon(icon1) + self.date_range_help_button2.setObjectName("date_range_help_button2") + self.horizontalLayout_2.addWidget(self.date_range_help_button2) + self.verticalLayout_26.addWidget(self.frame_2) + self.frame_3 = QtWidgets.QFrame(self.custom_dir_frame) + self.frame_3.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame_3.setFrameShadow(QtWidgets.QFrame.Plain) + self.frame_3.setObjectName("frame_3") + self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.frame_3) + self.horizontalLayout_5.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_5.setSpacing(0) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.label_41 = QtWidgets.QLabel(self.frame_3) + self.label_41.setAlignment(QtCore.Qt.AlignCenter) + self.label_41.setObjectName("label_41") + self.horizontalLayout_5.addWidget(self.label_41) + self.date_range_help_button3 = QtWidgets.QPushButton(self.frame_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.date_range_help_button3.sizePolicy().hasHeightForWidth()) + self.date_range_help_button3.setSizePolicy(sizePolicy) + self.date_range_help_button3.setText("") + self.date_range_help_button3.setIcon(icon1) + self.date_range_help_button3.setObjectName("date_range_help_button3") + self.horizontalLayout_5.addWidget(self.date_range_help_button3) + self.verticalLayout_26.addWidget(self.frame_3) + self.frame_39 = QtWidgets.QFrame(self.custom_dir_frame) + self.frame_39.setObjectName("frame_39") + self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.frame_39) + self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.custom_dir_edit = QtWidgets.QLineEdit(self.frame_39) + self.custom_dir_edit.setObjectName("custom_dir_edit") + self.horizontalLayout_7.addWidget(self.custom_dir_edit) + self.custom_dir_button = QtWidgets.QPushButton(self.frame_39) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.custom_dir_button.sizePolicy().hasHeightForWidth()) + self.custom_dir_button.setSizePolicy(sizePolicy) + self.custom_dir_button.setObjectName("custom_dir_button") + self.horizontalLayout_7.addWidget(self.custom_dir_button) + self.verticalLayout_26.addWidget(self.frame_39) + self.verticalLayout_2.addWidget(self.custom_dir_frame) + self.verticalLayout_7.addWidget(self.frame_14) + self.frame_13 = QtWidgets.QFrame(self.frame_7) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_13.sizePolicy().hasHeightForWidth()) + self.frame_13.setSizePolicy(sizePolicy) + self.frame_13.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_13.setObjectName("frame_13") + self.gridLayout_2 = QtWidgets.QGridLayout(self.frame_13) + self.gridLayout_2.setObjectName("gridLayout_2") + self.fetch_advanced_button = QtWidgets.QPushButton(self.frame_13) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fetch_advanced_button.sizePolicy().hasHeightForWidth()) + self.fetch_advanced_button.setSizePolicy(sizePolicy) + self.fetch_advanced_button.setObjectName("fetch_advanced_button") + self.gridLayout_2.addWidget(self.fetch_advanced_button, 1, 1, 1, 1) + self.verticalLayout_7.addWidget(self.frame_13) + self.horizontalLayout_3.addWidget(self.frame_7) + self.verticalLayout_3.addWidget(self.frame_5) + self.verticalLayout.addWidget(self.frame_4) + self.label_2 = QtWidgets.QLabel(fetch_reports_tab) + self.label_2.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_2.setObjectName("label_2") + self.verticalLayout.addWidget(self.label_2) + + self.retranslateUi(fetch_reports_tab) + QtCore.QMetaObject.connectSlotsByName(fetch_reports_tab) + fetch_reports_tab.setTabOrder(self.All_reports_edit_fetch, self.fetch_all_data_button) + fetch_reports_tab.setTabOrder(self.fetch_all_data_button, self.select_vendors_button_fetch) + fetch_reports_tab.setTabOrder(self.select_vendors_button_fetch, self.deselect_vendors_button_fetch) + fetch_reports_tab.setTabOrder(self.deselect_vendors_button_fetch, self.vendors_list_view_fetch) + fetch_reports_tab.setTabOrder(self.vendors_list_view_fetch, self.select_report_types_button_fetch) + fetch_reports_tab.setTabOrder(self.select_report_types_button_fetch, self.deselect_report_types_button_fetch) + fetch_reports_tab.setTabOrder(self.deselect_report_types_button_fetch, self.report_types_help_button) + fetch_reports_tab.setTabOrder(self.report_types_help_button, self.report_types_list_view) + fetch_reports_tab.setTabOrder(self.report_types_list_view, self.end_date_edit_fetch_year) + fetch_reports_tab.setTabOrder(self.end_date_edit_fetch_year, self.custom_dir_edit) + fetch_reports_tab.setTabOrder(self.custom_dir_edit, self.custom_dir_button) + fetch_reports_tab.setTabOrder(self.custom_dir_button, self.fetch_advanced_button) + + def retranslateUi(self, fetch_reports_tab): + _translate = QtCore.QCoreApplication.translate + fetch_reports_tab.setWindowTitle(_translate("fetch_reports_tab", "Fetch Report")) + self.label_35.setText(_translate("fetch_reports_tab", "Fetch All Reports")) + self.label_34.setText(_translate("fetch_reports_tab", "Year")) + self.All_reports_edit_fetch.setDisplayFormat(_translate("fetch_reports_tab", "yyyy")) + self.fetch_all_data_button.setText(_translate("fetch_reports_tab", "Fetch All Reports")) + self.Adv_Fetch_text.setText(_translate("fetch_reports_tab", "Advanced Fetch Reports")) + self.label_11.setText(_translate("fetch_reports_tab", "Select Vendors")) + self.select_vendors_button_fetch.setText(_translate("fetch_reports_tab", "Select All")) + self.deselect_vendors_button_fetch.setText(_translate("fetch_reports_tab", "Deselect All")) + self.label_12.setText(_translate("fetch_reports_tab", "Select Report Types")) + self.select_report_types_button_fetch.setText(_translate("fetch_reports_tab", "Select All")) + self.deselect_report_types_button_fetch.setText(_translate("fetch_reports_tab", "Deselect All")) + self.label_8.setText(_translate("fetch_reports_tab", "Date Range")) + self.end_date_edit_fetch_year.setDisplayFormat(_translate("fetch_reports_tab", "yyyy")) + self.label_9.setText(_translate("fetch_reports_tab", "Begin Date")) + self.begin_date_edit_fetch_year.setDisplayFormat(_translate("fetch_reports_tab", "yyyy")) + self.label_10.setText(_translate("fetch_reports_tab", "End Date")) + self.label_38.setText(_translate("fetch_reports_tab", "Date range is not a calendar year")) + self.label.setText(_translate("fetch_reports_tab", "The date range contains months with no available data")) + self.label_41.setText(_translate("fetch_reports_tab", "Report(s) will be saved to:")) + self.custom_dir_button.setText(_translate("fetch_reports_tab", "Change")) + self.fetch_advanced_button.setText(_translate("fetch_reports_tab", "Fetch Selected Reports")) + self.label_2.setText(_translate("fetch_reports_tab", "Note: Yearly reports cover all available data for one calender year. Only yearly reports are added to the search database.")) +import Resources_rc diff --git a/ui/FetchReportsTab.ui b/ui/FetchReportsTab.ui new file mode 100644 index 0000000..8ef0722 --- /dev/null +++ b/ui/FetchReportsTab.ui @@ -0,0 +1,801 @@ + + + fetch_reports_tab + + + + 0 + 0 + 1014 + 746 + + + + + 0 + 0 + + + + + 800 + 0 + + + + Fetch Report + + + + :/ui/resources/tab_icons/fetch_reports_icon.png:/ui/resources/tab_icons/fetch_reports_icon.png + + + + + + true + + + QFrame::StyledPanel + + + + + + + 15 + 15 + + + + + Segoe UI + 16 + 75 + true + + + + Fetch All Reports + + + Qt::AlignCenter + + + + + + + + 0 + 60 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 10 + + + + + Year + + + + + + + + 0 + 0 + 0 + 2020 + 1 + 1 + + + + yyyy + + + + + + + + + + + 0 + 0 + + + + + 300 + 16777215 + + + + Fetch All Reports + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Minimum + + + + 20 + 45 + + + + + + + + + 16777215 + 20 + + + + + Segoe UI + 16 + 75 + false + true + false + + + + Advanced Fetch Reports + + + Qt::AlignCenter + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QFrame::StyledPanel + + + + + + + 75 + true + + + + Select Vendors + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select All + + + + + + + Deselect All + + + + + + + + + + + Segoe UI + 11 + + + + true + + + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QFrame::StyledPanel + + + + + + + 75 + true + + + + Select Report Types + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select All + + + + + + + Deselect All + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + + Segoe UI + 11 + + + + true + + + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 75 + true + + + + Date Range + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + 20 + + + + + + 0 + 0 + 0 + 2020 + 1 + 1 + + + + yyyy + + + + + + + + + + Begin Date + + + + + + + + + + yyyy + + + + 2020 + 1 + 1 + + + + + + + + End Date + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Date range is not a calendar year + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + The date range contains months with no available data + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Report(s) will be saved to: + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + 0 + + + + Change + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + Fetch Selected Reports + + + + + + + + + + + + + + + + + + + Note: Yearly reports cover all available data for one calender year. Only yearly reports are added to the search database. + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + All_reports_edit_fetch + fetch_all_data_button + select_vendors_button_fetch + deselect_vendors_button_fetch + vendors_list_view_fetch + select_report_types_button_fetch + deselect_report_types_button_fetch + report_types_help_button + report_types_list_view + end_date_edit_fetch_year + custom_dir_edit + custom_dir_button + fetch_advanced_button + + + + + + diff --git a/ui/FetchSpecialReportsTab.py b/ui/FetchSpecialReportsTab.py new file mode 100644 index 0000000..4063c59 --- /dev/null +++ b/ui/FetchSpecialReportsTab.py @@ -0,0 +1,360 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'FetchSpecialReportsTab.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_fetch_special_reports_tab(object): + def setupUi(self, fetch_special_reports_tab): + fetch_special_reports_tab.setObjectName("fetch_special_reports_tab") + fetch_special_reports_tab.resize(1094, 577) + fetch_special_reports_tab.setMinimumSize(QtCore.QSize(800, 0)) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/fetch_special_reports_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + fetch_special_reports_tab.setWindowIcon(icon) + self.verticalLayout_2 = QtWidgets.QVBoxLayout(fetch_special_reports_tab) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.frame_2 = QtWidgets.QFrame(fetch_special_reports_tab) + self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_2.setObjectName("frame_2") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame_2) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.frame_26 = QtWidgets.QFrame(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_26.sizePolicy().hasHeightForWidth()) + self.frame_26.setSizePolicy(sizePolicy) + self.frame_26.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_26.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_26.setObjectName("frame_26") + self.verticalLayout_15 = QtWidgets.QVBoxLayout(self.frame_26) + self.verticalLayout_15.setObjectName("verticalLayout_15") + self.label_21 = QtWidgets.QLabel(self.frame_26) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_21.setFont(font) + self.label_21.setObjectName("label_21") + self.verticalLayout_15.addWidget(self.label_21) + self.frame_32 = QtWidgets.QFrame(self.frame_26) + self.frame_32.setObjectName("frame_32") + self.horizontalLayout_9 = QtWidgets.QHBoxLayout(self.frame_32) + self.horizontalLayout_9.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_9.setObjectName("horizontalLayout_9") + self.select_vendors_button_special = QtWidgets.QPushButton(self.frame_32) + self.select_vendors_button_special.setObjectName("select_vendors_button_special") + self.horizontalLayout_9.addWidget(self.select_vendors_button_special) + self.deselect_vendors_button_special = QtWidgets.QPushButton(self.frame_32) + self.deselect_vendors_button_special.setObjectName("deselect_vendors_button_special") + self.horizontalLayout_9.addWidget(self.deselect_vendors_button_special) + self.verticalLayout_15.addWidget(self.frame_32) + self.vendors_list_view_special = QtWidgets.QListView(self.frame_26) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + self.vendors_list_view_special.setFont(font) + self.vendors_list_view_special.setAlternatingRowColors(True) + self.vendors_list_view_special.setObjectName("vendors_list_view_special") + self.verticalLayout_15.addWidget(self.vendors_list_view_special) + self.horizontalLayout_2.addWidget(self.frame_26) + self.frame_25 = QtWidgets.QFrame(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_25.sizePolicy().hasHeightForWidth()) + self.frame_25.setSizePolicy(sizePolicy) + self.frame_25.setMinimumSize(QtCore.QSize(180, 0)) + self.frame_25.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_25.setObjectName("frame_25") + self.verticalLayout_14 = QtWidgets.QVBoxLayout(self.frame_25) + self.verticalLayout_14.setObjectName("verticalLayout_14") + self.label_20 = QtWidgets.QLabel(self.frame_25) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_20.sizePolicy().hasHeightForWidth()) + self.label_20.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_20.setFont(font) + self.label_20.setObjectName("label_20") + self.verticalLayout_14.addWidget(self.label_20) + self.frame_30 = QtWidgets.QFrame(self.frame_25) + self.frame_30.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_30.setObjectName("frame_30") + self.gridLayout_9 = QtWidgets.QGridLayout(self.frame_30) + self.gridLayout_9.setObjectName("gridLayout_9") + self.tr_radio_button = QtWidgets.QRadioButton(self.frame_30) + self.tr_radio_button.setObjectName("tr_radio_button") + self.gridLayout_9.addWidget(self.tr_radio_button, 2, 0, 1, 1, QtCore.Qt.AlignHCenter) + self.pr_radio_button = QtWidgets.QRadioButton(self.frame_30) + self.pr_radio_button.setChecked(True) + self.pr_radio_button.setObjectName("pr_radio_button") + self.gridLayout_9.addWidget(self.pr_radio_button, 0, 0, 1, 1, QtCore.Qt.AlignHCenter) + self.dr_radio_button = QtWidgets.QRadioButton(self.frame_30) + self.dr_radio_button.setObjectName("dr_radio_button") + self.gridLayout_9.addWidget(self.dr_radio_button, 1, 0, 1, 1, QtCore.Qt.AlignHCenter) + self.ir_radio_button = QtWidgets.QRadioButton(self.frame_30) + self.ir_radio_button.setObjectName("ir_radio_button") + self.gridLayout_9.addWidget(self.ir_radio_button, 3, 0, 1, 1, QtCore.Qt.AlignHCenter) + self.verticalLayout_14.addWidget(self.frame_30) + self.horizontalLayout_2.addWidget(self.frame_25) + self.frame_27 = QtWidgets.QFrame(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_27.sizePolicy().hasHeightForWidth()) + self.frame_27.setSizePolicy(sizePolicy) + self.frame_27.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_27.setObjectName("frame_27") + self.verticalLayout_16 = QtWidgets.QVBoxLayout(self.frame_27) + self.verticalLayout_16.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_16.setObjectName("verticalLayout_16") + self.frame_15 = QtWidgets.QFrame(self.frame_27) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_15.sizePolicy().hasHeightForWidth()) + self.frame_15.setSizePolicy(sizePolicy) + self.frame_15.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_15.setObjectName("frame_15") + self.verticalLayout_22 = QtWidgets.QVBoxLayout(self.frame_15) + self.verticalLayout_22.setObjectName("verticalLayout_22") + self.frame_3 = QtWidgets.QFrame(self.frame_15) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_3.sizePolicy().hasHeightForWidth()) + self.frame_3.setSizePolicy(sizePolicy) + self.frame_3.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_3.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_3.setObjectName("frame_3") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame_3) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.label_14 = QtWidgets.QLabel(self.frame_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_14.sizePolicy().hasHeightForWidth()) + self.label_14.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_14.setFont(font) + self.label_14.setObjectName("label_14") + self.horizontalLayout.addWidget(self.label_14) + self.options_help_button = QtWidgets.QPushButton(self.frame_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.options_help_button.sizePolicy().hasHeightForWidth()) + self.options_help_button.setSizePolicy(sizePolicy) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/ui/resources/help_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.options_help_button.setIcon(icon1) + self.options_help_button.setObjectName("options_help_button") + self.horizontalLayout.addWidget(self.options_help_button) + self.verticalLayout_22.addWidget(self.frame_3) + self.options_frame = QtWidgets.QFrame(self.frame_15) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.options_frame.sizePolicy().hasHeightForWidth()) + self.options_frame.setSizePolicy(sizePolicy) + self.options_frame.setMinimumSize(QtCore.QSize(0, 0)) + self.options_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.options_frame.setObjectName("options_frame") + self.gridLayout_6 = QtWidgets.QGridLayout(self.options_frame) + self.gridLayout_6.setObjectName("gridLayout_6") + self.verticalLayout_22.addWidget(self.options_frame) + self.verticalLayout_16.addWidget(self.frame_15) + self.frame_16 = QtWidgets.QFrame(self.frame_27) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_16.sizePolicy().hasHeightForWidth()) + self.frame_16.setSizePolicy(sizePolicy) + self.frame_16.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_16.setObjectName("frame_16") + self.verticalLayout = QtWidgets.QVBoxLayout(self.frame_16) + self.verticalLayout.setObjectName("verticalLayout") + self.frame_4 = QtWidgets.QFrame(self.frame_16) + self.frame_4.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame_4.setFrameShadow(QtWidgets.QFrame.Plain) + self.frame_4.setObjectName("frame_4") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_4) + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_3.setSpacing(0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.label_15 = QtWidgets.QLabel(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_15.sizePolicy().hasHeightForWidth()) + self.label_15.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_15.setFont(font) + self.label_15.setObjectName("label_15") + self.horizontalLayout_3.addWidget(self.label_15) + self.date_range_help_button = QtWidgets.QPushButton(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.date_range_help_button.sizePolicy().hasHeightForWidth()) + self.date_range_help_button.setSizePolicy(sizePolicy) + self.date_range_help_button.setText("") + self.date_range_help_button.setIcon(icon1) + self.date_range_help_button.setObjectName("date_range_help_button") + self.horizontalLayout_3.addWidget(self.date_range_help_button) + self.verticalLayout.addWidget(self.frame_4) + self.frame = QtWidgets.QFrame(self.frame_16) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame.setObjectName("frame") + self.gridLayout_2 = QtWidgets.QGridLayout(self.frame) + self.gridLayout_2.setSpacing(20) + self.gridLayout_2.setObjectName("gridLayout_2") + self.label_25 = QtWidgets.QLabel(self.frame) + self.label_25.setObjectName("label_25") + self.gridLayout_2.addWidget(self.label_25, 1, 0, 1, 1) + self.begin_date_edit_special_year = QtWidgets.QDateEdit(self.frame) + self.begin_date_edit_special_year.setDateTime(QtCore.QDateTime(QtCore.QDate(2020, 1, 1), QtCore.QTime(0, 0, 0))) + self.begin_date_edit_special_year.setObjectName("begin_date_edit_special_year") + self.gridLayout_2.addWidget(self.begin_date_edit_special_year, 0, 1, 1, 1) + self.label_24 = QtWidgets.QLabel(self.frame) + self.label_24.setObjectName("label_24") + self.gridLayout_2.addWidget(self.label_24, 0, 0, 1, 1) + self.end_date_edit_special_year = QtWidgets.QDateEdit(self.frame) + self.end_date_edit_special_year.setDateTime(QtCore.QDateTime(QtCore.QDate(2020, 1, 1), QtCore.QTime(0, 0, 0))) + self.end_date_edit_special_year.setObjectName("end_date_edit_special_year") + self.gridLayout_2.addWidget(self.end_date_edit_special_year, 1, 1, 1, 1) + self.begin_month_combo_box = QtWidgets.QComboBox(self.frame) + self.begin_month_combo_box.setObjectName("begin_month_combo_box") + self.gridLayout_2.addWidget(self.begin_month_combo_box, 0, 2, 1, 1) + self.end_month_combo_box = QtWidgets.QComboBox(self.frame) + self.end_month_combo_box.setObjectName("end_month_combo_box") + self.gridLayout_2.addWidget(self.end_month_combo_box, 1, 2, 1, 1) + self.verticalLayout.addWidget(self.frame) + self.custom_dir_frame = QtWidgets.QFrame(self.frame_16) + self.custom_dir_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.custom_dir_frame.setObjectName("custom_dir_frame") + self.verticalLayout_26 = QtWidgets.QVBoxLayout(self.custom_dir_frame) + self.verticalLayout_26.setObjectName("verticalLayout_26") + self.frame_5 = QtWidgets.QFrame(self.custom_dir_frame) + self.frame_5.setFrameShape(QtWidgets.QFrame.NoFrame) + self.frame_5.setFrameShadow(QtWidgets.QFrame.Plain) + self.frame_5.setObjectName("frame_5") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.frame_5) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setSpacing(0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.label_41 = QtWidgets.QLabel(self.frame_5) + self.label_41.setAlignment(QtCore.Qt.AlignCenter) + self.label_41.setObjectName("label_41") + self.horizontalLayout_4.addWidget(self.label_41) + self.custom_dir_help_button = QtWidgets.QPushButton(self.frame_5) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.custom_dir_help_button.sizePolicy().hasHeightForWidth()) + self.custom_dir_help_button.setSizePolicy(sizePolicy) + self.custom_dir_help_button.setText("") + self.custom_dir_help_button.setIcon(icon1) + self.custom_dir_help_button.setObjectName("custom_dir_help_button") + self.horizontalLayout_4.addWidget(self.custom_dir_help_button) + self.verticalLayout_26.addWidget(self.frame_5) + self.frame_39 = QtWidgets.QFrame(self.custom_dir_frame) + self.frame_39.setObjectName("frame_39") + self.horizontalLayout_7 = QtWidgets.QHBoxLayout(self.frame_39) + self.horizontalLayout_7.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_7.setObjectName("horizontalLayout_7") + self.custom_dir_edit = QtWidgets.QLineEdit(self.frame_39) + self.custom_dir_edit.setObjectName("custom_dir_edit") + self.horizontalLayout_7.addWidget(self.custom_dir_edit) + self.custom_dir_button = QtWidgets.QPushButton(self.frame_39) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.custom_dir_button.sizePolicy().hasHeightForWidth()) + self.custom_dir_button.setSizePolicy(sizePolicy) + self.custom_dir_button.setObjectName("custom_dir_button") + self.horizontalLayout_7.addWidget(self.custom_dir_button) + self.verticalLayout_26.addWidget(self.frame_39) + self.verticalLayout.addWidget(self.custom_dir_frame) + self.verticalLayout_16.addWidget(self.frame_16) + self.frame_29 = QtWidgets.QFrame(self.frame_27) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_29.sizePolicy().hasHeightForWidth()) + self.frame_29.setSizePolicy(sizePolicy) + self.frame_29.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_29.setObjectName("frame_29") + self.gridLayout_8 = QtWidgets.QGridLayout(self.frame_29) + self.gridLayout_8.setObjectName("gridLayout_8") + self.fetch_special_data_button = QtWidgets.QPushButton(self.frame_29) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.fetch_special_data_button.sizePolicy().hasHeightForWidth()) + self.fetch_special_data_button.setSizePolicy(sizePolicy) + self.fetch_special_data_button.setMaximumSize(QtCore.QSize(180, 16777215)) + self.fetch_special_data_button.setObjectName("fetch_special_data_button") + self.gridLayout_8.addWidget(self.fetch_special_data_button, 0, 0, 1, 1) + self.verticalLayout_16.addWidget(self.frame_29) + self.horizontalLayout_2.addWidget(self.frame_27) + self.verticalLayout_2.addWidget(self.frame_2) + self.label = QtWidgets.QLabel(fetch_special_reports_tab) + self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label.setObjectName("label") + self.verticalLayout_2.addWidget(self.label) + + self.retranslateUi(fetch_special_reports_tab) + QtCore.QMetaObject.connectSlotsByName(fetch_special_reports_tab) + fetch_special_reports_tab.setTabOrder(self.select_vendors_button_special, self.deselect_vendors_button_special) + fetch_special_reports_tab.setTabOrder(self.deselect_vendors_button_special, self.vendors_list_view_special) + fetch_special_reports_tab.setTabOrder(self.vendors_list_view_special, self.pr_radio_button) + fetch_special_reports_tab.setTabOrder(self.pr_radio_button, self.dr_radio_button) + fetch_special_reports_tab.setTabOrder(self.dr_radio_button, self.tr_radio_button) + fetch_special_reports_tab.setTabOrder(self.tr_radio_button, self.ir_radio_button) + fetch_special_reports_tab.setTabOrder(self.ir_radio_button, self.options_frame) + fetch_special_reports_tab.setTabOrder(self.options_frame, self.begin_date_edit_special_year) + fetch_special_reports_tab.setTabOrder(self.begin_date_edit_special_year, self.end_date_edit_special_year) + fetch_special_reports_tab.setTabOrder(self.end_date_edit_special_year, self.fetch_special_data_button) + + def retranslateUi(self, fetch_special_reports_tab): + _translate = QtCore.QCoreApplication.translate + fetch_special_reports_tab.setWindowTitle(_translate("fetch_special_reports_tab", "Fetch Special Reports")) + self.label_21.setText(_translate("fetch_special_reports_tab", "Select Vendors")) + self.select_vendors_button_special.setText(_translate("fetch_special_reports_tab", "Select All")) + self.deselect_vendors_button_special.setText(_translate("fetch_special_reports_tab", "Deselect All")) + self.label_20.setText(_translate("fetch_special_reports_tab", "Select Report Type")) + self.tr_radio_button.setText(_translate("fetch_special_reports_tab", "TR")) + self.pr_radio_button.setText(_translate("fetch_special_reports_tab", "PR")) + self.dr_radio_button.setText(_translate("fetch_special_reports_tab", "DR")) + self.ir_radio_button.setText(_translate("fetch_special_reports_tab", "IR")) + self.label_14.setText(_translate("fetch_special_reports_tab", "Options")) + self.label_15.setText(_translate("fetch_special_reports_tab", "Date Range")) + self.label_25.setText(_translate("fetch_special_reports_tab", "End Date")) + self.begin_date_edit_special_year.setDisplayFormat(_translate("fetch_special_reports_tab", "yyyy")) + self.label_24.setText(_translate("fetch_special_reports_tab", "Begin Date")) + self.end_date_edit_special_year.setDisplayFormat(_translate("fetch_special_reports_tab", "yyyy")) + self.label_41.setText(_translate("fetch_special_reports_tab", "Report(s) will be saved to:")) + self.custom_dir_button.setText(_translate("fetch_special_reports_tab", "Change")) + self.fetch_special_data_button.setText(_translate("fetch_special_reports_tab", "Fetch Special Report")) + self.label.setText(_translate("fetch_special_reports_tab", "Note: Special reports are not added to the search database.")) + +import Resources_rc diff --git a/ui/FetchSpecialReportsTab.ui b/ui/FetchSpecialReportsTab.ui new file mode 100644 index 0000000..1c332eb --- /dev/null +++ b/ui/FetchSpecialReportsTab.ui @@ -0,0 +1,633 @@ + + + fetch_special_reports_tab + + + + 0 + 0 + 1094 + 577 + + + + + 800 + 0 + + + + Fetch Special Reports + + + + :/ui/resources/tab_icons/fetch_special_reports_icon.png:/ui/resources/tab_icons/fetch_special_reports_icon.png + + + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QFrame::StyledPanel + + + + + + + 75 + true + + + + Select Vendors + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Select All + + + + + + + Deselect All + + + + + + + + + + + Segoe UI + 11 + + + + true + + + + + + + + + + + 0 + 0 + + + + + 180 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 75 + true + + + + Select Report Type + + + + + + + QFrame::StyledPanel + + + + + + TR + + + + + + + PR + + + true + + + + + + + DR + + + + + + + IR + + + + + + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 75 + true + + + + Options + + + + + + + + 0 + 0 + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 75 + true + + + + Date Range + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + 20 + + + + + End Date + + + + + + + + 0 + 0 + 0 + 2020 + 1 + 1 + + + + yyyy + + + + + + + Begin Date + + + + + + + + 0 + 0 + 0 + 2020 + 1 + 1 + + + + yyyy + + + + + + + + + + + + + + + + QFrame::StyledPanel + + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Report(s) will be saved to: + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + 0 + + + + Change + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 180 + 16777215 + + + + Fetch Special Report + + + + + + + + + + + + + + + + Note: Special reports are not added to the search database. + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + select_vendors_button_special + deselect_vendors_button_special + vendors_list_view_special + pr_radio_button + dr_radio_button + tr_radio_button + ir_radio_button + options_frame + begin_date_edit_special_year + end_date_edit_special_year + fetch_special_data_button + + + + + + diff --git a/ui/ImportReportTab.py b/ui/ImportReportTab.py new file mode 100644 index 0000000..ca29e54 --- /dev/null +++ b/ui/ImportReportTab.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ImportReportTab.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_import_report_tab(object): + def setupUi(self, import_report_tab): + import_report_tab.setObjectName("import_report_tab") + import_report_tab.resize(1008, 530) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/import_report_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + import_report_tab.setWindowIcon(icon) + self.verticalLayout = QtWidgets.QVBoxLayout(import_report_tab) + self.verticalLayout.setObjectName("verticalLayout") + self.frame = QtWidgets.QFrame(import_report_tab) + self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame.setObjectName("frame") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.frame_21 = QtWidgets.QFrame(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_21.sizePolicy().hasHeightForWidth()) + self.frame_21.setSizePolicy(sizePolicy) + self.frame_21.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_21.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_21.setObjectName("frame_21") + self.verticalLayout_12 = QtWidgets.QVBoxLayout(self.frame_21) + self.verticalLayout_12.setObjectName("verticalLayout_12") + self.label_18 = QtWidgets.QLabel(self.frame_21) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.label_18.setFont(font) + self.label_18.setObjectName("label_18") + self.verticalLayout_12.addWidget(self.label_18) + self.vendors_list_view_import = QtWidgets.QListView(self.frame_21) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + self.vendors_list_view_import.setFont(font) + self.vendors_list_view_import.setAlternatingRowColors(True) + self.vendors_list_view_import.setObjectName("vendors_list_view_import") + self.verticalLayout_12.addWidget(self.vendors_list_view_import) + self.horizontalLayout_2.addWidget(self.frame_21) + self.frame_20 = QtWidgets.QFrame(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_20.sizePolicy().hasHeightForWidth()) + self.frame_20.setSizePolicy(sizePolicy) + self.frame_20.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_20.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_20.setObjectName("frame_20") + self.verticalLayout_11 = QtWidgets.QVBoxLayout(self.frame_20) + self.verticalLayout_11.setObjectName("verticalLayout_11") + self.label_17 = QtWidgets.QLabel(self.frame_20) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.label_17.setFont(font) + self.label_17.setObjectName("label_17") + self.verticalLayout_11.addWidget(self.label_17) + self.report_types_list_view_import = QtWidgets.QListView(self.frame_20) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + self.report_types_list_view_import.setFont(font) + self.report_types_list_view_import.setAlternatingRowColors(True) + self.report_types_list_view_import.setObjectName("report_types_list_view_import") + self.verticalLayout_11.addWidget(self.report_types_list_view_import) + self.horizontalLayout_2.addWidget(self.frame_20) + self.frame_22 = QtWidgets.QFrame(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_22.sizePolicy().hasHeightForWidth()) + self.frame_22.setSizePolicy(sizePolicy) + self.frame_22.setMinimumSize(QtCore.QSize(200, 0)) + self.frame_22.setObjectName("frame_22") + self.verticalLayout_13 = QtWidgets.QVBoxLayout(self.frame_22) + self.verticalLayout_13.setContentsMargins(0, 0, 0, 0) + self.verticalLayout_13.setObjectName("verticalLayout_13") + self.frame_17 = QtWidgets.QFrame(self.frame_22) + self.frame_17.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_17.setObjectName("frame_17") + self.verticalLayout_24 = QtWidgets.QVBoxLayout(self.frame_17) + self.verticalLayout_24.setObjectName("verticalLayout_24") + self.label_16 = QtWidgets.QLabel(self.frame_17) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_16.sizePolicy().hasHeightForWidth()) + self.label_16.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_16.setFont(font) + self.label_16.setObjectName("label_16") + self.verticalLayout_24.addWidget(self.label_16) + self.frame_19 = QtWidgets.QFrame(self.frame_17) + self.frame_19.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_19.setObjectName("frame_19") + self.horizontalLayout_13 = QtWidgets.QHBoxLayout(self.frame_19) + self.horizontalLayout_13.setObjectName("horizontalLayout_13") + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_13.addItem(spacerItem) + self.label_19 = QtWidgets.QLabel(self.frame_19) + self.label_19.setMinimumSize(QtCore.QSize(100, 0)) + self.label_19.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label_19.setObjectName("label_19") + self.horizontalLayout_13.addWidget(self.label_19) + self.report_year_date_edit = QtWidgets.QDateEdit(self.frame_19) + self.report_year_date_edit.setObjectName("report_year_date_edit") + self.horizontalLayout_13.addWidget(self.report_year_date_edit) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_13.addItem(spacerItem1) + self.verticalLayout_24.addWidget(self.frame_19) + self.verticalLayout_13.addWidget(self.frame_17) + self.frame_18 = QtWidgets.QFrame(self.frame_22) + self.frame_18.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_18.setObjectName("frame_18") + self.verticalLayout_25 = QtWidgets.QVBoxLayout(self.frame_18) + self.verticalLayout_25.setObjectName("verticalLayout_25") + self.label_36 = QtWidgets.QLabel(self.frame_18) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_36.sizePolicy().hasHeightForWidth()) + self.label_36.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.label_36.setFont(font) + self.label_36.setObjectName("label_36") + self.verticalLayout_25.addWidget(self.label_36) + self.frame_38 = QtWidgets.QFrame(self.frame_18) + self.frame_38.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_38.setObjectName("frame_38") + self.horizontalLayout_15 = QtWidgets.QHBoxLayout(self.frame_38) + self.horizontalLayout_15.setObjectName("horizontalLayout_15") + self.select_file_button = QtWidgets.QPushButton(self.frame_38) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.select_file_button.sizePolicy().hasHeightForWidth()) + self.select_file_button.setSizePolicy(sizePolicy) + self.select_file_button.setObjectName("select_file_button") + self.horizontalLayout_15.addWidget(self.select_file_button) + self.selected_file_edit = QtWidgets.QLineEdit(self.frame_38) + self.selected_file_edit.setReadOnly(True) + self.selected_file_edit.setObjectName("selected_file_edit") + self.horizontalLayout_15.addWidget(self.selected_file_edit) + self.verticalLayout_25.addWidget(self.frame_38) + self.verticalLayout_13.addWidget(self.frame_18) + self.frame_24 = QtWidgets.QFrame(self.frame_22) + self.frame_24.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_24.setObjectName("frame_24") + self.gridLayout_7 = QtWidgets.QGridLayout(self.frame_24) + self.gridLayout_7.setObjectName("gridLayout_7") + self.import_report_button = QtWidgets.QPushButton(self.frame_24) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.import_report_button.sizePolicy().hasHeightForWidth()) + self.import_report_button.setSizePolicy(sizePolicy) + self.import_report_button.setMaximumSize(QtCore.QSize(180, 16777215)) + self.import_report_button.setObjectName("import_report_button") + self.gridLayout_7.addWidget(self.import_report_button, 0, 0, 1, 1) + self.verticalLayout_13.addWidget(self.frame_24) + self.horizontalLayout_2.addWidget(self.frame_22) + self.verticalLayout.addWidget(self.frame) + self.label = QtWidgets.QLabel(import_report_tab) + self.label.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) + self.label.setObjectName("label") + self.verticalLayout.addWidget(self.label) + + self.retranslateUi(import_report_tab) + QtCore.QMetaObject.connectSlotsByName(import_report_tab) + import_report_tab.setTabOrder(self.vendors_list_view_import, self.report_types_list_view_import) + import_report_tab.setTabOrder(self.report_types_list_view_import, self.report_year_date_edit) + import_report_tab.setTabOrder(self.report_year_date_edit, self.selected_file_edit) + import_report_tab.setTabOrder(self.selected_file_edit, self.import_report_button) + + def retranslateUi(self, import_report_tab): + _translate = QtCore.QCoreApplication.translate + import_report_tab.setWindowTitle(_translate("import_report_tab", "Import Report")) + self.label_18.setText(_translate("import_report_tab", "Select Vendor")) + self.label_17.setText(_translate("import_report_tab", "Select Report Type")) + self.label_16.setText(_translate("import_report_tab", "Date")) + self.label_19.setText(_translate("import_report_tab", "Report Year")) + self.report_year_date_edit.setDisplayFormat(_translate("import_report_tab", "yyyy")) + self.label_36.setText(_translate("import_report_tab", "Target Report File")) + self.select_file_button.setText(_translate("import_report_tab", "Select File")) + self.import_report_button.setText(_translate("import_report_tab", "Import Selected Report")) + self.label.setText(_translate("import_report_tab", "Note: Only yearly reports (all available data for one calender year) should be imported. Imported reports are added to the search database.")) + +import Resources_rc diff --git a/ui/ImportReportTab.ui b/ui/ImportReportTab.ui new file mode 100644 index 0000000..b831e8f --- /dev/null +++ b/ui/ImportReportTab.ui @@ -0,0 +1,352 @@ + + + import_report_tab + + + + 0 + 0 + 1008 + 530 + + + + Import Report + + + + :/ui/resources/tab_icons/import_report_icon.png:/ui/resources/tab_icons/import_report_icon.png + + + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QFrame::StyledPanel + + + + + + + Segoe UI + 11 + 75 + true + + + + Select Vendor + + + + + + + + Segoe UI + 11 + + + + true + + + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + QFrame::StyledPanel + + + + + + + Segoe UI + 11 + 75 + true + + + + Select Report Type + + + + + + + + Segoe UI + 11 + + + + true + + + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 75 + true + + + + Date + + + + + + + QFrame::StyledPanel + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 100 + 0 + + + + Report Year + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + yyyy + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 75 + true + + + + Target Report File + + + + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + Select File + + + + + + + true + + + + + + + + + + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + 180 + 16777215 + + + + Import Selected Report + + + + + + + + + + + + + + + + Note: Only yearly reports (all available data for one calender year) should be imported. Imported reports are added to the search database. + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + vendors_list_view_import + report_types_list_view_import + report_year_date_edit + selected_file_edit + import_report_button + + + + + + diff --git a/ui/MainWindow.py b/ui/MainWindow.py new file mode 100644 index 0000000..bb58cfd --- /dev/null +++ b/ui/MainWindow.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'MainWindow.ui' +# +# Created by: PyQt5 UI code generator 5.12.3 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_mainWindow(object): + def setupUi(self, mainWindow): + mainWindow.setObjectName("mainWindow") + mainWindow.resize(1200, 721) + mainWindow.setMinimumSize(QtCore.QSize(1200, 640)) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + mainWindow.setFont(font) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/main_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + mainWindow.setWindowIcon(icon) + self.centralwidget = QtWidgets.QWidget(mainWindow) + self.centralwidget.setObjectName("centralwidget") + self.gridLayout = QtWidgets.QGridLayout(self.centralwidget) + self.gridLayout.setContentsMargins(-1, -1, -1, 0) + self.gridLayout.setObjectName("gridLayout") + self.tab_widget = QtWidgets.QTabWidget(self.centralwidget) + self.tab_widget.setEnabled(True) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.tab_widget.sizePolicy().hasHeightForWidth()) + self.tab_widget.setSizePolicy(sizePolicy) + self.tab_widget.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.tab_widget.setTabPosition(QtWidgets.QTabWidget.North) + self.tab_widget.setElideMode(QtCore.Qt.ElideNone) + self.tab_widget.setObjectName("tab_widget") + self.gridLayout.addWidget(self.tab_widget, 1, 0, 1, 1) + mainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtWidgets.QMenuBar(mainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 1200, 21)) + self.menubar.setObjectName("menubar") + mainWindow.setMenuBar(self.menubar) + self.statusbar = QtWidgets.QStatusBar(mainWindow) + self.statusbar.setObjectName("statusbar") + mainWindow.setStatusBar(self.statusbar) + + self.retranslateUi(mainWindow) + self.tab_widget.setCurrentIndex(-1) + QtCore.QMetaObject.connectSlotsByName(mainWindow) + + def retranslateUi(self, mainWindow): + _translate = QtCore.QCoreApplication.translate + mainWindow.setWindowTitle(_translate("mainWindow", "COUNTER 5 Report Tool")) +import Resources_rc diff --git a/ui/MainWindow.ui b/ui/MainWindow.ui new file mode 100644 index 0000000..77479c7 --- /dev/null +++ b/ui/MainWindow.ui @@ -0,0 +1,83 @@ + + + mainWindow + + + + 0 + 0 + 1200 + 721 + + + + + 1200 + 640 + + + + + Segoe UI + 11 + + + + COUNTER 5 Report Tool + + + + :/ui/resources/main_icon.png:/ui/resources/main_icon.png + + + + + 0 + + + + + true + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + QTabWidget::North + + + -1 + + + Qt::ElideNone + + + + + + + + + 0 + 0 + 1200 + 21 + + + + + + + + + + diff --git a/ui/ManageVendorsTab.py b/ui/ManageVendorsTab.py new file mode 100644 index 0000000..3940e50 --- /dev/null +++ b/ui/ManageVendorsTab.py @@ -0,0 +1,262 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ManageVendorsTab.ui' +# +# Created by: PyQt5 UI code generator 5.12.3 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_manage_vendors_tab(object): + def setupUi(self, manage_vendors_tab): + manage_vendors_tab.setObjectName("manage_vendors_tab") + manage_vendors_tab.resize(807, 642) + font = QtGui.QFont() + font.setFamily("Segoe UI") + manage_vendors_tab.setFont(font) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/manage_vendors_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + manage_vendors_tab.setWindowIcon(icon) + self.horizontalLayout = QtWidgets.QHBoxLayout(manage_vendors_tab) + self.horizontalLayout.setObjectName("horizontalLayout") + self.frame_12 = QtWidgets.QFrame(manage_vendors_tab) + self.frame_12.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_12.setObjectName("frame_12") + self.verticalLayout_6 = QtWidgets.QVBoxLayout(self.frame_12) + self.verticalLayout_6.setObjectName("verticalLayout_6") + self.label_13 = QtWidgets.QLabel(self.frame_12) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.label_13.setFont(font) + self.label_13.setObjectName("label_13") + self.verticalLayout_6.addWidget(self.label_13) + self.vendorsListView = QtWidgets.QListView(self.frame_12) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + self.vendorsListView.setFont(font) + self.vendorsListView.setAlternatingRowColors(True) + self.vendorsListView.setObjectName("vendorsListView") + self.verticalLayout_6.addWidget(self.vendorsListView) + self.frame_2 = QtWidgets.QFrame(self.frame_12) + self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_2.setObjectName("frame_2") + self.gridLayout_3 = QtWidgets.QGridLayout(self.frame_2) + self.gridLayout_3.setObjectName("gridLayout_3") + self.importVendorsButton = QtWidgets.QPushButton(self.frame_2) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/ui/resources/import_vendors_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.importVendorsButton.setIcon(icon1) + self.importVendorsButton.setObjectName("importVendorsButton") + self.gridLayout_3.addWidget(self.importVendorsButton, 0, 1, 1, 1) + self.addVendorButton = QtWidgets.QPushButton(self.frame_2) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(":/ui/resources/add_vendor_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.addVendorButton.setIcon(icon2) + self.addVendorButton.setObjectName("addVendorButton") + self.gridLayout_3.addWidget(self.addVendorButton, 0, 0, 1, 1) + self.exportVendorsButton = QtWidgets.QPushButton(self.frame_2) + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap(":/ui/resources/export_vendors_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.exportVendorsButton.setIcon(icon3) + self.exportVendorsButton.setObjectName("exportVendorsButton") + self.gridLayout_3.addWidget(self.exportVendorsButton, 0, 2, 1, 1) + self.verticalLayout_6.addWidget(self.frame_2) + self.horizontalLayout.addWidget(self.frame_12) + self.frame_8 = QtWidgets.QFrame(manage_vendors_tab) + self.frame_8.setObjectName("frame_8") + self.horizontalLayout_2 = QtWidgets.QVBoxLayout(self.frame_8) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.frame = QtWidgets.QFrame(self.frame_8) + self.frame.setEnabled(True) + self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame.setObjectName("frame") + self.verticalLayout = QtWidgets.QVBoxLayout(self.frame) + self.verticalLayout.setObjectName("verticalLayout") + self.label_7 = QtWidgets.QLabel(self.frame) + self.label_7.setEnabled(True) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.label_7.setFont(font) + self.label_7.setFrameShape(QtWidgets.QFrame.NoFrame) + self.label_7.setObjectName("label_7") + self.verticalLayout.addWidget(self.label_7) + self.edit_vendor_details_frame = QtWidgets.QFrame(self.frame) + self.edit_vendor_details_frame.setObjectName("edit_vendor_details_frame") + self.gridLayout_5 = QtWidgets.QGridLayout(self.edit_vendor_details_frame) + self.gridLayout_5.setSpacing(20) + self.gridLayout_5.setObjectName("gridLayout_5") + self.name_validation_label = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.name_validation_label.setObjectName("name_validation_label") + self.gridLayout_5.addWidget(self.name_validation_label, 1, 1, 1, 1) + self.label_28 = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label_28.setObjectName("label_28") + self.gridLayout_5.addWidget(self.label_28, 11, 0, 1, 1, QtCore.Qt.AlignTop) + self.descriptionEdit = QtWidgets.QPlainTextEdit(self.edit_vendor_details_frame) + self.descriptionEdit.setAutoFillBackground(False) + self.descriptionEdit.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.descriptionEdit.setFrameShadow(QtWidgets.QFrame.Sunken) + self.descriptionEdit.setTabChangesFocus(False) + self.descriptionEdit.setPlainText("") + self.descriptionEdit.setBackgroundVisible(False) + self.descriptionEdit.setObjectName("descriptionEdit") + self.gridLayout_5.addWidget(self.descriptionEdit, 11, 1, 1, 1) + self.nameEdit = QtWidgets.QLineEdit(self.edit_vendor_details_frame) + self.nameEdit.setObjectName("nameEdit") + self.gridLayout_5.addWidget(self.nameEdit, 0, 1, 1, 1) + self.url_validation_label = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.url_validation_label.setObjectName("url_validation_label") + self.gridLayout_5.addWidget(self.url_validation_label, 3, 1, 1, 1) + self.companiesText = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.companiesText.setObjectName("companiesText") + self.gridLayout_5.addWidget(self.companiesText, 12, 0, 1, 1, QtCore.Qt.AlignTop) + self.label_4 = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label_4.setObjectName("label_4") + self.gridLayout_5.addWidget(self.label_4, 7, 0, 1, 1) + self.frame_3 = QtWidgets.QFrame(self.edit_vendor_details_frame) + self.frame_3.setObjectName("frame_3") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_3) + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.non_Sushi_check_box = QtWidgets.QCheckBox(self.frame_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.non_Sushi_check_box.sizePolicy().hasHeightForWidth()) + self.non_Sushi_check_box.setSizePolicy(sizePolicy) + self.non_Sushi_check_box.setText("") + self.non_Sushi_check_box.setObjectName("non_Sushi_check_box") + self.horizontalLayout_3.addWidget(self.non_Sushi_check_box) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_3.addItem(spacerItem) + self.non_sushi_help_button = QtWidgets.QPushButton(self.frame_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.non_sushi_help_button.sizePolicy().hasHeightForWidth()) + self.non_sushi_help_button.setSizePolicy(sizePolicy) + self.non_sushi_help_button.setText("") + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap(":/ui/resources/help_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.non_sushi_help_button.setIcon(icon4) + self.non_sushi_help_button.setObjectName("non_sushi_help_button") + self.horizontalLayout_3.addWidget(self.non_sushi_help_button) + self.gridLayout_5.addWidget(self.frame_3, 10, 1, 1, 1) + self.label_3 = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label_3.setObjectName("label_3") + self.gridLayout_5.addWidget(self.label_3, 2, 0, 1, 1) + self.baseUrlEdit = QtWidgets.QLineEdit(self.edit_vendor_details_frame) + self.baseUrlEdit.setObjectName("baseUrlEdit") + self.gridLayout_5.addWidget(self.baseUrlEdit, 2, 1, 1, 1) + self.label_2 = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label_2.setObjectName("label_2") + self.gridLayout_5.addWidget(self.label_2, 4, 0, 1, 1) + self.apiKeyEdit = QtWidgets.QLineEdit(self.edit_vendor_details_frame) + self.apiKeyEdit.setObjectName("apiKeyEdit") + self.gridLayout_5.addWidget(self.apiKeyEdit, 8, 1, 1, 1) + self.label_5 = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label_5.setObjectName("label_5") + self.gridLayout_5.addWidget(self.label_5, 8, 0, 1, 1) + self.label = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label.setObjectName("label") + self.gridLayout_5.addWidget(self.label, 0, 0, 1, 1) + self.companiesEdit = QtWidgets.QPlainTextEdit(self.edit_vendor_details_frame) + self.companiesEdit.setObjectName("companiesEdit") + self.gridLayout_5.addWidget(self.companiesEdit, 12, 1, 1, 1) + self.platformEdit = QtWidgets.QLineEdit(self.edit_vendor_details_frame) + self.platformEdit.setObjectName("platformEdit") + self.gridLayout_5.addWidget(self.platformEdit, 9, 1, 1, 1) + self.label_39 = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label_39.setObjectName("label_39") + self.gridLayout_5.addWidget(self.label_39, 10, 0, 1, 1) + self.label_6 = QtWidgets.QLabel(self.edit_vendor_details_frame) + self.label_6.setObjectName("label_6") + self.gridLayout_5.addWidget(self.label_6, 9, 0, 1, 1) + self.requestorIdEdit = QtWidgets.QLineEdit(self.edit_vendor_details_frame) + self.requestorIdEdit.setObjectName("requestorIdEdit") + self.gridLayout_5.addWidget(self.requestorIdEdit, 7, 1, 1, 1) + self.customerIdEdit = QtWidgets.QLineEdit(self.edit_vendor_details_frame) + self.customerIdEdit.setObjectName("customerIdEdit") + self.gridLayout_5.addWidget(self.customerIdEdit, 4, 1, 1, 1) + self.verticalLayout.addWidget(self.edit_vendor_details_frame) + self.edit_vendor_options_frame = QtWidgets.QFrame(self.frame) + self.edit_vendor_options_frame.setEnabled(False) + self.edit_vendor_options_frame.setObjectName("edit_vendor_options_frame") + self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.edit_vendor_options_frame) + self.horizontalLayout_5.setObjectName("horizontalLayout_5") + self.saveVendorChangesButton = QtWidgets.QPushButton(self.edit_vendor_options_frame) + icon5 = QtGui.QIcon() + icon5.addPixmap(QtGui.QPixmap(":/ui/resources/save_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.saveVendorChangesButton.setIcon(icon5) + self.saveVendorChangesButton.setDefault(False) + self.saveVendorChangesButton.setFlat(False) + self.saveVendorChangesButton.setObjectName("saveVendorChangesButton") + self.horizontalLayout_5.addWidget(self.saveVendorChangesButton) + self.undoVendorChangesButton = QtWidgets.QPushButton(self.edit_vendor_options_frame) + icon6 = QtGui.QIcon() + icon6.addPixmap(QtGui.QPixmap(":/ui/resources/undo_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.undoVendorChangesButton.setIcon(icon6) + self.undoVendorChangesButton.setObjectName("undoVendorChangesButton") + self.horizontalLayout_5.addWidget(self.undoVendorChangesButton) + self.removeVendorButton = QtWidgets.QPushButton(self.edit_vendor_options_frame) + icon7 = QtGui.QIcon() + icon7.addPixmap(QtGui.QPixmap(":/ui/resources/remove_vendor_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.removeVendorButton.setIcon(icon7) + self.removeVendorButton.setObjectName("removeVendorButton") + self.horizontalLayout_5.addWidget(self.removeVendorButton) + self.verticalLayout.addWidget(self.edit_vendor_options_frame) + self.horizontalLayout_2.addWidget(self.frame) + self.horizontalLayout.addWidget(self.frame_8) + + self.retranslateUi(manage_vendors_tab) + QtCore.QMetaObject.connectSlotsByName(manage_vendors_tab) + manage_vendors_tab.setTabOrder(self.vendorsListView, self.addVendorButton) + manage_vendors_tab.setTabOrder(self.addVendorButton, self.importVendorsButton) + manage_vendors_tab.setTabOrder(self.importVendorsButton, self.exportVendorsButton) + manage_vendors_tab.setTabOrder(self.exportVendorsButton, self.nameEdit) + manage_vendors_tab.setTabOrder(self.nameEdit, self.baseUrlEdit) + manage_vendors_tab.setTabOrder(self.baseUrlEdit, self.customerIdEdit) + manage_vendors_tab.setTabOrder(self.customerIdEdit, self.requestorIdEdit) + manage_vendors_tab.setTabOrder(self.requestorIdEdit, self.apiKeyEdit) + manage_vendors_tab.setTabOrder(self.apiKeyEdit, self.platformEdit) + manage_vendors_tab.setTabOrder(self.platformEdit, self.non_Sushi_check_box) + manage_vendors_tab.setTabOrder(self.non_Sushi_check_box, self.non_sushi_help_button) + manage_vendors_tab.setTabOrder(self.non_sushi_help_button, self.descriptionEdit) + manage_vendors_tab.setTabOrder(self.descriptionEdit, self.companiesEdit) + manage_vendors_tab.setTabOrder(self.companiesEdit, self.saveVendorChangesButton) + manage_vendors_tab.setTabOrder(self.saveVendorChangesButton, self.undoVendorChangesButton) + manage_vendors_tab.setTabOrder(self.undoVendorChangesButton, self.removeVendorButton) + + def retranslateUi(self, manage_vendors_tab): + _translate = QtCore.QCoreApplication.translate + manage_vendors_tab.setWindowTitle(_translate("manage_vendors_tab", "Manage Vendors")) + self.label_13.setText(_translate("manage_vendors_tab", "Select Vendor")) + self.importVendorsButton.setText(_translate("manage_vendors_tab", "Import Vendors")) + self.addVendorButton.setText(_translate("manage_vendors_tab", "Add New Vendor")) + self.exportVendorsButton.setText(_translate("manage_vendors_tab", "Export Vendors")) + self.label_7.setText(_translate("manage_vendors_tab", "Edit Vendor")) + self.name_validation_label.setText(_translate("manage_vendors_tab", "Validation label")) + self.label_28.setText(_translate("manage_vendors_tab", "Description")) + self.url_validation_label.setText(_translate("manage_vendors_tab", "Validation label")) + self.companiesText.setText(_translate("manage_vendors_tab", "Third party provider")) + self.label_4.setText(_translate("manage_vendors_tab", "Requestor ID")) + self.label_3.setText(_translate("manage_vendors_tab", "Base URL")) + self.label_2.setText(_translate("manage_vendors_tab", "Customer ID")) + self.label_5.setText(_translate("manage_vendors_tab", "API Key")) + self.label.setText(_translate("manage_vendors_tab", "Name")) + self.label_39.setText(_translate("manage_vendors_tab", "Non-SUSHI Vendor")) + self.label_6.setText(_translate("manage_vendors_tab", "Platform")) + self.saveVendorChangesButton.setText(_translate("manage_vendors_tab", "Save Changes")) + self.undoVendorChangesButton.setText(_translate("manage_vendors_tab", "Undo Changes")) + self.removeVendorButton.setText(_translate("manage_vendors_tab", "Remove Vendor")) +import Resources_rc diff --git a/ui/ManageVendorsTab.ui b/ui/ManageVendorsTab.ui new file mode 100644 index 0000000..ecb48d0 --- /dev/null +++ b/ui/ManageVendorsTab.ui @@ -0,0 +1,419 @@ + + + manage_vendors_tab + + + + 0 + 0 + 807 + 642 + + + + + Segoe UI + + + + Manage Vendors + + + + :/ui/resources/tab_icons/manage_vendors_icon.png:/ui/resources/tab_icons/manage_vendors_icon.png + + + + + + QFrame::StyledPanel + + + + + + + Segoe UI + 11 + 75 + true + + + + Select Vendor + + + + + + + + Segoe UI + 11 + + + + true + + + + + + + QFrame::StyledPanel + + + + + + Import Vendors + + + + :/ui/resources/import_vendors_icon.png:/ui/resources/import_vendors_icon.png + + + + + + + Add New Vendor + + + + :/ui/resources/add_vendor_icon.png:/ui/resources/add_vendor_icon.png + + + + + + + Export Vendors + + + + :/ui/resources/export_vendors_icon.png:/ui/resources/export_vendors_icon.png + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + QFrame::StyledPanel + + + + + + true + + + + Segoe UI + 11 + 75 + true + + + + QFrame::NoFrame + + + Edit Vendor + + + + + + + + 20 + + + + + Validation label + + + + + + + Description + + + + + + + false + + + QFrame::StyledPanel + + + QFrame::Sunken + + + false + + + + + + false + + + + + + + + + + Validation label + + + + + + + Third party provider + + + + + + + Requestor ID + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + Base URL + + + + + + + + + + Customer ID + + + + + + + + + + API Key + + + + + + + Name + + + + + + + + + + + + + Non-SUSHI Vendor + + + + + + + Platform + + + + + + + + + + + + + + + + false + + + + + + Save Changes + + + + :/ui/resources/save_icon.png:/ui/resources/save_icon.png + + + false + + + false + + + + + + + Undo Changes + + + + :/ui/resources/undo_icon.png:/ui/resources/undo_icon.png + + + + + + + Remove Vendor + + + + :/ui/resources/remove_vendor_icon.png:/ui/resources/remove_vendor_icon.png + + + + + + + + + + + + + + + + vendorsListView + addVendorButton + importVendorsButton + exportVendorsButton + nameEdit + baseUrlEdit + customerIdEdit + requestorIdEdit + apiKeyEdit + platformEdit + non_Sushi_check_box + non_sushi_help_button + descriptionEdit + companiesEdit + saveVendorChangesButton + undoVendorChangesButton + removeVendorButton + + + + + + diff --git a/ui/MessageDialog.py b/ui/MessageDialog.py new file mode 100644 index 0000000..c4a25fb --- /dev/null +++ b/ui/MessageDialog.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'MessageDialog.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_message_dialog(object): + def setupUi(self, message_dialog): + message_dialog.setObjectName("message_dialog") + message_dialog.resize(400, 60) + self.verticalLayout = QtWidgets.QVBoxLayout(message_dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.message_label = QtWidgets.QLabel(message_dialog) + font = QtGui.QFont() + font.setPointSize(9) + self.message_label.setFont(font) + self.message_label.setAlignment(QtCore.Qt.AlignCenter) + self.message_label.setObjectName("message_label") + self.verticalLayout.addWidget(self.message_label) + self.buttonBox = QtWidgets.QDialogButtonBox(message_dialog) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.buttonBox.accepted.connect(message_dialog.accept) + self.retranslateUi(message_dialog) + QtCore.QMetaObject.connectSlotsByName(message_dialog) + + def retranslateUi(self, message_dialog): + _translate = QtCore.QCoreApplication.translate + message_dialog.setWindowTitle(_translate("message_dialog", "Message")) + self.message_label.setText(_translate("message_dialog", "Message!")) + diff --git a/ui/MessageDialog.ui b/ui/MessageDialog.ui new file mode 100644 index 0000000..b4752fc --- /dev/null +++ b/ui/MessageDialog.ui @@ -0,0 +1,43 @@ + + + message_dialog + + + + 0 + 0 + 400 + 60 + + + + Message + + + + + + + 9 + + + + Message! + + + Qt::AlignCenter + + + + + + + QDialogButtonBox::Ok + + + + + + + + diff --git a/ui/RemoveVendorDialog.py b/ui/RemoveVendorDialog.py new file mode 100644 index 0000000..cb8562c --- /dev/null +++ b/ui/RemoveVendorDialog.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'RemoveVendorDialog.ui' +# +# Created by: PyQt5 UI code generator 5.14.1 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_dialog_remove(object): + def setupUi(self, dialog_remove): + dialog_remove.setObjectName("dialog_remove") + dialog_remove.resize(399, 100) + self.label = QtWidgets.QLabel(dialog_remove) + self.label.setGeometry(QtCore.QRect(10, 10, 371, 41)) + self.label.setObjectName("label") + self.buttonBox = QtWidgets.QDialogButtonBox(dialog_remove) + self.buttonBox.setGeometry(QtCore.QRect(210, 60, 164, 32)) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + + self.retranslateUi(dialog_remove) + self.buttonBox.accepted.connect(dialog_remove.accept) + self.buttonBox.rejected.connect(dialog_remove.reject) + QtCore.QMetaObject.connectSlotsByName(dialog_remove) + + def retranslateUi(self, dialog_remove): + _translate = QtCore.QCoreApplication.translate + dialog_remove.setWindowTitle(_translate("dialog_remove", "Remove Vendor")) + self.label.setText(_translate("dialog_remove", "Are you sure you want to remove this vendor?")) diff --git a/ui/RemoveVendorDialog.ui b/ui/RemoveVendorDialog.ui new file mode 100644 index 0000000..53cd726 --- /dev/null +++ b/ui/RemoveVendorDialog.ui @@ -0,0 +1,45 @@ + + + dialog_remove + + + + 0 + 0 + 399 + 100 + + + + Remove Vendor + + + + + 10 + 10 + 371 + 41 + + + + Are you sure you want to remove this vendor? + + + + + + 210 + 60 + 164 + 32 + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + diff --git a/ui/ReportResultWidget.py b/ui/ReportResultWidget.py new file mode 100644 index 0000000..a043cb6 --- /dev/null +++ b/ui/ReportResultWidget.py @@ -0,0 +1,192 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ReportResultWidget.ui' +# +# Created by: PyQt5 UI code generator 5.12.3 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_ReportResultWidget(object): + def setupUi(self, ReportResultWidget): + ReportResultWidget.setObjectName("ReportResultWidget") + ReportResultWidget.resize(600, 60) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(200) + sizePolicy.setHeightForWidth(ReportResultWidget.sizePolicy().hasHeightForWidth()) + ReportResultWidget.setSizePolicy(sizePolicy) + ReportResultWidget.setMinimumSize(QtCore.QSize(600, 60)) + ReportResultWidget.setMaximumSize(QtCore.QSize(16777215, 16777215)) + ReportResultWidget.setSizeIncrement(QtCore.QSize(0, 1000)) + font = QtGui.QFont() + font.setPointSize(9) + ReportResultWidget.setFont(font) + self.horizontalLayout = QtWidgets.QHBoxLayout(ReportResultWidget) + self.horizontalLayout.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout.setObjectName("horizontalLayout") + self.frame = QtWidgets.QFrame(ReportResultWidget) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setMinimumSize(QtCore.QSize(600, 0)) + self.frame.setFrameShape(QtWidgets.QFrame.Box) + self.frame.setFrameShadow(QtWidgets.QFrame.Sunken) + self.frame.setObjectName("frame") + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame) + self.horizontalLayout_2.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.frame_6 = QtWidgets.QFrame(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_6.sizePolicy().hasHeightForWidth()) + self.frame_6.setSizePolicy(sizePolicy) + self.frame_6.setMinimumSize(QtCore.QSize(80, 50)) + self.frame_6.setObjectName("frame_6") + self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.frame_6) + self.verticalLayout_5.setContentsMargins(-1, 0, -1, 0) + self.verticalLayout_5.setSpacing(3) + self.verticalLayout_5.setObjectName("verticalLayout_5") + self.report_type_label = QtWidgets.QLabel(self.frame_6) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.report_type_label.sizePolicy().hasHeightForWidth()) + self.report_type_label.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.report_type_label.setFont(font) + self.report_type_label.setAlignment(QtCore.Qt.AlignCenter) + self.report_type_label.setObjectName("report_type_label") + self.verticalLayout_5.addWidget(self.report_type_label, 0, QtCore.Qt.AlignHCenter) + self.horizontalLayout_2.addWidget(self.frame_6) + self.frame_2 = QtWidgets.QFrame(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) + self.frame_2.setSizePolicy(sizePolicy) + self.frame_2.setObjectName("frame_2") + self.verticalLayout = QtWidgets.QVBoxLayout(self.frame_2) + self.verticalLayout.setContentsMargins(0, 0, 0, 0) + self.verticalLayout.setSpacing(6) + self.verticalLayout.setObjectName("verticalLayout") + self.file_frame = QtWidgets.QFrame(self.frame_2) + self.file_frame.setObjectName("file_frame") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.file_frame) + self.horizontalLayout_4.setContentsMargins(0, 0, 0, 0) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.file_label = QtWidgets.QLabel(self.file_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.file_label.sizePolicy().hasHeightForWidth()) + self.file_label.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setUnderline(True) + self.file_label.setFont(font) + self.file_label.setCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor)) + self.file_label.setStyleSheet("color: #0000EE") + self.file_label.setWordWrap(True) + self.file_label.setObjectName("file_label") + self.horizontalLayout_4.addWidget(self.file_label) + self.folder_button = QtWidgets.QPushButton(self.file_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.folder_button.sizePolicy().hasHeightForWidth()) + self.folder_button.setSizePolicy(sizePolicy) + self.folder_button.setText("") + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/folder_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.folder_button.setIcon(icon) + self.folder_button.setObjectName("folder_button") + self.horizontalLayout_4.addWidget(self.folder_button) + self.verticalLayout.addWidget(self.file_frame) + self.message_label = QtWidgets.QLabel(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.message_label.sizePolicy().hasHeightForWidth()) + self.message_label.setSizePolicy(sizePolicy) + self.message_label.setWordWrap(True) + self.message_label.setTextInteractionFlags(QtCore.Qt.LinksAccessibleByMouse|QtCore.Qt.TextSelectableByMouse) + self.message_label.setObjectName("message_label") + self.verticalLayout.addWidget(self.message_label) + self.horizontalLayout_2.addWidget(self.frame_2) + self.frame_4 = QtWidgets.QFrame(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_4.sizePolicy().hasHeightForWidth()) + self.frame_4.setSizePolicy(sizePolicy) + self.frame_4.setMinimumSize(QtCore.QSize(120, 50)) + self.frame_4.setObjectName("frame_4") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_4) + self.horizontalLayout_3.setContentsMargins(9, 0, -1, 0) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + self.success_label = QtWidgets.QLabel(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.success_label.sizePolicy().hasHeightForWidth()) + self.success_label.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.success_label.setFont(font) + self.success_label.setAlignment(QtCore.Qt.AlignCenter) + self.success_label.setObjectName("success_label") + self.horizontalLayout_3.addWidget(self.success_label) + self.retry_frame = QtWidgets.QFrame(self.frame_4) + self.retry_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.retry_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.retry_frame.setObjectName("retry_frame") + self.verticalLayout_7 = QtWidgets.QVBoxLayout(self.retry_frame) + self.verticalLayout_7.setContentsMargins(-1, 0, -1, -1) + self.verticalLayout_7.setObjectName("verticalLayout_7") + self.label_6 = QtWidgets.QLabel(self.retry_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_6.sizePolicy().hasHeightForWidth()) + self.label_6.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(False) + font.setWeight(50) + self.label_6.setFont(font) + self.label_6.setAlignment(QtCore.Qt.AlignCenter) + self.label_6.setObjectName("label_6") + self.verticalLayout_7.addWidget(self.label_6) + self.retry_check_box = QtWidgets.QCheckBox(self.retry_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.retry_check_box.sizePolicy().hasHeightForWidth()) + self.retry_check_box.setSizePolicy(sizePolicy) + self.retry_check_box.setText("") + self.retry_check_box.setObjectName("retry_check_box") + self.verticalLayout_7.addWidget(self.retry_check_box, 0, QtCore.Qt.AlignHCenter) + self.horizontalLayout_3.addWidget(self.retry_frame) + self.horizontalLayout_2.addWidget(self.frame_4) + self.horizontalLayout.addWidget(self.frame) + + self.retranslateUi(ReportResultWidget) + QtCore.QMetaObject.connectSlotsByName(ReportResultWidget) + + def retranslateUi(self, ReportResultWidget): + _translate = QtCore.QCoreApplication.translate + ReportResultWidget.setWindowTitle(_translate("ReportResultWidget", "Report Result")) + self.report_type_label.setText(_translate("ReportResultWidget", "TR_J1")) + self.file_label.setText(_translate("ReportResultWidget", "Saved as: Bleh.tsv")) + self.message_label.setText(_translate("ReportResultWidget", "No exception messages")) + self.success_label.setText(_translate("ReportResultWidget", "Failed!")) + self.label_6.setText(_translate("ReportResultWidget", "Retry")) +import Resources_rc diff --git a/ui/ReportResultWidget.ui b/ui/ReportResultWidget.ui new file mode 100644 index 0000000..1b4289d --- /dev/null +++ b/ui/ReportResultWidget.ui @@ -0,0 +1,356 @@ + + + ReportResultWidget + + + + 0 + 0 + 600 + 60 + + + + + 0 + 200 + + + + + 600 + 60 + + + + + 16777215 + 16777215 + + + + + 0 + 1000 + + + + + 9 + + + + Report Result + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 600 + 0 + + + + QFrame::Box + + + QFrame::Sunken + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 80 + 50 + + + + + 3 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 75 + true + + + + TR_J1 + + + Qt::AlignCenter + + + + + + + + + + + 0 + 0 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + true + + + + PointingHandCursor + + + color: #0000EE + + + Saved as: Bleh.tsv + + + true + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/folder_icon.png:/ui/resources/folder_icon.png + + + + + + + + + + + 0 + 0 + + + + No exception messages + + + true + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + + + + 0 + 0 + + + + + 120 + 50 + + + + + 9 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 75 + true + + + + Failed! + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 0 + + + + + + 0 + 0 + + + + + 50 + false + + + + Retry + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/ReportTypeDialog.py b/ui/ReportTypeDialog.py new file mode 100644 index 0000000..eb6cde2 --- /dev/null +++ b/ui/ReportTypeDialog.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'ReportTypeDialog.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_report_type_dialog(object): + def setupUi(self, report_type_dialog): + report_type_dialog.setObjectName("report_type_dialog") + report_type_dialog.resize(433, 155) + self.verticalLayout = QtWidgets.QVBoxLayout(report_type_dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.report_type_combobox = QtWidgets.QComboBox(report_type_dialog) + self.report_type_combobox.setObjectName("report_type_combobox") + self.verticalLayout.addWidget(self.report_type_combobox) + self.buttonBox = QtWidgets.QDialogButtonBox(report_type_dialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(report_type_dialog) + self.buttonBox.accepted.connect(report_type_dialog.accept) + self.buttonBox.rejected.connect(report_type_dialog.reject) + QtCore.QMetaObject.connectSlotsByName(report_type_dialog) + + def retranslateUi(self, report_type_dialog): + _translate = QtCore.QCoreApplication.translate + report_type_dialog.setWindowTitle(_translate("report_type_dialog", "Report Type")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + report_type_dialog = QtWidgets.QDialog() + ui = Ui_report_type_dialog() + ui.setupUi(report_type_dialog) + report_type_dialog.show() + sys.exit(app.exec_()) + diff --git a/ui/ReportTypeDialog.ui b/ui/ReportTypeDialog.ui new file mode 100644 index 0000000..d1203da --- /dev/null +++ b/ui/ReportTypeDialog.ui @@ -0,0 +1,67 @@ + + + report_type_dialog + + + + 0 + 0 + 433 + 155 + + + + Report Type + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + report_type_dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + report_type_dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/SearchAndClauseFrame.py b/ui/SearchAndClauseFrame.py new file mode 100644 index 0000000..4e90946 --- /dev/null +++ b/ui/SearchAndClauseFrame.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'SearchAndClauseFrame.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_search_and_clause_parameter_frame(object): + def setupUi(self, search_and_clause_parameter_frame): + search_and_clause_parameter_frame.setObjectName("search_and_clause_parameter_frame") + search_and_clause_parameter_frame.resize(542, 175) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(search_and_clause_parameter_frame.sizePolicy().hasHeightForWidth()) + search_and_clause_parameter_frame.setSizePolicy(sizePolicy) + search_and_clause_parameter_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + search_and_clause_parameter_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.gridLayout = QtWidgets.QGridLayout(search_and_clause_parameter_frame) + self.gridLayout.setObjectName("gridLayout") + self.search_or_clause_parameters_frame = QtWidgets.QFrame(search_and_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_or_clause_parameters_frame.sizePolicy().hasHeightForWidth()) + self.search_or_clause_parameters_frame.setSizePolicy(sizePolicy) + self.search_or_clause_parameters_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.search_or_clause_parameters_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.search_or_clause_parameters_frame.setObjectName("search_or_clause_parameters_frame") + self.verticalLayout_22 = QtWidgets.QVBoxLayout(self.search_or_clause_parameters_frame) + self.verticalLayout_22.setObjectName("verticalLayout_22") + self.gridLayout.addWidget(self.search_or_clause_parameters_frame, 0, 0, 1, 1) + self.search_add_or_clause_button = QtWidgets.QPushButton(search_and_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_add_or_clause_button.sizePolicy().hasHeightForWidth()) + self.search_add_or_clause_button.setSizePolicy(sizePolicy) + self.search_add_or_clause_button.setObjectName("search_add_or_clause_button") + self.gridLayout.addWidget(self.search_add_or_clause_button, 1, 0, 1, 1) + self.search_remove_and_clause_button = QtWidgets.QPushButton(search_and_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_remove_and_clause_button.sizePolicy().hasHeightForWidth()) + self.search_remove_and_clause_button.setSizePolicy(sizePolicy) + self.search_remove_and_clause_button.setObjectName("search_remove_and_clause_button") + self.gridLayout.addWidget(self.search_remove_and_clause_button, 0, 1, 1, 1) + + self.retranslateUi(search_and_clause_parameter_frame) + QtCore.QMetaObject.connectSlotsByName(search_and_clause_parameter_frame) + + def retranslateUi(self, search_and_clause_parameter_frame): + _translate = QtCore.QCoreApplication.translate + search_and_clause_parameter_frame.setWindowTitle(_translate("search_and_clause_parameter_frame", "Frame")) + self.search_add_or_clause_button.setText(_translate("search_and_clause_parameter_frame", "Add \"Or\" Clause")) + self.search_remove_and_clause_button.setText(_translate("search_and_clause_parameter_frame", "Remove \"And\" Clause")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + search_and_clause_parameter_frame = QtWidgets.QFrame() + ui = Ui_search_and_clause_parameter_frame() + ui.setupUi(search_and_clause_parameter_frame) + search_and_clause_parameter_frame.show() + sys.exit(app.exec_()) + diff --git a/ui/SearchAndClauseFrame.ui b/ui/SearchAndClauseFrame.ui new file mode 100644 index 0000000..5d0e03c --- /dev/null +++ b/ui/SearchAndClauseFrame.ui @@ -0,0 +1,76 @@ + + + search_and_clause_parameter_frame + + + + 0 + 0 + 542 + 175 + + + + + 0 + 0 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 0 + 0 + + + + Add "Or" Clause + + + + + + + + 0 + 0 + + + + Remove "And" Clause + + + + + + + + diff --git a/ui/SearchAndFrame.py b/ui/SearchAndFrame.py new file mode 100644 index 0000000..b8c18b7 --- /dev/null +++ b/ui/SearchAndFrame.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'SearchAndFrame.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_search_and_clause_parameter_frame(object): + def setupUi(self, search_and_clause_parameter_frame): + search_and_clause_parameter_frame.setObjectName("search_and_clause_parameter_frame") + search_and_clause_parameter_frame.resize(338, 256) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(search_and_clause_parameter_frame.sizePolicy().hasHeightForWidth()) + search_and_clause_parameter_frame.setSizePolicy(sizePolicy) + search_and_clause_parameter_frame.setFrameShape(QtWidgets.QFrame.NoFrame) + search_and_clause_parameter_frame.setFrameShadow(QtWidgets.QFrame.Plain) + self.verticalLayout = QtWidgets.QVBoxLayout(search_and_clause_parameter_frame) + self.verticalLayout.setObjectName("verticalLayout") + self.search_and_label = QtWidgets.QLabel(search_and_clause_parameter_frame) + self.search_and_label.setMaximumSize(QtCore.QSize(16777215, 22)) + font = QtGui.QFont() + font.setPointSize(14) + font.setBold(True) + font.setWeight(75) + self.search_and_label.setFont(font) + self.search_and_label.setAlignment(QtCore.Qt.AlignCenter) + self.search_and_label.setObjectName("search_and_label") + self.verticalLayout.addWidget(self.search_and_label) + self.search_and_clause_body_frame = QtWidgets.QFrame(search_and_clause_parameter_frame) + self.search_and_clause_body_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.search_and_clause_body_frame.setFrameShadow(QtWidgets.QFrame.Sunken) + self.search_and_clause_body_frame.setObjectName("search_and_clause_body_frame") + self.gridLayout = QtWidgets.QGridLayout(self.search_and_clause_body_frame) + self.gridLayout.setObjectName("gridLayout") + self.search_add_or_clause_button = QtWidgets.QPushButton(self.search_and_clause_body_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_add_or_clause_button.sizePolicy().hasHeightForWidth()) + self.search_add_or_clause_button.setSizePolicy(sizePolicy) + self.search_add_or_clause_button.setObjectName("search_add_or_clause_button") + self.gridLayout.addWidget(self.search_add_or_clause_button, 0, 1, 1, 1) + self.search_remove_and_clause_button = QtWidgets.QPushButton(self.search_and_clause_body_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_remove_and_clause_button.sizePolicy().hasHeightForWidth()) + self.search_remove_and_clause_button.setSizePolicy(sizePolicy) + self.search_remove_and_clause_button.setObjectName("search_remove_and_clause_button") + self.gridLayout.addWidget(self.search_remove_and_clause_button, 1, 1, 1, 1) + self.search_or_clause_parameters_frame = QtWidgets.QFrame(self.search_and_clause_body_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_or_clause_parameters_frame.sizePolicy().hasHeightForWidth()) + self.search_or_clause_parameters_frame.setSizePolicy(sizePolicy) + self.search_or_clause_parameters_frame.setFrameShape(QtWidgets.QFrame.NoFrame) + self.search_or_clause_parameters_frame.setFrameShadow(QtWidgets.QFrame.Plain) + self.search_or_clause_parameters_frame.setObjectName("search_or_clause_parameters_frame") + self.gridLayout_2 = QtWidgets.QGridLayout(self.search_or_clause_parameters_frame) + self.gridLayout_2.setObjectName("gridLayout_2") + self.gridLayout.addWidget(self.search_or_clause_parameters_frame, 0, 0, 2, 1) + self.verticalLayout.addWidget(self.search_and_clause_body_frame) + + self.retranslateUi(search_and_clause_parameter_frame) + QtCore.QMetaObject.connectSlotsByName(search_and_clause_parameter_frame) + + def retranslateUi(self, search_and_clause_parameter_frame): + _translate = QtCore.QCoreApplication.translate + search_and_clause_parameter_frame.setWindowTitle(_translate("search_and_clause_parameter_frame", "Frame")) + self.search_and_label.setText(_translate("search_and_clause_parameter_frame", "AND")) + self.search_add_or_clause_button.setText(_translate("search_and_clause_parameter_frame", "Add \"Or\"")) + self.search_remove_and_clause_button.setText(_translate("search_and_clause_parameter_frame", "Remove \"And\"")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + search_and_clause_parameter_frame = QtWidgets.QFrame() + ui = Ui_search_and_clause_parameter_frame() + ui.setupUi(search_and_clause_parameter_frame) + search_and_clause_parameter_frame.show() + sys.exit(app.exec_()) + diff --git a/ui/SearchAndFrame.ui b/ui/SearchAndFrame.ui new file mode 100644 index 0000000..6c641ed --- /dev/null +++ b/ui/SearchAndFrame.ui @@ -0,0 +1,111 @@ + + + search_and_clause_parameter_frame + + + + 0 + 0 + 338 + 256 + + + + + 0 + 0 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + 16777215 + 22 + + + + + 14 + 75 + true + + + + AND + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + QFrame::Sunken + + + + + + + 0 + 0 + + + + Add "Or" + + + + + + + + 0 + 0 + + + + Remove "And" + + + + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + + + + + + diff --git a/ui/SearchOrClauseFrame.py b/ui/SearchOrClauseFrame.py new file mode 100644 index 0000000..e0adb56 --- /dev/null +++ b/ui/SearchOrClauseFrame.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'SearchOrClauseFrame.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_search_or_clause_parameter_frame(object): + def setupUi(self, search_or_clause_parameter_frame): + search_or_clause_parameter_frame.setObjectName("search_or_clause_parameter_frame") + search_or_clause_parameter_frame.resize(755, 103) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(search_or_clause_parameter_frame.sizePolicy().hasHeightForWidth()) + search_or_clause_parameter_frame.setSizePolicy(sizePolicy) + search_or_clause_parameter_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + search_or_clause_parameter_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.gridLayout = QtWidgets.QGridLayout(search_or_clause_parameter_frame) + self.gridLayout.setObjectName("gridLayout") + self.search_value_parameter_lineedit = QtWidgets.QLineEdit(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_value_parameter_lineedit.sizePolicy().hasHeightForWidth()) + self.search_value_parameter_lineedit.setSizePolicy(sizePolicy) + self.search_value_parameter_lineedit.setObjectName("search_value_parameter_lineedit") + self.gridLayout.addWidget(self.search_value_parameter_lineedit, 0, 2, 1, 1) + self.search_comparison_parameter_combobox = QtWidgets.QComboBox(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_comparison_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.search_comparison_parameter_combobox.setSizePolicy(sizePolicy) + self.search_comparison_parameter_combobox.setObjectName("search_comparison_parameter_combobox") + self.gridLayout.addWidget(self.search_comparison_parameter_combobox, 0, 1, 1, 1) + self.search_field_parameter_combobox = QtWidgets.QComboBox(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_field_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.search_field_parameter_combobox.setSizePolicy(sizePolicy) + self.search_field_parameter_combobox.setObjectName("search_field_parameter_combobox") + self.gridLayout.addWidget(self.search_field_parameter_combobox, 0, 0, 1, 1) + self.search_remove_or_clause_button = QtWidgets.QPushButton(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_remove_or_clause_button.sizePolicy().hasHeightForWidth()) + self.search_remove_or_clause_button.setSizePolicy(sizePolicy) + self.search_remove_or_clause_button.setObjectName("search_remove_or_clause_button") + self.gridLayout.addWidget(self.search_remove_or_clause_button, 0, 3, 1, 1) + + self.retranslateUi(search_or_clause_parameter_frame) + QtCore.QMetaObject.connectSlotsByName(search_or_clause_parameter_frame) + + def retranslateUi(self, search_or_clause_parameter_frame): + _translate = QtCore.QCoreApplication.translate + search_or_clause_parameter_frame.setWindowTitle(_translate("search_or_clause_parameter_frame", "Frame")) + self.search_remove_or_clause_button.setText(_translate("search_or_clause_parameter_frame", "Remove \"Or\" Clause")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + search_or_clause_parameter_frame = QtWidgets.QFrame() + ui = Ui_search_or_clause_parameter_frame() + ui.setupUi(search_or_clause_parameter_frame) + search_or_clause_parameter_frame.show() + sys.exit(app.exec_()) + diff --git a/ui/SearchOrClauseFrame.ui b/ui/SearchOrClauseFrame.ui new file mode 100644 index 0000000..c0371ab --- /dev/null +++ b/ui/SearchOrClauseFrame.ui @@ -0,0 +1,76 @@ + + + search_or_clause_parameter_frame + + + + 0 + 0 + 755 + 103 + + + + + 0 + 0 + + + + Frame + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Remove "Or" Clause + + + + + + + + diff --git a/ui/SearchOrFrame.py b/ui/SearchOrFrame.py new file mode 100644 index 0000000..55eaaf0 --- /dev/null +++ b/ui/SearchOrFrame.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'SearchOrFrame.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_search_or_clause_parameter_frame(object): + def setupUi(self, search_or_clause_parameter_frame): + search_or_clause_parameter_frame.setObjectName("search_or_clause_parameter_frame") + search_or_clause_parameter_frame.resize(842, 101) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(search_or_clause_parameter_frame.sizePolicy().hasHeightForWidth()) + search_or_clause_parameter_frame.setSizePolicy(sizePolicy) + search_or_clause_parameter_frame.setFrameShape(QtWidgets.QFrame.NoFrame) + search_or_clause_parameter_frame.setFrameShadow(QtWidgets.QFrame.Plain) + self.horizontalLayout = QtWidgets.QHBoxLayout(search_or_clause_parameter_frame) + self.horizontalLayout.setObjectName("horizontalLayout") + self.search_or_label = QtWidgets.QLabel(search_or_clause_parameter_frame) + self.search_or_label.setMaximumSize(QtCore.QSize(16777215, 22)) + font = QtGui.QFont() + font.setPointSize(14) + font.setBold(True) + font.setWeight(75) + self.search_or_label.setFont(font) + self.search_or_label.setAutoFillBackground(False) + self.search_or_label.setAlignment(QtCore.Qt.AlignCenter) + self.search_or_label.setObjectName("search_or_label") + self.horizontalLayout.addWidget(self.search_or_label) + self.search_field_parameter_combobox = QtWidgets.QComboBox(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_field_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.search_field_parameter_combobox.setSizePolicy(sizePolicy) + self.search_field_parameter_combobox.setObjectName("search_field_parameter_combobox") + self.horizontalLayout.addWidget(self.search_field_parameter_combobox) + self.search_comparison_parameter_combobox = QtWidgets.QComboBox(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_comparison_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.search_comparison_parameter_combobox.setSizePolicy(sizePolicy) + self.search_comparison_parameter_combobox.setObjectName("search_comparison_parameter_combobox") + self.horizontalLayout.addWidget(self.search_comparison_parameter_combobox) + self.search_type_label = QtWidgets.QLabel(search_or_clause_parameter_frame) + self.search_type_label.setObjectName("search_type_label") + self.horizontalLayout.addWidget(self.search_type_label) + self.search_value_parameter_lineedit = QtWidgets.QLineEdit(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_value_parameter_lineedit.sizePolicy().hasHeightForWidth()) + self.search_value_parameter_lineedit.setSizePolicy(sizePolicy) + self.search_value_parameter_lineedit.setObjectName("search_value_parameter_lineedit") + self.horizontalLayout.addWidget(self.search_value_parameter_lineedit) + self.search_remove_or_clause_button = QtWidgets.QPushButton(search_or_clause_parameter_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_remove_or_clause_button.sizePolicy().hasHeightForWidth()) + self.search_remove_or_clause_button.setSizePolicy(sizePolicy) + self.search_remove_or_clause_button.setObjectName("search_remove_or_clause_button") + self.horizontalLayout.addWidget(self.search_remove_or_clause_button) + + self.retranslateUi(search_or_clause_parameter_frame) + QtCore.QMetaObject.connectSlotsByName(search_or_clause_parameter_frame) + + def retranslateUi(self, search_or_clause_parameter_frame): + _translate = QtCore.QCoreApplication.translate + search_or_clause_parameter_frame.setWindowTitle(_translate("search_or_clause_parameter_frame", "Frame")) + self.search_or_label.setText(_translate("search_or_clause_parameter_frame", "OR")) + self.search_type_label.setText(_translate("search_or_clause_parameter_frame", "Type")) + self.search_remove_or_clause_button.setText(_translate("search_or_clause_parameter_frame", "Remove \"Or\"")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + search_or_clause_parameter_frame = QtWidgets.QFrame() + ui = Ui_search_or_clause_parameter_frame() + ui.setupUi(search_or_clause_parameter_frame) + search_or_clause_parameter_frame.show() + sys.exit(app.exec_()) + diff --git a/ui/SearchOrFrame.ui b/ui/SearchOrFrame.ui new file mode 100644 index 0000000..97593d1 --- /dev/null +++ b/ui/SearchOrFrame.ui @@ -0,0 +1,109 @@ + + + search_or_clause_parameter_frame + + + + 0 + 0 + 842 + 101 + + + + + 0 + 0 + + + + Frame + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + + 16777215 + 22 + + + + + 14 + 75 + true + + + + false + + + OR + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + Type + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Remove "Or" + + + + + + + + diff --git a/ui/SearchTab.py b/ui/SearchTab.py new file mode 100644 index 0000000..d95044e --- /dev/null +++ b/ui/SearchTab.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'SearchTab.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_search_tab(object): + def setupUi(self, search_tab): + search_tab.setObjectName("search_tab") + search_tab.resize(913, 578) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/search_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + search_tab.setWindowIcon(icon) + self.verticalLayout = QtWidgets.QVBoxLayout(search_tab) + self.verticalLayout.setObjectName("verticalLayout") + self.search_parameters_frame = QtWidgets.QFrame(search_tab) + self.search_parameters_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.search_parameters_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.search_parameters_frame.setObjectName("search_parameters_frame") + self.gridLayout_2 = QtWidgets.QGridLayout(self.search_parameters_frame) + self.gridLayout_2.setObjectName("gridLayout_2") + self.search_and_clause_parameters_scrollarea = QtWidgets.QScrollArea(self.search_parameters_frame) + self.search_and_clause_parameters_scrollarea.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) + self.search_and_clause_parameters_scrollarea.setWidgetResizable(True) + self.search_and_clause_parameters_scrollarea.setObjectName("search_and_clause_parameters_scrollarea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 778, 176)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.scrollAreaWidgetContents.sizePolicy().hasHeightForWidth()) + self.scrollAreaWidgetContents.setSizePolicy(sizePolicy) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.formLayout_8 = QtWidgets.QFormLayout(self.scrollAreaWidgetContents) + self.formLayout_8.setObjectName("formLayout_8") + self.search_and_clause_parameters_scrollarea.setWidget(self.scrollAreaWidgetContents) + self.gridLayout_2.addWidget(self.search_and_clause_parameters_scrollarea, 4, 0, 1, 5) + self.search_start_year_parameter_dateedit = QtWidgets.QDateEdit(self.search_parameters_frame) + self.search_start_year_parameter_dateedit.setObjectName("search_start_year_parameter_dateedit") + self.gridLayout_2.addWidget(self.search_start_year_parameter_dateedit, 3, 1, 1, 1) + self.search_add_and_button = QtWidgets.QPushButton(self.search_parameters_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_add_and_button.sizePolicy().hasHeightForWidth()) + self.search_add_and_button.setSizePolicy(sizePolicy) + self.search_add_and_button.setObjectName("search_add_and_button") + self.gridLayout_2.addWidget(self.search_add_and_button, 3, 4, 1, 1) + spacerItem = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_2.addItem(spacerItem, 3, 3, 1, 1) + self.search_report_parameter_combobox = QtWidgets.QComboBox(self.search_parameters_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_report_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.search_report_parameter_combobox.setSizePolicy(sizePolicy) + self.search_report_parameter_combobox.setObjectName("search_report_parameter_combobox") + self.gridLayout_2.addWidget(self.search_report_parameter_combobox, 3, 0, 1, 1) + self.search_report_parameter_label = QtWidgets.QLabel(self.search_parameters_frame) + self.search_report_parameter_label.setObjectName("search_report_parameter_label") + self.gridLayout_2.addWidget(self.search_report_parameter_label, 1, 0, 1, 1) + self.search_start_year_parameter_label = QtWidgets.QLabel(self.search_parameters_frame) + self.search_start_year_parameter_label.setObjectName("search_start_year_parameter_label") + self.gridLayout_2.addWidget(self.search_start_year_parameter_label, 1, 1, 1, 1) + self.search_end_year_parameter_label = QtWidgets.QLabel(self.search_parameters_frame) + self.search_end_year_parameter_label.setObjectName("search_end_year_parameter_label") + self.gridLayout_2.addWidget(self.search_end_year_parameter_label, 1, 2, 1, 1) + self.search_end_year_parameter_dateedit = QtWidgets.QDateEdit(self.search_parameters_frame) + self.search_end_year_parameter_dateedit.setObjectName("search_end_year_parameter_dateedit") + self.gridLayout_2.addWidget(self.search_end_year_parameter_dateedit, 3, 2, 1, 1) + self.verticalLayout.addWidget(self.search_parameters_frame) + self.search_control_frame = QtWidgets.QFrame(search_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_control_frame.sizePolicy().hasHeightForWidth()) + self.search_control_frame.setSizePolicy(sizePolicy) + self.search_control_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.search_control_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.search_control_frame.setObjectName("search_control_frame") + self.gridLayout = QtWidgets.QGridLayout(self.search_control_frame) + self.gridLayout.setObjectName("gridLayout") + self.search_export_button = QtWidgets.QPushButton(self.search_control_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_export_button.sizePolicy().hasHeightForWidth()) + self.search_export_button.setSizePolicy(sizePolicy) + self.search_export_button.setObjectName("search_export_button") + self.gridLayout.addWidget(self.search_export_button, 1, 0, 1, 1) + self.search_button = QtWidgets.QPushButton(self.search_control_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_button.sizePolicy().hasHeightForWidth()) + self.search_button.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setBold(True) + font.setWeight(75) + self.search_button.setFont(font) + self.search_button.setObjectName("search_button") + self.gridLayout.addWidget(self.search_button, 0, 3, 2, 1) + self.search_import_button = QtWidgets.QPushButton(self.search_control_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_import_button.sizePolicy().hasHeightForWidth()) + self.search_import_button.setSizePolicy(sizePolicy) + self.search_import_button.setObjectName("search_import_button") + self.gridLayout.addWidget(self.search_import_button, 0, 0, 1, 1) + self.search_open_file_checkbox = QtWidgets.QCheckBox(self.search_control_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_open_file_checkbox.sizePolicy().hasHeightForWidth()) + self.search_open_file_checkbox.setSizePolicy(sizePolicy) + self.search_open_file_checkbox.setChecked(True) + self.search_open_file_checkbox.setTristate(False) + self.search_open_file_checkbox.setObjectName("search_open_file_checkbox") + self.gridLayout.addWidget(self.search_open_file_checkbox, 0, 2, 1, 1) + self.search_open_folder_checkbox = QtWidgets.QCheckBox(self.search_control_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_open_folder_checkbox.sizePolicy().hasHeightForWidth()) + self.search_open_folder_checkbox.setSizePolicy(sizePolicy) + self.search_open_folder_checkbox.setObjectName("search_open_folder_checkbox") + self.gridLayout.addWidget(self.search_open_folder_checkbox, 1, 2, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) + self.gridLayout.addItem(spacerItem1, 0, 1, 2, 1) + self.verticalLayout.addWidget(self.search_control_frame) + + self.retranslateUi(search_tab) + QtCore.QMetaObject.connectSlotsByName(search_tab) + + def retranslateUi(self, search_tab): + _translate = QtCore.QCoreApplication.translate + search_tab.setWindowTitle(_translate("search_tab", "Search")) + self.search_start_year_parameter_dateedit.setDisplayFormat(_translate("search_tab", "yyyy")) + self.search_add_and_button.setText(_translate("search_tab", "Add \"And\"")) + self.search_report_parameter_label.setText(_translate("search_tab", "Report")) + self.search_start_year_parameter_label.setText(_translate("search_tab", "Start Year")) + self.search_end_year_parameter_label.setText(_translate("search_tab", "End Year")) + self.search_end_year_parameter_dateedit.setDisplayFormat(_translate("search_tab", "yyyy")) + self.search_export_button.setText(_translate("search_tab", "Export Search")) + self.search_button.setText(_translate("search_tab", "Search")) + self.search_import_button.setText(_translate("search_tab", "Import Search")) + self.search_open_file_checkbox.setText(_translate("search_tab", "Open File")) + self.search_open_folder_checkbox.setText(_translate("search_tab", "Open Folder")) + +import Resources_rc + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + search_tab = QtWidgets.QWidget() + ui = Ui_search_tab() + ui.setupUi(search_tab) + search_tab.show() + sys.exit(app.exec_()) + diff --git a/ui/SearchTab.ui b/ui/SearchTab.ui new file mode 100644 index 0000000..9db499f --- /dev/null +++ b/ui/SearchTab.ui @@ -0,0 +1,248 @@ + + + search_tab + + + + 0 + 0 + 913 + 578 + + + + Search + + + + :/ui/resources/tab_icons/search_icon.png:/ui/resources/tab_icons/search_icon.png + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Qt::ScrollBarAlwaysOn + + + true + + + + + 0 + 0 + 778 + 176 + + + + + 0 + 0 + + + + + + + + + + yyyy + + + + + + + + 0 + 0 + + + + Add "And" + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + + + + Report + + + + + + + Start Year + + + + + + + End Year + + + + + + + yyyy + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + Export Search + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Search + + + + + + + + 0 + 0 + + + + Import Search + + + + + + + + 0 + 0 + + + + Open File + + + true + + + false + + + + + + + + 0 + 0 + + + + Open Folder + + + + + + + Qt::Horizontal + + + + 0 + 0 + + + + + + + + + + + + + + diff --git a/ui/SettingsTab.py b/ui/SettingsTab.py new file mode 100644 index 0000000..69c8a71 --- /dev/null +++ b/ui/SettingsTab.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'SettingsTab.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_settings_tab(object): + def setupUi(self, settings_tab): + settings_tab.setObjectName("settings_tab") + settings_tab.resize(852, 507) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/settings_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + settings_tab.setWindowIcon(icon) + self.horizontalLayout_2 = QtWidgets.QHBoxLayout(settings_tab) + self.horizontalLayout_2.setObjectName("horizontalLayout_2") + self.scrollArea = QtWidgets.QScrollArea(settings_tab) + self.scrollArea.setFocusPolicy(QtCore.Qt.StrongFocus) + self.scrollArea.setWidgetResizable(True) + self.scrollArea.setObjectName("scrollArea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 815, 596)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.scrollAreaWidgetContents) + self.horizontalLayout_3.setContentsMargins(0, 0, 0, 2) + self.horizontalLayout_3.setObjectName("horizontalLayout_3") + spacerItem = QtWidgets.QSpacerItem(120, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_3.addItem(spacerItem) + self.frame_4 = QtWidgets.QFrame(self.scrollAreaWidgetContents) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_4.sizePolicy().hasHeightForWidth()) + self.frame_4.setSizePolicy(sizePolicy) + self.frame_4.setObjectName("frame_4") + self.verticalLayout = QtWidgets.QVBoxLayout(self.frame_4) + self.verticalLayout.setObjectName("verticalLayout") + self.frame_2 = QtWidgets.QFrame(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) + self.frame_2.setSizePolicy(sizePolicy) + self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_2.setObjectName("frame_2") + self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.frame_2) + self.verticalLayout_2.setObjectName("verticalLayout_2") + self.label_27 = QtWidgets.QLabel(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_27.sizePolicy().hasHeightForWidth()) + self.label_27.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.label_27.setFont(font) + self.label_27.setObjectName("label_27") + self.verticalLayout_2.addWidget(self.label_27, 0, QtCore.Qt.AlignHCenter) + self.frame_3 = QtWidgets.QFrame(self.frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_3.sizePolicy().hasHeightForWidth()) + self.frame_3.setSizePolicy(sizePolicy) + self.frame_3.setObjectName("frame_3") + self.gridLayout = QtWidgets.QGridLayout(self.frame_3) + self.gridLayout.setHorizontalSpacing(10) + self.gridLayout.setObjectName("gridLayout") + self.label = QtWidgets.QLabel(self.frame_3) + self.label.setObjectName("label") + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.show_debug_check_box = QtWidgets.QCheckBox(self.frame_3) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.show_debug_check_box.sizePolicy().hasHeightForWidth()) + self.show_debug_check_box.setSizePolicy(sizePolicy) + self.show_debug_check_box.setText("") + self.show_debug_check_box.setObjectName("show_debug_check_box") + self.gridLayout.addWidget(self.show_debug_check_box, 0, 1, 1, 1) + self.verticalLayout_2.addWidget(self.frame_3) + self.verticalLayout.addWidget(self.frame_2) + self.frame_33 = QtWidgets.QFrame(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_33.sizePolicy().hasHeightForWidth()) + self.frame_33.setSizePolicy(sizePolicy) + self.frame_33.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_33.setObjectName("frame_33") + self.verticalLayout_19 = QtWidgets.QVBoxLayout(self.frame_33) + self.verticalLayout_19.setObjectName("verticalLayout_19") + self.label_24 = QtWidgets.QLabel(self.frame_33) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_24.sizePolicy().hasHeightForWidth()) + self.label_24.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.label_24.setFont(font) + self.label_24.setObjectName("label_24") + self.verticalLayout_19.addWidget(self.label_24, 0, QtCore.Qt.AlignHCenter) + self.frame_34 = QtWidgets.QFrame(self.frame_33) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_34.sizePolicy().hasHeightForWidth()) + self.frame_34.setSizePolicy(sizePolicy) + self.frame_34.setObjectName("frame_34") + self.gridLayout_11 = QtWidgets.QGridLayout(self.frame_34) + self.gridLayout_11.setHorizontalSpacing(10) + self.gridLayout_11.setObjectName("gridLayout_11") + self.other_directory_help_button = QtWidgets.QPushButton(self.frame_34) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.other_directory_help_button.sizePolicy().hasHeightForWidth()) + self.other_directory_help_button.setSizePolicy(sizePolicy) + self.other_directory_help_button.setText("") + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(":/ui/resources/help_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.other_directory_help_button.setIcon(icon1) + self.other_directory_help_button.setObjectName("other_directory_help_button") + self.gridLayout_11.addWidget(self.other_directory_help_button, 1, 3, 1, 1) + self.other_directory_button = QtWidgets.QPushButton(self.frame_34) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(":/ui/resources/folder_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.other_directory_button.setIcon(icon2) + self.other_directory_button.setObjectName("other_directory_button") + self.gridLayout_11.addWidget(self.other_directory_button, 1, 2, 1, 1) + self.request_timeout_help_button = QtWidgets.QPushButton(self.frame_34) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.request_timeout_help_button.sizePolicy().hasHeightForWidth()) + self.request_timeout_help_button.setSizePolicy(sizePolicy) + self.request_timeout_help_button.setText("") + self.request_timeout_help_button.setIcon(icon1) + self.request_timeout_help_button.setObjectName("request_timeout_help_button") + self.gridLayout_11.addWidget(self.request_timeout_help_button, 6, 2, 1, 1) + self.concurrent_vendors_help_button = QtWidgets.QPushButton(self.frame_34) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.concurrent_vendors_help_button.sizePolicy().hasHeightForWidth()) + self.concurrent_vendors_help_button.setSizePolicy(sizePolicy) + self.concurrent_vendors_help_button.setText("") + self.concurrent_vendors_help_button.setIcon(icon1) + self.concurrent_vendors_help_button.setObjectName("concurrent_vendors_help_button") + self.gridLayout_11.addWidget(self.concurrent_vendors_help_button, 7, 2, 1, 1) + self.label_37 = QtWidgets.QLabel(self.frame_34) + self.label_37.setObjectName("label_37") + self.gridLayout_11.addWidget(self.label_37, 6, 0, 1, 1) + self.user_agent_edit = QtWidgets.QLineEdit(self.frame_34) + self.user_agent_edit.setObjectName("user_agent_edit") + self.gridLayout_11.addWidget(self.user_agent_edit, 10, 1, 1, 1) + self.other_directory_edit = QtWidgets.QLineEdit(self.frame_34) + self.other_directory_edit.setReadOnly(True) + self.other_directory_edit.setObjectName("other_directory_edit") + self.gridLayout_11.addWidget(self.other_directory_edit, 1, 1, 1, 1) + self.label_32 = QtWidgets.QLabel(self.frame_34) + self.label_32.setObjectName("label_32") + self.gridLayout_11.addWidget(self.label_32, 8, 0, 1, 1) + self.yearly_directory_help_button = QtWidgets.QPushButton(self.frame_34) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.yearly_directory_help_button.sizePolicy().hasHeightForWidth()) + self.yearly_directory_help_button.setSizePolicy(sizePolicy) + self.yearly_directory_help_button.setText("") + self.yearly_directory_help_button.setIcon(icon1) + self.yearly_directory_help_button.setObjectName("yearly_directory_help_button") + self.gridLayout_11.addWidget(self.yearly_directory_help_button, 0, 3, 1, 1) + self.request_interval_spin_box = QtWidgets.QSpinBox(self.frame_34) + self.request_interval_spin_box.setMaximum(999) + self.request_interval_spin_box.setObjectName("request_interval_spin_box") + self.gridLayout_11.addWidget(self.request_interval_spin_box, 5, 1, 1, 1) + self.label_25 = QtWidgets.QLabel(self.frame_34) + self.label_25.setObjectName("label_25") + self.gridLayout_11.addWidget(self.label_25, 0, 0, 1, 1) + self.request_interval_help_button = QtWidgets.QPushButton(self.frame_34) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.request_interval_help_button.sizePolicy().hasHeightForWidth()) + self.request_interval_help_button.setSizePolicy(sizePolicy) + self.request_interval_help_button.setText("") + self.request_interval_help_button.setIcon(icon1) + self.request_interval_help_button.setObjectName("request_interval_help_button") + self.gridLayout_11.addWidget(self.request_interval_help_button, 5, 2, 1, 1) + self.request_timeout_spin_box = QtWidgets.QSpinBox(self.frame_34) + self.request_timeout_spin_box.setMaximum(9999) + self.request_timeout_spin_box.setSingleStep(30) + self.request_timeout_spin_box.setObjectName("request_timeout_spin_box") + self.gridLayout_11.addWidget(self.request_timeout_spin_box, 6, 1, 1, 1) + self.yearly_directory_button = QtWidgets.QPushButton(self.frame_34) + self.yearly_directory_button.setIcon(icon2) + self.yearly_directory_button.setObjectName("yearly_directory_button") + self.gridLayout_11.addWidget(self.yearly_directory_button, 0, 2, 1, 1) + self.concurrent_reports_spin_box = QtWidgets.QSpinBox(self.frame_34) + self.concurrent_reports_spin_box.setMaximum(999) + self.concurrent_reports_spin_box.setObjectName("concurrent_reports_spin_box") + self.gridLayout_11.addWidget(self.concurrent_reports_spin_box, 8, 1, 1, 1) + self.label_31 = QtWidgets.QLabel(self.frame_34) + self.label_31.setObjectName("label_31") + self.gridLayout_11.addWidget(self.label_31, 7, 0, 1, 1) + self.yearly_directory_edit = QtWidgets.QLineEdit(self.frame_34) + self.yearly_directory_edit.setReadOnly(True) + self.yearly_directory_edit.setObjectName("yearly_directory_edit") + self.gridLayout_11.addWidget(self.yearly_directory_edit, 0, 1, 1, 1) + self.label_73 = QtWidgets.QLabel(self.frame_34) + self.label_73.setObjectName("label_73") + self.gridLayout_11.addWidget(self.label_73, 10, 0, 1, 1) + self.user_agent_help_button = QtWidgets.QPushButton(self.frame_34) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.user_agent_help_button.sizePolicy().hasHeightForWidth()) + self.user_agent_help_button.setSizePolicy(sizePolicy) + self.user_agent_help_button.setText("") + self.user_agent_help_button.setIcon(icon1) + self.user_agent_help_button.setObjectName("user_agent_help_button") + self.gridLayout_11.addWidget(self.user_agent_help_button, 10, 2, 1, 1) + self.concurrent_reports_help_button = QtWidgets.QPushButton(self.frame_34) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.concurrent_reports_help_button.sizePolicy().hasHeightForWidth()) + self.concurrent_reports_help_button.setSizePolicy(sizePolicy) + self.concurrent_reports_help_button.setText("") + self.concurrent_reports_help_button.setIcon(icon1) + self.concurrent_reports_help_button.setObjectName("concurrent_reports_help_button") + self.gridLayout_11.addWidget(self.concurrent_reports_help_button, 8, 2, 1, 1) + self.concurrent_vendors_spin_box = QtWidgets.QSpinBox(self.frame_34) + self.concurrent_vendors_spin_box.setMaximum(999) + self.concurrent_vendors_spin_box.setObjectName("concurrent_vendors_spin_box") + self.gridLayout_11.addWidget(self.concurrent_vendors_spin_box, 7, 1, 1, 1) + self.label_29 = QtWidgets.QLabel(self.frame_34) + self.label_29.setObjectName("label_29") + self.gridLayout_11.addWidget(self.label_29, 1, 0, 1, 1) + self.label_30 = QtWidgets.QLabel(self.frame_34) + self.label_30.setObjectName("label_30") + self.gridLayout_11.addWidget(self.label_30, 5, 0, 1, 1) + self.verticalLayout_19.addWidget(self.frame_34) + self.verticalLayout.addWidget(self.frame_33) + self.settings_costs_frame = QtWidgets.QFrame(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.settings_costs_frame.sizePolicy().hasHeightForWidth()) + self.settings_costs_frame.setSizePolicy(sizePolicy) + self.settings_costs_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.settings_costs_frame.setFrameShadow(QtWidgets.QFrame.Plain) + self.settings_costs_frame.setObjectName("settings_costs_frame") + self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.settings_costs_frame) + self.verticalLayout_3.setObjectName("verticalLayout_3") + self.settings_costs_label = QtWidgets.QLabel(self.settings_costs_frame) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.settings_costs_label.setFont(font) + self.settings_costs_label.setAlignment(QtCore.Qt.AlignCenter) + self.settings_costs_label.setObjectName("settings_costs_label") + self.verticalLayout_3.addWidget(self.settings_costs_label) + self.settings_costs_items_frame = QtWidgets.QFrame(self.settings_costs_frame) + self.settings_costs_items_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.settings_costs_items_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.settings_costs_items_frame.setObjectName("settings_costs_items_frame") + self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.settings_costs_items_frame) + self.horizontalLayout_4.setObjectName("horizontalLayout_4") + self.settings_costs_default_currency_label = QtWidgets.QLabel(self.settings_costs_items_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.settings_costs_default_currency_label.sizePolicy().hasHeightForWidth()) + self.settings_costs_default_currency_label.setSizePolicy(sizePolicy) + self.settings_costs_default_currency_label.setObjectName("settings_costs_default_currency_label") + self.horizontalLayout_4.addWidget(self.settings_costs_default_currency_label) + self.settings_costs_default_currency_combobox = QtWidgets.QComboBox(self.settings_costs_items_frame) + self.settings_costs_default_currency_combobox.setEditable(True) + self.settings_costs_default_currency_combobox.setObjectName("settings_costs_default_currency_combobox") + self.horizontalLayout_4.addWidget(self.settings_costs_default_currency_combobox) + self.default_currency_help_button = QtWidgets.QPushButton(self.settings_costs_items_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.default_currency_help_button.sizePolicy().hasHeightForWidth()) + self.default_currency_help_button.setSizePolicy(sizePolicy) + self.default_currency_help_button.setText("") + self.default_currency_help_button.setIcon(icon1) + self.default_currency_help_button.setObjectName("default_currency_help_button") + self.horizontalLayout_4.addWidget(self.default_currency_help_button) + self.verticalLayout_3.addWidget(self.settings_costs_items_frame) + self.verticalLayout.addWidget(self.settings_costs_frame) + self.save_button = QtWidgets.QPushButton(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.save_button.sizePolicy().hasHeightForWidth()) + self.save_button.setSizePolicy(sizePolicy) + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap(":/ui/resources/save_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.save_button.setIcon(icon3) + self.save_button.setObjectName("save_button") + self.verticalLayout.addWidget(self.save_button, 0, QtCore.Qt.AlignHCenter) + self.settings_search_frame = QtWidgets.QFrame(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.settings_search_frame.sizePolicy().hasHeightForWidth()) + self.settings_search_frame.setSizePolicy(sizePolicy) + self.settings_search_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.settings_search_frame.setObjectName("settings_search_frame") + self.verticalLayout_20 = QtWidgets.QVBoxLayout(self.settings_search_frame) + self.verticalLayout_20.setObjectName("verticalLayout_20") + self.settings_search_label = QtWidgets.QLabel(self.settings_search_frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.settings_search_label.sizePolicy().hasHeightForWidth()) + self.settings_search_label.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Segoe UI") + font.setPointSize(11) + font.setBold(True) + font.setWeight(75) + self.settings_search_label.setFont(font) + self.settings_search_label.setObjectName("settings_search_label") + self.verticalLayout_20.addWidget(self.settings_search_label, 0, QtCore.Qt.AlignHCenter) + self.settings_rebuild_database_button = QtWidgets.QPushButton(self.settings_search_frame) + self.settings_rebuild_database_button.setObjectName("settings_rebuild_database_button") + self.verticalLayout_20.addWidget(self.settings_rebuild_database_button) + self.verticalLayout.addWidget(self.settings_search_frame) + self.frame = QtWidgets.QFrame(self.frame_4) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setObjectName("frame") + self.horizontalLayout = QtWidgets.QHBoxLayout(self.frame) + self.horizontalLayout.setObjectName("horizontalLayout") + self.verticalLayout.addWidget(self.frame) + self.horizontalLayout_3.addWidget(self.frame_4) + spacerItem1 = QtWidgets.QSpacerItem(120, 20, QtWidgets.QSizePolicy.Maximum, QtWidgets.QSizePolicy.Minimum) + self.horizontalLayout_3.addItem(spacerItem1) + self.scrollArea.setWidget(self.scrollAreaWidgetContents) + self.horizontalLayout_2.addWidget(self.scrollArea) + + self.retranslateUi(settings_tab) + QtCore.QMetaObject.connectSlotsByName(settings_tab) + + def retranslateUi(self, settings_tab): + _translate = QtCore.QCoreApplication.translate + settings_tab.setWindowTitle(_translate("settings_tab", "Settings")) + self.label_27.setText(_translate("settings_tab", "General")) + self.label.setText(_translate("settings_tab", "Show Debug Messages in Console Window")) + self.label_24.setText(_translate("settings_tab", "Reports")) + self.other_directory_button.setText(_translate("settings_tab", "Choose")) + self.label_37.setText(_translate("settings_tab", "Request Timeout")) + self.label_32.setText(_translate("settings_tab", "Concurrent Reports")) + self.label_25.setText(_translate("settings_tab", "Yearly Reports Directory")) + self.yearly_directory_button.setText(_translate("settings_tab", "Choose")) + self.label_31.setText(_translate("settings_tab", "Concurrent Vendors")) + self.label_73.setText(_translate("settings_tab", "User Agent")) + self.label_29.setText(_translate("settings_tab", "Other Reports Directory")) + self.label_30.setText(_translate("settings_tab", "Report Request Interval")) + self.settings_costs_label.setText(_translate("settings_tab", "Costs")) + self.settings_costs_default_currency_label.setText(_translate("settings_tab", "Default Currency")) + self.save_button.setText(_translate("settings_tab", "Save All Changes")) + self.settings_search_label.setText(_translate("settings_tab", "Search")) + self.settings_rebuild_database_button.setText(_translate("settings_tab", "Rebuild Database")) + +import Resources_rc diff --git a/ui/SettingsTab.ui b/ui/SettingsTab.ui new file mode 100644 index 0000000..a93dfb2 --- /dev/null +++ b/ui/SettingsTab.ui @@ -0,0 +1,621 @@ + + + settings_tab + + + + 0 + 0 + 852 + 507 + + + + Settings + + + + :/ui/resources/tab_icons/settings_icon.png:/ui/resources/tab_icons/settings_icon.png + + + + + + Qt::StrongFocus + + + true + + + + + 0 + 0 + 815 + 596 + + + + + 0 + + + 0 + + + 0 + + + 2 + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 120 + 20 + + + + + + + + + 0 + 0 + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + Segoe UI + 11 + 75 + true + + + + General + + + + + + + + 0 + 0 + + + + + 10 + + + + + Show Debug Messages in Console Window + + + + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + Segoe UI + 11 + 75 + true + + + + Reports + + + + + + + + 0 + 0 + + + + + 10 + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + Choose + + + + :/ui/resources/folder_icon.png:/ui/resources/folder_icon.png + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + Request Timeout + + + + + + + + + + true + + + + + + + Concurrent Reports + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + 999 + + + + + + + Yearly Reports Directory + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + 9999 + + + 30 + + + + + + + Choose + + + + :/ui/resources/folder_icon.png:/ui/resources/folder_icon.png + + + + + + + 999 + + + + + + + Concurrent Vendors + + + + + + + true + + + + + + + User Agent + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + 999 + + + + + + + Other Reports Directory + + + + + + + Report Request Interval + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + + + + Segoe UI + 11 + 75 + true + + + + Costs + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + Default Currency + + + + + + + true + + + + + + + + 0 + 0 + + + + + + + + :/ui/resources/help_icon.png:/ui/resources/help_icon.png + + + + + + + + + + + + + + 0 + 0 + + + + Save All Changes + + + + :/ui/resources/save_icon.png:/ui/resources/save_icon.png + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + 0 + 0 + + + + + Segoe UI + 11 + 75 + true + + + + Search + + + + + + + Rebuild Database + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Maximum + + + + 120 + 20 + + + + + + + + + + + + + + + diff --git a/ui/UpdateDatabaseProgressDialog.py b/ui/UpdateDatabaseProgressDialog.py new file mode 100644 index 0000000..cf5a775 --- /dev/null +++ b/ui/UpdateDatabaseProgressDialog.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'UpdateDatabaseProgressDialog.ui' +# +# Created by: PyQt5 UI code generator 5.9.2 +# +# WARNING! All changes made in this file will be lost! + +from PyQt5 import QtCore, QtGui, QtWidgets + +class Ui_update_database_dialog(object): + def setupUi(self, update_database_dialog): + update_database_dialog.setObjectName("update_database_dialog") + update_database_dialog.resize(433, 396) + self.verticalLayout = QtWidgets.QVBoxLayout(update_database_dialog) + self.verticalLayout.setObjectName("verticalLayout") + self.status_label = QtWidgets.QLabel(update_database_dialog) + self.status_label.setAlignment(QtCore.Qt.AlignCenter) + self.status_label.setObjectName("status_label") + self.verticalLayout.addWidget(self.status_label) + self.progressbar = QtWidgets.QProgressBar(update_database_dialog) + self.progressbar.setMaximum(1) + self.progressbar.setProperty("value", 0) + self.progressbar.setObjectName("progressbar") + self.verticalLayout.addWidget(self.progressbar) + self.scrollarea = QtWidgets.QScrollArea(update_database_dialog) + self.scrollarea.setWidgetResizable(True) + self.scrollarea.setObjectName("scrollarea") + self.scrollAreaWidgetContents = QtWidgets.QWidget() + self.scrollAreaWidgetContents.setGeometry(QtCore.QRect(0, 0, 387, 174)) + self.scrollAreaWidgetContents.setObjectName("scrollAreaWidgetContents") + self.scrollarea.setWidget(self.scrollAreaWidgetContents) + self.verticalLayout.addWidget(self.scrollarea) + self.buttonBox = QtWidgets.QDialogButtonBox(update_database_dialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtWidgets.QDialogButtonBox.Cancel|QtWidgets.QDialogButtonBox.Ok) + self.buttonBox.setObjectName("buttonBox") + self.verticalLayout.addWidget(self.buttonBox) + + self.retranslateUi(update_database_dialog) + self.buttonBox.accepted.connect(update_database_dialog.accept) + self.buttonBox.rejected.connect(update_database_dialog.reject) + QtCore.QMetaObject.connectSlotsByName(update_database_dialog) + + def retranslateUi(self, update_database_dialog): + _translate = QtCore.QCoreApplication.translate + update_database_dialog.setWindowTitle(_translate("update_database_dialog", "Update Database")) + self.status_label.setText(_translate("update_database_dialog", "Status")) + + +if __name__ == "__main__": + import sys + app = QtWidgets.QApplication(sys.argv) + update_database_dialog = QtWidgets.QDialog() + ui = Ui_update_database_dialog() + ui.setupUi(update_database_dialog) + update_database_dialog.show() + sys.exit(app.exec_()) + diff --git a/ui/UpdateDatabaseProgressDialog.ui b/ui/UpdateDatabaseProgressDialog.ui new file mode 100644 index 0000000..63d48a7 --- /dev/null +++ b/ui/UpdateDatabaseProgressDialog.ui @@ -0,0 +1,101 @@ + + + update_database_dialog + + + + 0 + 0 + 433 + 396 + + + + Update Database + + + + + + Status + + + Qt::AlignCenter + + + + + + + 1 + + + 0 + + + + + + + true + + + + + 0 + 0 + 387 + 174 + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + update_database_dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + update_database_dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/ui/VendorResultsWidget.py b/ui/VendorResultsWidget.py new file mode 100644 index 0000000..bae9884 --- /dev/null +++ b/ui/VendorResultsWidget.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'VendorResultsWidget.ui' +# +# Created by: PyQt5 UI code generator 5.12.3 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_VendorResultsWidget(object): + def setupUi(self, VendorResultsWidget): + VendorResultsWidget.setObjectName("VendorResultsWidget") + VendorResultsWidget.resize(600, 59) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(VendorResultsWidget.sizePolicy().hasHeightForWidth()) + VendorResultsWidget.setSizePolicy(sizePolicy) + VendorResultsWidget.setMinimumSize(QtCore.QSize(600, 0)) + font = QtGui.QFont() + font.setPointSize(9) + VendorResultsWidget.setFont(font) + self.gridLayout = QtWidgets.QGridLayout(VendorResultsWidget) + self.gridLayout.setContentsMargins(0, 0, 0, 0) + self.gridLayout.setObjectName("gridLayout") + self.frame = QtWidgets.QFrame(VendorResultsWidget) + self.frame.setFrameShape(QtWidgets.QFrame.Box) + self.frame.setFrameShadow(QtWidgets.QFrame.Sunken) + self.frame.setObjectName("frame") + self.gridLayout_2 = QtWidgets.QGridLayout(self.frame) + self.gridLayout_2.setContentsMargins(3, 3, 3, 3) + self.gridLayout_2.setObjectName("gridLayout_2") + self.vendor_label = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.vendor_label.sizePolicy().hasHeightForWidth()) + self.vendor_label.setSizePolicy(sizePolicy) + self.vendor_label.setObjectName("vendor_label") + self.gridLayout_2.addWidget(self.vendor_label, 0, 1, 1, 1) + self.results_frame = QtWidgets.QFrame(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.results_frame.sizePolicy().hasHeightForWidth()) + self.results_frame.setSizePolicy(sizePolicy) + self.results_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.results_frame.setObjectName("results_frame") + self.verticalLayout = QtWidgets.QVBoxLayout(self.results_frame) + self.verticalLayout.setObjectName("verticalLayout") + self.gridLayout_2.addWidget(self.results_frame, 1, 1, 1, 6) + self.collapse_button = QtWidgets.QPushButton(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.collapse_button.sizePolicy().hasHeightForWidth()) + self.collapse_button.setSizePolicy(sizePolicy) + self.collapse_button.setMinimumSize(QtCore.QSize(70, 0)) + self.collapse_button.setMaximumSize(QtCore.QSize(70, 16777215)) + self.collapse_button.setObjectName("collapse_button") + self.gridLayout_2.addWidget(self.collapse_button, 0, 6, 1, 1) + self.status_label = QtWidgets.QLabel(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.status_label.sizePolicy().hasHeightForWidth()) + self.status_label.setSizePolicy(sizePolicy) + self.status_label.setMinimumSize(QtCore.QSize(100, 0)) + self.status_label.setAlignment(QtCore.Qt.AlignCenter) + self.status_label.setObjectName("status_label") + self.gridLayout_2.addWidget(self.status_label, 0, 4, 1, 1) + self.expand_button = QtWidgets.QPushButton(self.frame) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.expand_button.sizePolicy().hasHeightForWidth()) + self.expand_button.setSizePolicy(sizePolicy) + self.expand_button.setMinimumSize(QtCore.QSize(70, 0)) + self.expand_button.setMaximumSize(QtCore.QSize(70, 16777215)) + self.expand_button.setObjectName("expand_button") + self.gridLayout_2.addWidget(self.expand_button, 0, 5, 1, 1) + self.gridLayout.addWidget(self.frame, 1, 0, 1, 1) + + self.retranslateUi(VendorResultsWidget) + QtCore.QMetaObject.connectSlotsByName(VendorResultsWidget) + + def retranslateUi(self, VendorResultsWidget): + _translate = QtCore.QCoreApplication.translate + VendorResultsWidget.setWindowTitle(_translate("VendorResultsWidget", "Form")) + self.vendor_label.setText(_translate("VendorResultsWidget", "Bioone")) + self.collapse_button.setText(_translate("VendorResultsWidget", "Collapse")) + self.status_label.setText(_translate("VendorResultsWidget", "Success")) + self.expand_button.setText(_translate("VendorResultsWidget", "Expand")) diff --git a/ui/VendorResultsWidget.ui b/ui/VendorResultsWidget.ui new file mode 100644 index 0000000..fcdb705 --- /dev/null +++ b/ui/VendorResultsWidget.ui @@ -0,0 +1,173 @@ + + + VendorResultsWidget + + + + 0 + 0 + 600 + 59 + + + + + 0 + 0 + + + + + 600 + 0 + + + + + 9 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Box + + + QFrame::Sunken + + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 0 + 0 + + + + Bioone + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + + + + + + + 0 + 0 + + + + + 70 + 0 + + + + + 70 + 16777215 + + + + Collapse + + + + + + + + 0 + 0 + + + + + 100 + 0 + + + + Success + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + + 70 + 0 + + + + + 70 + 16777215 + + + + Expand + + + + + + + + + + + diff --git a/ui/VisualTab.py b/ui/VisualTab.py new file mode 100644 index 0000000..6d2b79a --- /dev/null +++ b/ui/VisualTab.py @@ -0,0 +1,374 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'VisualTab.ui' +# +# Created by: PyQt5 UI code generator 5.14.1 +# +# WARNING! All changes made in this file will be lost! + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_visual_tab(object): + def setupUi(self, visual_tab): + visual_tab.setObjectName("visual_tab") + visual_tab.resize(1177, 690) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(":/ui/resources/tab_icons/visual_icon.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) + visual_tab.setWindowIcon(icon) + self.verticalLayout = QtWidgets.QVBoxLayout(visual_tab) + self.verticalLayout.setObjectName("verticalLayout") + self.search_parameters_frame_2 = QtWidgets.QFrame(visual_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_parameters_frame_2.sizePolicy().hasHeightForWidth()) + self.search_parameters_frame_2.setSizePolicy(sizePolicy) + self.search_parameters_frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.search_parameters_frame_2.setFrameShadow(QtWidgets.QFrame.Raised) + self.search_parameters_frame_2.setObjectName("search_parameters_frame_2") + self.verticalLayout_22 = QtWidgets.QVBoxLayout(self.search_parameters_frame_2) + self.verticalLayout_22.setObjectName("verticalLayout_22") + self.search_initial_parameters_frame_2 = QtWidgets.QFrame(self.search_parameters_frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_initial_parameters_frame_2.sizePolicy().hasHeightForWidth()) + self.search_initial_parameters_frame_2.setSizePolicy(sizePolicy) + self.search_initial_parameters_frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.search_initial_parameters_frame_2.setFrameShadow(QtWidgets.QFrame.Raised) + self.search_initial_parameters_frame_2.setObjectName("search_initial_parameters_frame_2") + self.gridLayout_6 = QtWidgets.QGridLayout(self.search_initial_parameters_frame_2) + self.gridLayout_6.setObjectName("gridLayout_6") + self.search_report_parameter_label_2 = QtWidgets.QLabel(self.search_initial_parameters_frame_2) + self.search_report_parameter_label_2.setObjectName("search_report_parameter_label_2") + self.gridLayout_6.addWidget(self.search_report_parameter_label_2, 0, 0, 1, 1) + self.search_end_year_parameter_label_2 = QtWidgets.QLabel(self.search_initial_parameters_frame_2) + self.search_end_year_parameter_label_2.setObjectName("search_end_year_parameter_label_2") + self.gridLayout_6.addWidget(self.search_end_year_parameter_label_2, 0, 4, 1, 1) + self.search_start_year_parameter_dateedit_2 = QtWidgets.QDateEdit(self.search_initial_parameters_frame_2) + self.search_start_year_parameter_dateedit_2.setObjectName("search_start_year_parameter_dateedit_2") + self.gridLayout_6.addWidget(self.search_start_year_parameter_dateedit_2, 1, 2, 2, 1) + self.search_start_year_parameter_label_2 = QtWidgets.QLabel(self.search_initial_parameters_frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_start_year_parameter_label_2.sizePolicy().hasHeightForWidth()) + self.search_start_year_parameter_label_2.setSizePolicy(sizePolicy) + self.search_start_year_parameter_label_2.setObjectName("search_start_year_parameter_label_2") + self.gridLayout_6.addWidget(self.search_start_year_parameter_label_2, 0, 2, 1, 1) + self.search_report_parameter_combobox_2 = QtWidgets.QComboBox(self.search_initial_parameters_frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_report_parameter_combobox_2.sizePolicy().hasHeightForWidth()) + self.search_report_parameter_combobox_2.setSizePolicy(sizePolicy) + self.search_report_parameter_combobox_2.setObjectName("search_report_parameter_combobox_2") + self.gridLayout_6.addWidget(self.search_report_parameter_combobox_2, 1, 0, 2, 1) + self.search_end_year_parameter_dateedit_2 = QtWidgets.QDateEdit(self.search_initial_parameters_frame_2) + self.search_end_year_parameter_dateedit_2.setCurrentSection(QtWidgets.QDateTimeEdit.YearSection) + self.search_end_year_parameter_dateedit_2.setObjectName("search_end_year_parameter_dateedit_2") + self.gridLayout_6.addWidget(self.search_end_year_parameter_dateedit_2, 1, 4, 2, 1) + spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem, 1, 1, 1, 1) + spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem1, 1, 3, 1, 1) + self.verticalLayout_22.addWidget(self.search_initial_parameters_frame_2) + self.verticalLayout.addWidget(self.search_parameters_frame_2) + self.frame_14 = QtWidgets.QFrame(visual_tab) + self.frame_14.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_14.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_14.setObjectName("frame_14") + self.horizontalLayout_14 = QtWidgets.QHBoxLayout(self.frame_14) + self.horizontalLayout_14.setObjectName("horizontalLayout_14") + self.frame_15 = QtWidgets.QFrame(self.frame_14) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_15.sizePolicy().hasHeightForWidth()) + self.frame_15.setSizePolicy(sizePolicy) + self.frame_15.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_15.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_15.setObjectName("frame_15") + self.gridLayout_13 = QtWidgets.QGridLayout(self.frame_15) + self.gridLayout_13.setObjectName("gridLayout_13") + self.frame_2 = QtWidgets.QFrame(self.frame_15) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_2.sizePolicy().hasHeightForWidth()) + self.frame_2.setSizePolicy(sizePolicy) + self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_2.setObjectName("frame_2") + self.visual_name_label = QtWidgets.QLabel(self.frame_2) + self.visual_name_label.setEnabled(True) + self.visual_name_label.setGeometry(QtCore.QRect(50, 80, 97, 21)) + self.visual_name_label.setObjectName("visual_name_label") + self.visual_metric_parameter_label = QtWidgets.QLabel(self.frame_2) + self.visual_metric_parameter_label.setGeometry(QtCore.QRect(50, 130, 213, 20)) + self.visual_metric_parameter_label.setObjectName("visual_metric_parameter_label") + self.metric_Type_comboBox = QtWidgets.QComboBox(self.frame_2) + self.metric_Type_comboBox.setGeometry(QtCore.QRect(140, 130, 921, 26)) + self.metric_Type_comboBox.setObjectName("metric_Type_comboBox") + self.visual_vendor_parameter_label = QtWidgets.QLabel(self.frame_2) + self.visual_vendor_parameter_label.setGeometry(QtCore.QRect(50, 30, 61, 20)) + self.visual_vendor_parameter_label.setObjectName("visual_vendor_parameter_label") + self.visual_vendor_parameter_combobox = QtWidgets.QComboBox(self.frame_2) + self.visual_vendor_parameter_combobox.setGeometry(QtCore.QRect(140, 30, 921, 26)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.visual_vendor_parameter_combobox.sizePolicy().hasHeightForWidth()) + self.visual_vendor_parameter_combobox.setSizePolicy(sizePolicy) + self.visual_vendor_parameter_combobox.setObjectName("visual_vendor_parameter_combobox") + self.visual_name_parameter_combobox = QtWidgets.QComboBox(self.frame_2) + self.visual_name_parameter_combobox.setEnabled(True) + self.visual_name_parameter_combobox.setGeometry(QtCore.QRect(140, 80, 921, 28)) + self.visual_name_parameter_combobox.setEditable(True) + self.visual_name_parameter_combobox.setObjectName("visual_name_parameter_combobox") + self.gridLayout_13.addWidget(self.frame_2, 5, 0, 1, 3) + self.frame = QtWidgets.QFrame(self.frame_15) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame.sizePolicy().hasHeightForWidth()) + self.frame.setSizePolicy(sizePolicy) + self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame.setObjectName("frame") + self.frame_18 = QtWidgets.QFrame(self.frame) + self.frame_18.setEnabled(True) + self.frame_18.setGeometry(QtCore.QRect(10, 10, 136, 161)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_18.sizePolicy().hasHeightForWidth()) + self.frame_18.setSizePolicy(sizePolicy) + self.frame_18.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_18.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_18.setObjectName("frame_18") + self.verticalLayout_28 = QtWidgets.QVBoxLayout(self.frame_18) + self.verticalLayout_28.setObjectName("verticalLayout_28") + self.label_45 = QtWidgets.QLabel(self.frame_18) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_45.sizePolicy().hasHeightForWidth()) + self.label_45.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Arial") + self.label_45.setFont(font) + self.label_45.setTextFormat(QtCore.Qt.AutoText) + self.label_45.setObjectName("label_45") + self.verticalLayout_28.addWidget(self.label_45) + self.radioButton_3 = QtWidgets.QRadioButton(self.frame_18) + self.radioButton_3.setObjectName("radioButton_3") + self.verticalLayout_28.addWidget(self.radioButton_3) + self.radioButton = QtWidgets.QRadioButton(self.frame_18) + self.radioButton.setObjectName("radioButton") + self.verticalLayout_28.addWidget(self.radioButton) + self.radioButton_4 = QtWidgets.QRadioButton(self.frame_18) + self.radioButton_4.setObjectName("radioButton_4") + self.verticalLayout_28.addWidget(self.radioButton_4) + self.frame_options = QtWidgets.QFrame(self.frame) + self.frame_options.setGeometry(QtCore.QRect(150, 10, 141, 161)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_options.sizePolicy().hasHeightForWidth()) + self.frame_options.setSizePolicy(sizePolicy) + self.frame_options.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_options.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_options.setObjectName("frame_options") + self.label_46 = QtWidgets.QLabel(self.frame_options) + self.label_46.setGeometry(QtCore.QRect(10, 10, 121, 21)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_46.sizePolicy().hasHeightForWidth()) + self.label_46.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Arial") + self.label_46.setFont(font) + self.label_46.setTextFormat(QtCore.Qt.AutoText) + self.label_46.setObjectName("label_46") + self.monthly_radioButton = QtWidgets.QRadioButton(self.frame_options) + self.monthly_radioButton.setGeometry(QtCore.QRect(10, 40, 112, 20)) + self.monthly_radioButton.setObjectName("monthly_radioButton") + self.yearly_radioButton = QtWidgets.QRadioButton(self.frame_options) + self.yearly_radioButton.setGeometry(QtCore.QRect(10, 70, 112, 20)) + self.yearly_radioButton.setObjectName("yearly_radioButton") + self.topnum_radioButton = QtWidgets.QRadioButton(self.frame_options) + self.topnum_radioButton.setGeometry(QtCore.QRect(10, 100, 112, 20)) + self.topnum_radioButton.setObjectName("topnum_radioButton") + self.costratio_radioButton = QtWidgets.QRadioButton(self.frame_options) + self.costratio_radioButton.setGeometry(QtCore.QRect(10, 130, 112, 20)) + self.costratio_radioButton.setObjectName("costratio_radioButton") + self.option_frame = QtWidgets.QFrame(self.frame) + self.option_frame.setEnabled(True) + self.option_frame.setGeometry(QtCore.QRect(293, 10, 151, 161)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.option_frame.sizePolicy().hasHeightForWidth()) + self.option_frame.setSizePolicy(sizePolicy) + self.option_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.option_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.option_frame.setObjectName("option_frame") + self.edit_cost_ratio_frame = QtWidgets.QFrame(self.option_frame) + self.edit_cost_ratio_frame.setGeometry(QtCore.QRect(10, 10, 131, 81)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.edit_cost_ratio_frame.sizePolicy().hasHeightForWidth()) + self.edit_cost_ratio_frame.setSizePolicy(sizePolicy) + self.edit_cost_ratio_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.edit_cost_ratio_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.edit_cost_ratio_frame.setObjectName("edit_cost_ratio_frame") + self.label_47 = QtWidgets.QLabel(self.edit_cost_ratio_frame) + self.label_47.setGeometry(QtCore.QRect(10, 10, 183, 15)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_47.sizePolicy().hasHeightForWidth()) + self.label_47.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Arial") + self.label_47.setFont(font) + self.label_47.setTextFormat(QtCore.Qt.AutoText) + self.label_47.setObjectName("label_47") + self.cost_ratio_option_combobox = QtWidgets.QComboBox(self.edit_cost_ratio_frame) + self.cost_ratio_option_combobox.setGeometry(QtCore.QRect(0, 30, 121, 26)) + self.cost_ratio_option_combobox.setObjectName("cost_ratio_option_combobox") + self.edit_top_num_frame = QtWidgets.QFrame(self.option_frame) + self.edit_top_num_frame.setGeometry(QtCore.QRect(10, 90, 131, 61)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.edit_top_num_frame.sizePolicy().hasHeightForWidth()) + self.edit_top_num_frame.setSizePolicy(sizePolicy) + self.edit_top_num_frame.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.edit_top_num_frame.setFrameShadow(QtWidgets.QFrame.Raised) + self.edit_top_num_frame.setObjectName("edit_top_num_frame") + self.label_48 = QtWidgets.QLabel(self.edit_top_num_frame) + self.label_48.setGeometry(QtCore.QRect(10, 10, 183, 15)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.label_48.sizePolicy().hasHeightForWidth()) + self.label_48.setSizePolicy(sizePolicy) + font = QtGui.QFont() + font.setFamily("Arial") + self.label_48.setFont(font) + self.label_48.setTextFormat(QtCore.Qt.AutoText) + self.label_48.setObjectName("label_48") + self.top_num_spinbox = QtWidgets.QSpinBox(self.edit_top_num_frame) + self.top_num_spinbox.setGeometry(QtCore.QRect(10, 30, 101, 24)) + self.top_num_spinbox.setMaximum(999) + self.top_num_spinbox.setObjectName("top_num_spinbox") + self.frame_16 = QtWidgets.QFrame(self.frame) + self.frame_16.setGeometry(QtCore.QRect(460, 10, 601, 161)) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.frame_16.sizePolicy().hasHeightForWidth()) + self.frame_16.setSizePolicy(sizePolicy) + self.frame_16.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.frame_16.setFrameShadow(QtWidgets.QFrame.Raised) + self.frame_16.setObjectName("frame_16") + self.gridLayout_14 = QtWidgets.QGridLayout(self.frame_16) + self.gridLayout_14.setObjectName("gridLayout_14") + self.vertical_axis_lineEdit = QtWidgets.QLineEdit(self.frame_16) + self.vertical_axis_lineEdit.setObjectName("vertical_axis_lineEdit") + self.gridLayout_14.addWidget(self.vertical_axis_lineEdit, 4, 1, 1, 2) + self.horizontal_axis_lineEdit = QtWidgets.QLineEdit(self.frame_16) + self.horizontal_axis_lineEdit.setObjectName("horizontal_axis_lineEdit") + self.gridLayout_14.addWidget(self.horizontal_axis_lineEdit, 3, 1, 1, 2) + self.chart_title_lineEdit = QtWidgets.QLineEdit(self.frame_16) + self.chart_title_lineEdit.setObjectName("chart_title_lineEdit") + self.gridLayout_14.addWidget(self.chart_title_lineEdit, 2, 1, 1, 2) + self.label_44 = QtWidgets.QLabel(self.frame_16) + self.label_44.setObjectName("label_44") + self.gridLayout_14.addWidget(self.label_44, 4, 0, 1, 1) + self.label_43 = QtWidgets.QLabel(self.frame_16) + self.label_43.setObjectName("label_43") + self.gridLayout_14.addWidget(self.label_43, 3, 0, 1, 1) + self.label_36 = QtWidgets.QLabel(self.frame_16) + self.label_36.setObjectName("label_36") + self.gridLayout_14.addWidget(self.label_36, 2, 0, 1, 1) + self.gridLayout_13.addWidget(self.frame, 0, 0, 1, 3) + self.horizontalLayout_14.addWidget(self.frame_15) + self.verticalLayout.addWidget(self.frame_14) + self.search_control_frame_2 = QtWidgets.QFrame(visual_tab) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.search_control_frame_2.sizePolicy().hasHeightForWidth()) + self.search_control_frame_2.setSizePolicy(sizePolicy) + self.search_control_frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel) + self.search_control_frame_2.setFrameShadow(QtWidgets.QFrame.Raised) + self.search_control_frame_2.setObjectName("search_control_frame_2") + self.gridLayout_12 = QtWidgets.QGridLayout(self.search_control_frame_2) + self.gridLayout_12.setObjectName("gridLayout_12") + self.open_folder_checkBox = QtWidgets.QCheckBox(self.search_control_frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.open_folder_checkBox.sizePolicy().hasHeightForWidth()) + self.open_folder_checkBox.setSizePolicy(sizePolicy) + self.open_folder_checkBox.setObjectName("open_folder_checkBox") + self.gridLayout_12.addWidget(self.open_folder_checkBox, 2, 0, 1, 1) + self.create_chart_button = QtWidgets.QPushButton(self.search_control_frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.create_chart_button.sizePolicy().hasHeightForWidth()) + self.create_chart_button.setSizePolicy(sizePolicy) + self.create_chart_button.setObjectName("create_chart_button") + self.gridLayout_12.addWidget(self.create_chart_button, 0, 1, 3, 2) + self.open_file_checkBox = QtWidgets.QCheckBox(self.search_control_frame_2) + sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.open_file_checkBox.sizePolicy().hasHeightForWidth()) + self.open_file_checkBox.setSizePolicy(sizePolicy) + self.open_file_checkBox.setMaximumSize(QtCore.QSize(16777215, 16777215)) + self.open_file_checkBox.setChecked(True) + self.open_file_checkBox.setObjectName("open_file_checkBox") + self.gridLayout_12.addWidget(self.open_file_checkBox, 0, 0, 1, 1) + self.verticalLayout.addWidget(self.search_control_frame_2) + + self.retranslateUi(visual_tab) + QtCore.QMetaObject.connectSlotsByName(visual_tab) + + def retranslateUi(self, visual_tab): + _translate = QtCore.QCoreApplication.translate + visual_tab.setWindowTitle(_translate("visual_tab", "Visual")) + self.search_report_parameter_label_2.setText(_translate("visual_tab", "Report")) + self.search_end_year_parameter_label_2.setText(_translate("visual_tab", "End Year")) + self.search_start_year_parameter_dateedit_2.setDisplayFormat(_translate("visual_tab", "yyyy")) + self.search_start_year_parameter_label_2.setText(_translate("visual_tab", "Start Year")) + self.search_end_year_parameter_dateedit_2.setDisplayFormat(_translate("visual_tab", "yyyy")) + self.visual_name_label.setText(_translate("visual_tab", "Database")) + self.visual_metric_parameter_label.setText(_translate("visual_tab", "Metric Type ")) + self.visual_vendor_parameter_label.setText(_translate("visual_tab", "Vendor")) + self.label_45.setText(_translate("visual_tab", "Select Chart Type")) + self.radioButton_3.setText(_translate("visual_tab", "Vertical Bar")) + self.radioButton.setText(_translate("visual_tab", "Horizontal Bar")) + self.radioButton_4.setText(_translate("visual_tab", "Line")) + self.label_46.setText(_translate("visual_tab", "Calculation Type")) + self.monthly_radioButton.setText(_translate("visual_tab", "Monthly")) + self.yearly_radioButton.setText(_translate("visual_tab", "Yearly")) + self.topnum_radioButton.setText(_translate("visual_tab", "Top #")) + self.costratio_radioButton.setText(_translate("visual_tab", "Cost Ratio")) + self.label_47.setText(_translate("visual_tab", "Cost Ratio Option")) + self.label_48.setText(_translate("visual_tab", " Top # ")) + self.label_44.setText(_translate("visual_tab", "Vertical Axis Title")) + self.label_43.setText(_translate("visual_tab", "Horizontal Axis Title")) + self.label_36.setText(_translate("visual_tab", "Chart Title")) + self.open_folder_checkBox.setText(_translate("visual_tab", "Open Folder")) + self.create_chart_button.setText(_translate("visual_tab", "Create Chart")) + self.open_file_checkBox.setText(_translate("visual_tab", "Open File")) +import Resources_rc diff --git a/ui/VisualTab.ui b/ui/VisualTab.ui new file mode 100644 index 0000000..cce375e --- /dev/null +++ b/ui/VisualTab.ui @@ -0,0 +1,731 @@ + + + visual_tab + + + + 0 + 0 + 1177 + 690 + + + + Visual + + + + :/ui/resources/tab_icons/visual_icon.png:/ui/resources/tab_icons/visual_icon.png + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Report + + + + + + + End Year + + + + + + + yyyy + + + + + + + + 0 + 0 + + + + Start Year + + + + + + + + 0 + 0 + + + + + + + + QDateTimeEdit::YearSection + + + yyyy + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + true + + + + 50 + 80 + 97 + 21 + + + + Database + + + + + + 50 + 130 + 213 + 20 + + + + Metric Type + + + + + + 140 + 130 + 921 + 26 + + + + + + + 50 + 30 + 61 + 20 + + + + Vendor + + + + + + 140 + 30 + 921 + 26 + + + + + 0 + 0 + + + + + + true + + + + 140 + 80 + 921 + 28 + + + + true + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + true + + + + 10 + 10 + 136 + 161 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + Arial + + + + Select Chart Type + + + Qt::AutoText + + + + + + + Vertical Bar + + + + + + + Horizontal Bar + + + + + + + Line + + + + + + + + + 150 + 10 + 141 + 161 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 121 + 21 + + + + + 0 + 0 + + + + + Arial + + + + Calculation Type + + + Qt::AutoText + + + + + + 10 + 40 + 112 + 20 + + + + Monthly + + + + + + 10 + 70 + 112 + 20 + + + + Yearly + + + + + + 10 + 100 + 112 + 20 + + + + Top # + + + + + + 10 + 130 + 112 + 20 + + + + Cost Ratio + + + + + + true + + + + 293 + 10 + 151 + 161 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 131 + 81 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 183 + 15 + + + + + 0 + 0 + + + + + Arial + + + + Cost Ratio Option + + + Qt::AutoText + + + + + + 0 + 30 + 121 + 26 + + + + + + + + 10 + 90 + 131 + 61 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + 10 + 10 + 183 + 15 + + + + + 0 + 0 + + + + + Arial + + + + Top # + + + Qt::AutoText + + + + + + 10 + 30 + 101 + 24 + + + + 999 + + + + + + + + 460 + 10 + 601 + 161 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + + + + + + + Vertical Axis Title + + + + + + + Horizontal Axis Title + + + + + + + Chart Title + + + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + Open Folder + + + + + + + + 0 + 0 + + + + Create Chart + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Open File + + + true + + + + + + + + + + + + + diff --git a/ui/resources/add_vendor_icon.png b/ui/resources/add_vendor_icon.png new file mode 100644 index 0000000..6495dcf Binary files /dev/null and b/ui/resources/add_vendor_icon.png differ diff --git a/ui/resources/export_vendors_icon.png b/ui/resources/export_vendors_icon.png new file mode 100644 index 0000000..44beb91 Binary files /dev/null and b/ui/resources/export_vendors_icon.png differ diff --git a/ui/resources/folder_icon.png b/ui/resources/folder_icon.png new file mode 100644 index 0000000..3e0d208 Binary files /dev/null and b/ui/resources/folder_icon.png differ diff --git a/ui/resources/help_icon.png b/ui/resources/help_icon.png new file mode 100644 index 0000000..5bbf47d Binary files /dev/null and b/ui/resources/help_icon.png differ diff --git a/ui/resources/import_vendors_icon.png b/ui/resources/import_vendors_icon.png new file mode 100644 index 0000000..0e69a9c Binary files /dev/null and b/ui/resources/import_vendors_icon.png differ diff --git a/ui/resources/main_icon.png b/ui/resources/main_icon.png new file mode 100644 index 0000000..0cd1339 Binary files /dev/null and b/ui/resources/main_icon.png differ diff --git a/ui/resources/remove_vendor_icon.png b/ui/resources/remove_vendor_icon.png new file mode 100644 index 0000000..355f354 Binary files /dev/null and b/ui/resources/remove_vendor_icon.png differ diff --git a/ui/resources/save_icon.png b/ui/resources/save_icon.png new file mode 100644 index 0000000..9342a35 Binary files /dev/null and b/ui/resources/save_icon.png differ diff --git a/ui/resources/tab_icons/costs_icon.png b/ui/resources/tab_icons/costs_icon.png new file mode 100644 index 0000000..c6ef76d Binary files /dev/null and b/ui/resources/tab_icons/costs_icon.png differ diff --git a/ui/resources/tab_icons/fetch_reports_icon.png b/ui/resources/tab_icons/fetch_reports_icon.png new file mode 100644 index 0000000..0b9f1c1 Binary files /dev/null and b/ui/resources/tab_icons/fetch_reports_icon.png differ diff --git a/ui/resources/tab_icons/fetch_special_reports_icon.png b/ui/resources/tab_icons/fetch_special_reports_icon.png new file mode 100644 index 0000000..1bd8cfe Binary files /dev/null and b/ui/resources/tab_icons/fetch_special_reports_icon.png differ diff --git a/ui/resources/tab_icons/import_report_icon.png b/ui/resources/tab_icons/import_report_icon.png new file mode 100644 index 0000000..7a63671 Binary files /dev/null and b/ui/resources/tab_icons/import_report_icon.png differ diff --git a/ui/resources/tab_icons/manage_vendors_icon.png b/ui/resources/tab_icons/manage_vendors_icon.png new file mode 100644 index 0000000..42fc90a Binary files /dev/null and b/ui/resources/tab_icons/manage_vendors_icon.png differ diff --git a/ui/resources/tab_icons/search_icon.png b/ui/resources/tab_icons/search_icon.png new file mode 100644 index 0000000..07c8e9b Binary files /dev/null and b/ui/resources/tab_icons/search_icon.png differ diff --git a/ui/resources/tab_icons/settings_icon.png b/ui/resources/tab_icons/settings_icon.png new file mode 100644 index 0000000..ec10589 Binary files /dev/null and b/ui/resources/tab_icons/settings_icon.png differ diff --git a/ui/resources/tab_icons/visual_icon.png b/ui/resources/tab_icons/visual_icon.png new file mode 100644 index 0000000..5ed66f2 Binary files /dev/null and b/ui/resources/tab_icons/visual_icon.png differ diff --git a/ui/resources/undo_icon.png b/ui/resources/undo_icon.png new file mode 100644 index 0000000..e1ab7d0 Binary files /dev/null and b/ui/resources/undo_icon.png differ